小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
C V 系列特点:
- 只关注功能的实现。让各位看官复制粘贴即可安心使用
- 代码注释会写详细,尽可能做到一行一注释,把细节都说清楚
- 涉及第三方库等等不做赘述,用到的库会给各位看官放上链接,方便各位深入了解
功能演示
话不多说 上代码
使用时
<!-- 整数 + 事件 -->
<MjInput
v-model="source.num"
:min="1"
:max="999999"
placeholder="请填写"
@blur="source.num=checkMm(source.num, 1, 999999)"
/>
<!-- 浮点 + 插槽 + 事件 -->
<MjInput
v-model="price"
class="money"
type="float"
clearable
@blur="priceJudge"
@keyup.enter.native="queryData(1,'search')"
>
<i slot="prefix" class="unit">¥</i>
</MjInput>
封装组件
父组件
<template>
<!-- $attrs $listeners 保证可以用el-input的写法使用封装好的组件 -->
<component :is="show_component" v-model="curVal" v-bind="$attrs" v-on="$listeners">
<!-- 没有这个不能使用el-input的插槽 -->
<template v-for="(index, name) in $slots" v-slot:[name]>
<slot :name="name"></slot>
</template>
</component>
</template>
<script>
import Int from './int' // int 整数类型
import Float from './float' // float 浮点类型
// 组件常量 不放在data里 没必要被vue劫持
const COMPONENTENUM = {
int: 'Int',
float: 'Float'
}
export default {
name: 'MjInput',
components: { Int, Float },
props: {
value: { // 父组件 v-model 传入的值
type: [String, Number],
default: ''
},
type: { // 要渲染的组件,注意这里直接劫持了传入的type属性 并且不会在往子组件传递 默认为int整数
type: String,
default: 'int'
}
},
data() {
return {
curVal: '' // 当前的数据
}
},
computed: {
show_component() {
return COMPONENTENUM[this.type]
}
},
watch: {
value: {
handler(val) {
// 因为''会被判断为false所以不能直接使用 !val,
// 因为是传入的值,子组件要保持单向数据流,不能直接修改值,所以深拷贝一下赋给data中的变量
(val !== null && val !== undefined) && (this.curVal = JSON.parse(JSON.stringify(val)))
},
// 立刻的,即时的;确认是否以当前的初始值执行handler的函数
immediate: true
},
// 监听当前值的变化 提交给父组件
curVal(val) {
// v-model语法糖传入的value 默认emit触发的是'input'
this.$emit('input', val)
}
}
}
</script>
子组件 ==> 浮点类
<template>
<!-- 小数点组件 -->
<!-- $attrs $listeners 保证可以用el-input的写法使用封装好的组件 -->
<el-input v-model.trim="curVal" v-bind="$attrs" @input="inputEvent" v-on="$listeners">
<!-- 没有这个不能使用el-input的插槽 -->
<template v-for="(index, name) in $slots" v-slot:[name]>
<slot :name="name"></slot>
</template>
</el-input>
</template>
<script>
export default {
name: 'FloatInput',
props: {
value: { // 父组件 v-model 传入的值
type: [String, Number],
default: ''
}
},
data() {
return {
curVal: '' // 当前的数据
}
},
watch: {
value: {
handler(val) {
// 因为''会被判断为false所以不能直接使用 !val,
// 因为是传入的值,子组件要保持单向数据流,不能直接修改值,所以深拷贝一下赋给data中的变量
(val !== null && val !== undefined) && (this.curVal = JSON.parse(JSON.stringify(val)))
},
// 立刻的,即时的;确认是否以当前的初始值执行handler的函数
immediate: true
},
// 监听当前值的变化 提交给父组件
curVal(val) {
// v-model语法糖传入的value 默认emit触发的是'input'
this.$emit('input', val)
}
},
methods: {
inputEvent(value) {
if (!value) return
// 解决macos 输入。在特定情况下识别为.
value = value.replace(/。/g, '.');
// 先把非数字的都替换掉,除了数字和.
value = value.replace(/[^\d.]/g, '');
// 保证只有出现一个.而没有多个.
value = value.replace(/\.{2,}/g, '.');
// 必须保证第一个为数字而不是.
value = value.replace(/^\./g, '');
// 保证.只出现一次,而不能出现两次以上
value = value.replace('.', '$#$').replace(/\./g, '').replace('$#$', '.');
// 只能输入两个小数
value = value.replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3');
// 开头只能输入一个0
value = value.replace(/^0+\d+?/g, '0');
this.$nextTick(() => {
if (Number.isNaN(value)) value = ''
this.curVal = value
})
}
}
}
</script>
子组件 ==> 整数类
<template>
<!-- $attrs $listeners 保证可以用el-input的写法使用封装好的组件 -->
<el-input v-model.number.trim="curVal" v-bind="$attrs" v-on="$listeners" @input="inputEvent" >
<!-- 没有这个不能使用el-input的插槽 -->
<template v-for="(index, name) in $slots" :slot="name">
<slot :name="name"></slot>
</template>
</el-input>
</template>
<script>
export default {
name: 'IntInput',
props: {
value: { // 父组件 v-model 传入的值
type: [String, Number],
default: ''
},
isStartZero: { // 开头是否可以输入多个零
type: Boolean,
default: false
}
},
data() {
return {
curVal: '' // 当前的数据
}
},
watch: {
value: {
handler(val) {
// 因为''会被判断为false所以不能直接使用 !val,
// 因为是传入的值,子组件要保持单向数据流,不能直接修改值,所以深拷贝一下赋给data中的变量
(val !== null && val !== undefined) && (this.curVal = JSON.parse(JSON.stringify(val)))
},
// 立刻的,即时的;确认是否以当前的初始值执行handler的函数
immediate: true
},
// 监听当前值的变化 提交给父组件
curVal(val) {
// v-model语法糖传入的value 默认emit触发的是'input'
this.$emit('input', val)
}
},
methods: {
inputEvent(value) {
if (!value) return
// 非数字的替换为空
value = value.replace(/[^\d]/g, '');
// 是否传入了开头可以出现的0的标识
!this.isStartZero && (value = value.replace(/^0+\d+?/g, '0'))
this.$nextTick(() => {
this.curVal = value
})
}
}
}
</script>
总结
- 用
<component></component>包裹一下,统一使用时的组件名字,统一用法 - 用
$attrs和$listeners保证可以用el-input的写法使用封装好的组件 watch中监听value,为了保证数据实时可变。并在查看详情时可以更好的回显所以也加上了immediate: trueinputEvent函数中使用this.$nextTick因为要确保在当前的dom变化完在赋值
欢迎查看更多C V系列 C V 大法