目录
1.emitter.JS
function broadcast (componentName, eventName, params) {
this.$children.forEach(child => {
var name = child.$options.componentName
if (name === componentName) {
child.$emit.apply(child, [eventName].concat(params))
} else {
broadcast.apply(child, [componentName, eventName].concat([params]))
}
})
}
export default {
methods: {
dispatch (componentName, eventName, params) {
var parent = this.$parent || this.$root
var name = parent.$options.componentName
while (parent && (!name || name !== componentName)) {
parent = parent.$parent
if (parent) {
name = parent.$options.componentName
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params))
}
},
broadcast (componentName, eventName, params) {
broadcast.call(this, componentName, eventName, params)
}
}
}
2.number.vue
<template>
<div>
<div class="el-number-input-wrap el-input" :class="{'is-disabled': this.inputDisabled}">
<input
:type="inputPositive"
class="el-input__inner"
:id="elementId"
:class="inputClasses"
:disabled="disabled"
autoComplete="off"
spellcheck="false"
:autofocus="autofocus"
@focus="handleFocus"
@blur="handleBlur"
@input="handleInput"
@change="change"
:readonly="readonly || !editable"
:name="name"
:value="formatterValue"
:placeholder="placeholder"/>
</div>
</div>
</template>
<script>
import emitter from './emitter.js'
export default {
name: 'Number',
componentName: 'Number',
mixins: [ emitter ],
inheritAttrs: false,
inject: {
unForm: {
default: ''
},
unFormItem: {
default: ''
}
},
props: {
value: {
type: [String, Number],
default: null
},
max: {
type: Number,
default: Infinity
},
min: {
type: Number,
default: -Infinity
},
step: {
type: Number,
default: 1
},
activeChange: {
type: Boolean,
default: true
},
isnum: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
autofocus: {
type: Boolean,
default: false
},
readonly: {
type: Boolean,
default: false
},
editable: {
type: Boolean,
default: true
},
name: {
type: String
},
precision: {
type: Number
},
elementId: {
type: String
},
formatter: {
type: Function
},
parser: {
type: Function
},
placeholder: {
type: String,
default: ''
}
},
data () {
return {
focused: false,
currentValue: this.value
}
},
computed: {
inputDisabled () {
return this.disabled || (this.unForm || {}).disabled
},
inputClasses () {
return 'el-number-input'
},
inputPositive () {
if (this.isnum) return 'number'
},
precisionValue () {
if (!this.currentValue) return this.currentValue
if (this.precision) {
var varStr = this.currentValue.toString()
if (varStr.split('.').length === 1) {
return this.currentValue
} else {
var len = varStr.split('.')[1].length
if (len > this.precision) return this.currentValue.toFixed(this.precision)
else return this.currentValue
}
} else {
return this.currentValue
}
},
formatterValue () {
if (this.formatter && this.precisionValue !== null) {
return this.formatter(this.precisionValue)
} else {
return this.precisionValue
}
},
_unFormItemSize () {
return (this.unFormItem || {}).unFormItemSize
},
validateState () {
return this.unFormItem ? this.unFormItem.validateState : ''
},
needStatusIcon () {
return this.unForm ? this.unForm.statusIcon : false
},
validateIcon () {
return {
validating: 'el-icon-loading',
success: 'el-iocn-circle-check',
error: 'el-icon-circle-close'
}[this.validateState]
}
},
methods: {
preventDefault (e) {
e.preventDefault()
},
setValue (val) {
if (val && !isNaN(this.precision)) val = Number(val).toFixed(this.precision)
if (val !== null) {
if (val > this.max) {
val = this.max && !isNaN(this.precision) ? Number(this.max).toFixed(this.precision) : this.max
} else if (val < this.min) {
val = this.min && !isNaN(this.precision) ? Number(this.min).toFixed(this.precision) : this.min
}
}
this.$nextTick(() => {
this.currentValue = val
this.$emit('input', val)
this.$emit('change', val)
this.dispatch('ElFormItem', 'el.form.change', val)
})
},
handleFocus (event) {
this.focused = true
this.$emit('focus', event)
},
handleBlur () {
this.focused = false
this.$emit('blur', event)
},
handleInput () {
const value = event.target.value
this.tmpValue = value
},
change (event) {
if (event.type === 'input' && !this.activeChange) return
let val = event.target.value.trim()
if (this.parser) {
val = this.parser(val)
}
const isEmptyString = val.length === 0
if (isEmptyString) {
this.setValue(null)
return
}
// if (event.type === 'input' && val.match(/^[\+|\-]?\.?$|\.$/)) return
if (event.type === 'input' && val.match(/^[+|-]?\.?$|\.$/)) return
val = Number(val)
if (!isNaN(val)) {
this.currentValue = val
event.target.value = this.currentValue
this.setValue(val)
} else {
event.target.value = this.currentValue
}
},
changeVal (val) {
if (val === '' || val === null || val === undefined) {
return
}
val = Number(val)
if (!isNaN(val)) {
const step = this.step
this.upDisabled = val + step > this.max
this.downDisabled = val - step < this.min
} else {
this.upDisabled = true
this.downDisabled = true
}
}
},
mounted () {
this.changeVal(this.currentValue)
},
watch: {
value (val) {
this.currentValue = val
},
currentValue (val) {
this.changeVal(val)
},
min () {
this.changeVal(this.currentValue)
},
max () {
this.changeVal(this.currentValue)
}
}
}
</script>
3.index.js
import Number from './src/number.vue'
Number.install = function (Vue) {
Vue.component(Number.name, Number)
}
export default Number
4.应用
<template>
<div class="phone">
<el-row :gutter="12">
<el-col :span="12">
<el-card shadow="hover">
<div>
<h3>基础用法</h3>
<numbers :max="9999" :min="-9999" v-model="value1" placeholder="请输入数字"/>
<div style="margin-top: 20px;">要使用它,只需要在el-number元素中使用v-model绑定变量即可,变量的初始值即为默认值,如果你只需要控制数值在某一范围内,可以设置min属性和max属性,不设置时,最小值为0</div>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card shadow="hover">
<div>
<h3>只读状态</h3>
<numbers :max="9999" :min="-9999" v-model="value2" placeholder="请输入数字" readonly/>
<div style="margin-top: 20px;">readonly属性可设置组件为只读状态</div>
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="12">
<el-card shadow="hover">
<div>
<h3>禁用状态</h3>
<numbers :max="9999" :min="-9999" v-model="value3" placeholder="请输入数字" disabled/>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card shadow="hover">
<div>
<h3>精度</h3>
<numbers :max="9999" :min="-9999" v-model="value4" :precision="3" placeholder="请输入数字"/>
<div style="margin-top: 20px;">precision属性可指定输入框数字的精度</div>
</div>
</el-card>
</el-col>
</el-row>
<div style="margin-bottom: 20px;">
<h3>Attributes</h3>
<el-table
:data="tableData"
stripe
border
style="width: 100%">
<el-table-column
prop="parameter"
label="参数"
width="180">
</el-table-column>
<el-table-column
prop="instructions"
label="说明">
</el-table-column>
<el-table-column
prop="type"
label="类型"
width="180">
</el-table-column>
<el-table-column
prop="optionalValue"
label="可选值"
width="180">
</el-table-column>
<el-table-column
prop="defaultValue"
label="默认值"
width="180">
</el-table-column>
</el-table>
<h3>Number Events</h3>
<el-table
:data="tableData2"
stripe
border
style="width: 100%">
<el-table-column
prop="parameter"
label="事件名称"
width="180">
</el-table-column>
<el-table-column
prop="instructions"
label="说明">
</el-table-column>
<el-table-column
prop="callbackPar"
label="回调参数">
</el-table-column>
</el-table>
<h3>Number Methods</h3>
<el-table
:data="tableData3"
stripe
border
style="width: 100%">
<el-table-column
prop="parameter"
label="方法名"
width="180">
</el-table-column>
<el-table-column
prop="instructions"
label="说明">
</el-table-column>
<el-table-column
prop="callbackPar"
label="参数">
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
import numbers from '../../components/number/index.js'
export default {
name: 'PhoneS',
components: {
numbers
},
data () {
return {
value1: '',
value2: '123',
value3: '123',
value4: '',
tableData: [
{
parameter: 'value',
instructions: '绑定值',
type: 'string/number',
optionalValue: '-',
defaultValue: '-'
},
{
parameter: 'placeholder',
instructions: '输入框占位文本',
type: 'string',
optionalValue: '-',
defaultValue: '-'
},
{
parameter: 'disabled',
instructions: '禁用',
type: 'boolean',
optionalValue: '-',
defaultValue: 'false'
},
{
parameter: 'auto-complete',
instructions: '原生属性,自动补全',
type: 'string',
optionalValue: 'on,off',
defaultValue: 'off'
},
{
parameter: 'name',
instructions: '原生属性',
type: 'string',
optionalValue: '-',
defaultValue: '-'
},
{
parameter: 'readonly',
instructions: '原生属性,是否只读',
type: 'boolean',
optionalValue: '-',
defaultValue: 'false'
},
{
parameter: 'max',
instructions: '原生属性,设置最大值',
type: 'number',
optionalValue: '-',
defaultValue: 'infinity'
},
{
parameter: 'min',
instructions: '原生属性,设置最小值',
type: 'number',
optionalValue: '-',
defaultValue: '-infinity'
},
{
parameter: 'autofocus',
instructions: '原生属性,自动获取焦点',
type: 'string',
optionalValue: '-',
defaultValue: '-'
},
{
parameter: 'precision',
instructions: '数字精度',
type: 'number',
optionalValue: '-',
defaultValue: '-'
}
],
tableData2: [
{
parameter: 'blur',
instructions: '在Number失去焦点的时候触发',
callbackPar: '(event: Event)'
},
{
parameter: 'focus',
instructions: '在Number获得焦点的时候触发',
callbackPar: '(event: Event)'
}
],
tableData3: [
{
parameter: 'blur',
instructions: '使Number失去焦点',
callbackPar: '-'
},
{
parameter: 'focus',
instructions: '使Number获得焦点',
callbackPar: '-'
}
]
}
},
methods: {
// 13213
}
}
</script>
5.效果图