【Vue3】封装数字框组件

news2024/9/20 15:36:53

数量选择组件-基本结构

(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'
export default {
  install(app: App) {
    app.component('Numbox', Numbox)
  },
}

(3)提供类型声明

import Numbox from '@/components/numbox/index.vue'
declare module 'vue' {
  export interface GlobalComponents {
    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
}>()

const add = () => {
  if (props.modelValue >= props.max) return
  emit('update:modelValue', props.modelValue + 1)
}

const sub = () => {
  if (props.modelValue <= props.min) return
  emit('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
}>()

const add = () => {
  if (props.modelValue >= props.max) return
  emit('update:modelValue', props.modelValue + 1)
}

const sub = () => {
  if (props.modelValue <= props.min) return
  emit('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
}>()

const add = () => {
  if (props.modelValue >= props.max) return
  emit('update:modelValue', props.modelValue + 1)
}

const sub = () => {
  if (props.modelValue <= props.min) return
  emit('update:modelValue', props.modelValue - 1)
}
   
+const handleChange = (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>


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/391541.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言-基础了解-19-C位域

C位域 一、C位域 如果程序的结构中包含多个开关量&#xff0c;只有 TRUE/FALSE 变量&#xff0c;如下&#xff1a; struct {unsigned int widthValidated;unsigned int heightValidated; } status;这种结构需要 8 字节的内存空间&#xff0c;但在实际上&#xff0c;在每个变…

引领云数仓创新浪潮 HashData闪耀PostgreSQL中国技术大会

3月3日-3月5日&#xff0c;第12届PostgreSQL中国技术大会在杭州举行。本次大会以“突破•进化•共赢——安全可靠&#xff0c;共建与机遇”为主题&#xff0c;以线上线上结合的方式&#xff0c;邀请了PG领域众多行业大咖、学术精英及技术专家&#xff0c;共同探讨数据库未来的发…

【C/C++ 数据结构】-八大排序之 归并排序其它排序

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【C/C数据结构与算法】 分享&#xff1a;本王在此&#xff0c;狼狈为奸者&#xff0c;谋权篡位者&#xff0c;倒行逆施者&#xff0c;都得死&#xff01; ——岐王李茂贞《画江湖…

Linux 静态与动态编译、静态库与动态库

文章目录一、库的简介二、静态链接和动态链接1、静态链接2、动态链接3、GCC 下动态库与静态库三、静态库制作和使用四、动态库制作和使用总结一、库的简介 什么是库文件呢&#xff1f; 所谓库文件&#xff0c;大家可以将其等价为压缩包文件&#xff0c;该文件内部通常包含不止…

MyBatis源码分析(六)MetaObject工具类的使用与源码分析

文章目录一、MetaObject基本使用二、关键类源码分析1、MetaObject的构造方法2、PropertyTokenizer分词器3、BeanWrapper4、MetaClass5、DefaultReflectorFactory6、Reflector7、总结三、MetaObject的getValue源码分析写在后面一、MetaObject基本使用 public class User {priva…

OPT(奥普特)一键测量传感器SmartFlash高精度的四重保证

OPT&#xff08;奥普特&#xff09;一键测量传感器SmartFlash集成了机器视觉的边缘提取、自动匹配、自动对焦、自动学习及图像合成等人工智能技术&#xff0c;采用双远心光路及多角度照明系统设计&#xff0c;搭载高精度运动平台&#xff0c;并通过亚像素边缘提取算法处理图像&…

Mysql全解[中级篇]

目录存储引擎MySQL体系结构1). 连接层2). 服务层3). 引擎层4). 存储层存储引擎介绍存储引擎特点InnoDBMyISAMMemory文件区别及特点存储引擎选择索引无索引情况有索引情况特点索引结构二叉树红黑树B-TreeBTreeMySQL中优化之后的BTreeHash索引分类聚集索引&二级索引回表查询索…

双周赛99(贪心、数学、区间合并计算、换根DP)

文章目录双周赛99[6312. 最小和分割](https://leetcode.cn/problems/split-with-minimum-sum/)贪心[6311. 统计染色格子数](https://leetcode.cn/problems/count-total-number-of-colored-cells/)找规律[6313. 统计将重叠区间合并成组的方案数](https://leetcode.cn/problems/c…

规并排序(Swift版本)

Overview 概述 时间复杂度为 O(nlogn) ;适合大规模的数据排序 ;相比于冒泡排序、插入排序、选择排序这三种排序算法, 更加常用 ;用到了分治思想(即分而治之, 英文叫 “Divide and conquer”)&#xff0c;非常巧妙 ;英文名称: Merge Sort ; 分治思想, 在很多领域都有广泛的应用…

windows系统安装Linux虚拟机教程

虚拟机的安装首先要下载虚拟机的安装包&#xff0c;当前最新版本是VMware 16.2.1。软件我都已经给大家准备好了&#xff08;含序列号&#xff09;&#xff0c;大家在这里下载就好。虚拟机安装包下载完毕之后&#xff0c;将它安装到电脑里。这个安装过程很简单&#xff0c;一路下…

Linux操作系统学习(线程池)

文章目录线程池线程池原理代码示例单例模式饿汉模式懒汉模式饿汉懒汉对比其他的锁线程池 线程池原理 ​ 线程池是一种线程使用模式。在多线程应用中&#xff0c;若每有一个任务&#xff0c;线程就去调度相应的函数去创建&#xff0c;当任务过多时&#xff0c;每次都去调度且每…

CCF大数据专家委员会十周年纪念庆典纪实:拥抱数字时代,展望科技未来

山河远阔&#xff0c;奋进十年&#xff0c;作为国内大数据领域最权威的学术组织&#xff0c;CCF大数据专家委员会&#xff08;以下简称“大专委”&#xff09;不忘初心&#xff0c;凝心聚力&#xff0c;见证并推动了过去10年来大数据技术生态在中国的建立、发展和成熟。 2023年…

HBase安装

文章目录一、安装Zookeeper二、安装HBase三、启动Hbase步骤四、关闭进程顺序五、简单使用Hbase在开始安装HBase之前&#xff0c;请确保您已经安装了Java运行环境和Hadoop分布式文件系统。如果您还没有安装这些软件&#xff0c;请查看之前博文介绍安装。 HBase安装包&#xff1a…

谷歌广告投放步骤流程是什么?一文带你全方位了解实操细节

谷歌&#xff0c;大家都不陌生吧&#xff0c;一个人们很常用的搜索引擎。而谷歌还可以打广告&#xff0c;即谷歌广告&#xff0c;那这跟跨境电商有什么关心呢&#xff1f;东哥告诉大家&#xff0c;关系大了去了&#xff0c;毕竟如果用户搜索与我们相关的关键词&#xff0c;就有…

streaming systems 第二章

The What, Where, When, and How of Data Processing 第一章主要关注三个领域:术语&#xff0c;准确定义我使用重载术语时的意思&#xff0c;如“流”;批处理和流处理&#xff0c;比较两种类型系统的理论能力&#xff0c;并假设使流处理系统超越批处理系统只有两件事是必要的:…

【Netty】第一章 NIO 三大组件、ByteBuffer 和文件编程

【Netty】第一章 NIO 三大组件、ByteBuffer 和文件编程 文章目录【Netty】第一章 NIO 三大组件、ByteBuffer 和文件编程一、Channel & Buffer二、Selector三、ByteBuffer1.ByteBuffer 使用方式2.ByteBuffer 结构3.ByteBuffer 常用方法4.Scattering Reads4.Gathering Write5…

Java SPI机制了解与应用

1. 了解SPI机制 我们在平时学习和工作中总是会听到Java SPI机制&#xff0c;特别是使用第三方框架的时候&#xff0c;那么什么是SP机制呢&#xff1f;SPI 全称 Service Provider Interface&#xff0c;是 Java 提供的一套用来被第三方实现或者扩展的接口&#xff0c;它可以用来…

【java】Java连接mysql数据库及mysql驱动jar包下载和使用

文章目录JDBCJDBC本质&#xff1a;JDBC作用&#xff1a;跟数据库建立连接发送 SQL 语句返回处理结果操作流程和具体的连接步骤如下&#xff1a;操作步骤&#xff1a;需要导入驱动jar包 mysql-connector-java-8.0.22.jar注册驱动获取数据库连接对象 Connection定义sql获取执行sq…

第十届CCF大数据与计算智能大赛总决赛暨颁奖典礼在苏州吴江顺利举办

2月24日-25日&#xff0c;中国计算机学会&#xff08;CCF&#xff09;主办、苏州市吴江区人民政府支持&#xff0c;苏州市吴江区工信局、吴江区东太湖度假区管理办公室、苏州市吴江区科技局、CCF大数据专家委员会、CCF自然语言处理专业委员会、CCF高性能计算专业委员会、CCF计算…

深度剖析C语言符号篇

致前行的人&#xff1a; 人生像攀登一座山&#xff0c;而找寻出路&#xff0c;却是一种学习的过程&#xff0c;我们应当在这过程中&#xff0c;学习稳定冷静&#xff0c;学习如何从慌乱中找到生机。 目录 1.注释符号&#xff1a; 2.续接符和转义符&#xff1a; 3.回车与换行…