数量选择组件-基本结构
(1)准备基本结构
<script lang="ts" setup name="Numbox">//</script><template><div class="numbox"><div class="label">数量</div><div class="numbox"><a href="javascript:;">-</a><input type="text"readonly value="1"/><a href="javascript:;">+</a></div></div></template><style scoped lang="less">.numbox {
display: flex;
align-items: center;.label {
width: 60px;
color: #999;
padding-left: 10px;}.numbox {
width: 120px;
height: 30px;
border: 1px solid #e4e4e4;
display: flex;> a {
width: 29px;
line-height: 28px;
text-align: center;
background: #f8f8f8;
font-size: 16px;
color: #666;&:first-of-type{
border-right: 1px solid #e4e4e4;}&:last-of-type{
border-left: 1px solid #e4e4e4;}}> input {
width: 60px;
padding:0 5px;
text-align: center;
color: #666;}}}</style>
(2)全局注册
import Numbox from'@/components/numbox/index.vue'exportdefault{install(app: App){
app.component('Numbox', Numbox)},}
(3)提供类型声明
import Numbox from'@/components/numbox/index.vue'declaremodule'vue'{exportinterfaceGlobalComponents{
Numbox:typeof Numbox
}}export{}
(4)渲染
<div class="spec"><!-- 数字选择框 --><XtxNumbox></XtxNumbox></div>
效果
数量选择组件-v-model语法糖
目标:掌握vue3.0的v-model语法糖原理
在vue2.0中v-mode语法糖简写的代码
<Son :value="msg" @input="msg=$event" />
在vue3.0中v-model语法糖有所调整:
<Son :modelValue="msg" @update:modelValue="msg=$event" />
演示代码:
<script lang="ts" setup>defineProps({
money:{
type: Number,default:0,},})const emit =defineEmits(['update:money'])</script><template><h3>子组件-{{ money }}</h3><button @click="emit('update:money', money + 1)">+1</button></template><style scoped lang="less"></style>
总结: vue3.0封装组件支持v-model的时候,父传子
:modelValue
子传父
@update:modelValue
补充: vue2.0的
xxx.sync
语法糖解析 父传子
:xxx
子传父
@update:xxx
在vue3.0 使用
v-model:xxx
代替。
数量选择组件-功能实现
大致功能分析:
- 默认值为1
- 可限制最大最小值
- 点击-就是减1 点击+就是加1
- 需要完成v-model得实现
- 存在无label情况
<script lang="ts" setup name="Numbox">const props =defineProps({
modelValue:{
type: Number,default:1,},
min:{
type: Number,default:1,},
max:{
type: Number,default:20,},
showLabel:{
type: Boolean,default:false,},})const emit =defineEmits<{(e:'update:modelValue', value:number):void}>()constadd=()=>{if(props.modelValue >= props.max)returnemit('update:modelValue', props.modelValue +1)}constsub=()=>{if(props.modelValue <= props.min)returnemit('update:modelValue', props.modelValue -1)}</script><template><div class="numbox"><div class="label" v-if="showLabel"><slot>数量</slot></div><div class="numbox"><a href="javascript:;"@click="sub">-</a><input type="text"readonly:value="modelValue"/><a href="javascript:;"@click="add">+</a></div></div></template><style scoped lang="less">.numbox {
display: flex;
align-items: center;.label {
width: 60px;
color: #999;
padding-left: 10px;}.numbox {
width: 120px;
height: 30px;
border: 1px solid #e4e4e4;
display: flex;> a {
width: 29px;
line-height: 28px;
text-align: center;
background: #f8f8f8;
font-size: 16px;
color: #666;&:first-of-type{
border-right: 1px solid #e4e4e4;}&:last-of-type{
border-left: 1px solid #e4e4e4;}}> input {
width: 60px;
padding:0 5px;
text-align: center;
color: #666;}}}</style>
动态控制禁用效果
<script lang="ts" setup name="Numbox">const props =defineProps({
modelValue:{
type: Number,default:1,},
min:{
type: Number,default:1,},
max:{
type: Number,default:20,},
showLabel:{
type: Boolean,default:false,},})const emit =defineEmits<{(e:'update:modelValue', value:number):void}>()constadd=()=>{if(props.modelValue >= props.max)returnemit('update:modelValue', props.modelValue +1)}constsub=()=>{if(props.modelValue <= props.min)returnemit('update:modelValue', props.modelValue -1)}</script><template><div class="numbox"><div class="label" v-if="showLabel"><slot>数量</slot></div><div class="numbox">+<a href="javascript:;"@click="sub":class="{not:props.modelValue <= props.main}">-</a><input type="text"readonly:value="modelValue"/>+<a href="javascript:;"@click="add":class="{not:props.modelValue >= props.max}">+</a></div></div></template><style scoped lang="less">.numbox {
display: flex;
align-items: center;.label {
width: 60px;
color: #999;
padding-left: 10px;}.numbox {
width: 120px;
height: 30px;
border: 1px solid #e4e4e4;
display: flex;> a {
width: 29px;
line-height: 28px;
text-align: center;
background: #f8f8f8;
font-size: 16px;
color: #666;+&.not {+ cursor: not-allowed;+}&:first-of-type{
border-right: 1px solid #e4e4e4;}&:last-of-type{
border-left: 1px solid #e4e4e4;}}> input {
width: 60px;
padding:0 5px;
text-align: center;
color: #666;}}}</style>
使用组件:
src/views/goods/index.vue
<script lang="ts" setup name="Numbox">import{ref}from"vue";const count =ref(1)</script><!-- 商品信息 --><div class="goods-info"><!-- 数字选择框 --><XtxNumbox v-model="count" min:"1":max="20"></XtxNumbox></div>
思考:
我们的输入框不仅能点击加减还可以输入数字,如果用户通过输入框输入非数字会出现什么问题?
优化代码
<script lang="ts" setup name="Numbox">const props =defineProps({
modelValue:{
type: Number,default:1,},
min:{
type: Number,default:1,},
max:{
type: Number,default:20,},
showLabel:{
type: Boolean,default:false,},})+const{ proxy }=getCurrentInstance()as ComponentInternalInstance
const emit =defineEmits<{(e:'update:modelValue', value:number):void}>()constadd=()=>{if(props.modelValue >= props.max)returnemit('update:modelValue', props.modelValue +1)}constsub=()=>{if(props.modelValue <= props.min)returnemit('update:modelValue', props.modelValue -1)}+consthandleChange=(e: Event)=>{+// 通过类型断言,让ts知道目前元素的类型+const element = e.target as HTMLInputElement
+let value =+element.value
+if(isNaN(value)) value =1+if(value >= props.max) value = props.max
+if(value <= props.main) value = props.main
+emit('update:modelValue',value)+// 强制刷新+ proxy?.$forceUpdate()}</script><template><div class="numbox"><div class="label" v-if="showLabel"><slot>数量</slot></div><div class="numbox"><a href="javascript:;"@click="sub":class="{not:props.modelValue <= props.main}">-</a><input type="text"readonly:value="modelValue"@change="handleChange($event)"/><a href="javascript:;"@click="add":class="{not:props.modelValue >= props.max}">+</a></div></div></template><style scoped lang="less">.numbox {
display: flex;
align-items: center;.label {
width: 60px;
color: #999;
padding-left: 10px;}.numbox {
width: 120px;
height: 30px;
border: 1px solid #e4e4e4;
display: flex;> a {
width: 29px;
line-height: 28px;
text-align: center;
background: #f8f8f8;
font-size: 16px;
color: #666;&.not {
cursor: not-allowed;}&:first-of-type{
border-right: 1px solid #e4e4e4;}&:last-of-type{
border-left: 1px solid #e4e4e4;}}> input {
width: 60px;
padding:0 5px;
text-align: center;
color: #666;}}}</style>
版权归原作者 itpeilibo 所有, 如有侵权,请联系我们删除。