vuetify实现excel表格粘贴效果

vuetify实现excel表格粘贴效果

文尾附源码

  • 效果

  • 实现原理:

  • Excel表格中的数据粘贴复制到input表单中

  • 监测粘贴事件,获取粘贴的数据

  • 使用空格分隔字符串

  • 将解析的数据赋值

  • 列表渲染表单数据

1.定义粘贴的数据类型,以及条件渲染的数据

javascript 复制代码
import { ref } from 'vue'
import { VForm } from 'vuetify/components'
// 定义粘贴数据的类型 
class pasteContent {
    code = '' // 编码
    num = 0 // 数量
    // 构造函数
    constructor (code: string, num: number) {
      this.code = code
      this.num = num
    }

    // 静态工厂方法  调用pasteContent.createDefault() 会默认生成的初始数据
    static createDefault (): pasteContent {
      return new pasteContent('', 0)
    }
  }

  // 定义行数  默认生成初始化的一行数据
  const columns = ref<pasteContent[]>([pasteContent.createDefault()])
  const formRef = ref<VForm | null>(null)

2.前端代码

  • 注意事项,分多次粘贴的时候需要传入选中本文框的index,实现从选中的区域向下粘贴
html 复制代码
		<v-form ref="formRef">
          <template v-for="(item, index) in columns" :key="index">
            <v-row class="mt-0">
              <v-col cols="5" lg="4">
                <!-- 编码 -->
                <v-text-field
                  v-model="item.code"
                  clearable
                  density="compact"
                  :rules="[v => !!v && v.length == 7 || '不能为空且长度为7']"
                  type="text"
                  variant="outlined"
                  @paste="HandlepasteCode($event, index)"
                />
              </v-col>
              <v-col cols="5" lg="4">
                <!-- 数量 -->
                <v-text-field
                  v-model.number="item.num"
                  density="compact"
                  :rules="numRules"
                  type="number"
                  variant="outlined"
                  @paste="HandlepasteNum($event, index)"
                >
                  <!-- 删除数据行插槽 -->
                  <template #append>
                    <v-icon
                      color="error"
                      density="compact"
                      @click="Delcolumn(index)"
                    >mdi-trash-can-outline</v-icon>
                  </template>
                </v-text-field>
              </v-col>
            </v-row>
          </template>
        </v-form>

3.对应的绑定事件

javascript 复制代码
  // 监听粘贴编码事件
  function HandlepasteCode (event: any, _index: number) {
    // 阻止浏览器的默认粘贴行为
    event.preventDefault()
    // 获取格式化后的编码数据
    const codes = pasteFormat(event)
    // 遍历解析数据
    for (const [offset, value] of codes.entries()) {
      // 获取粘贴框的index下标 如果粘贴后的下标大于源数据行数就新增数据行
      const index = _index + offset
      while (index >= columns.value.length) {
        columns.value.push(pasteContent.createDefault())
      }
      //将数值赋值给columns对应下标
      if (columns.value[index]) {
        columns.value[index].code = value
      }
    }
  }

  // 监听粘贴数量事件
  function HandlepasteNum (event: any, _index: number) {
    // 阻止浏览器的默认粘贴行为
    event.preventDefault()
    // 获取格式化后的数量数据
    const nums = pasteFormat(event)
    // 遍历解析数据
    for (const [offset, value] of nums.entries()) {
      const index = _index + offset
      while (index >= columns.value.length) {
        columns.value.push(pasteContent.createDefault())
      }
      //格式化数据 字符串转换为float类型
      if (columns.value[index]) {
        columns.value[index].num = Number.parseFloat(value)
      }
    }
  }

  // 粘贴数据处理
  function pasteFormat (event: any) {
    // 获取粘贴数据
    const clipboardData = event.clipboardData || window.Clipboard
    const pastedData = clipboardData.getData('Text') // 获取粘贴的文本
    // 格式化数据 去除首尾空格
    return formatData(pastedData.trim())
  }

  // 格式化输入的数据 按照空格分隔,清空空格,剔除空字符串数据
  function formatData (data: string) {
    return data.split(/\s+/).map(item => item.trim()).filter(item => item != '')
  }

4.增加,删除,清空事件

javascript 复制代码
// 添加行
  function Addcolumns () {
    columns.value?.push(pasteContent.createDefault())
  }
  // 删除行
  function Delcolumn (index: number) {
    columns.value.splice(index, 1)
  }
  // 清空行
  function ClearColumns () {
    columns.value = []
    Addcolumns()
  }

5.表单验证规则

javascript 复制代码
// 表单校验规则定义
  const numRules = [
    (v: number | string | null | undefined) => {
      // 如果值为空(null/undefined/空字符串),报错
      if (v === null || v === undefined || v === '') {
        return '产品数量不能为空'
      }
      // 转换为数字检查有效性 默认0转成数值0
      const num = Number(v)
      if (Number.isNaN(num)) {
        return '请输入有效数字'
      }
      // 可选:限制为非负数(根据业务需求)
      if (num < 0) {
        return '数量不能为负数'
      }
      return true
    }]

6.源码 vue页面

javascript 复制代码
<template>
  <v-container>
    <v-form ref="formRef">
      <template v-for="(item, index) in columns" :key="index">
        <v-row class="mt-0">
          <v-col cols="5" lg="4">
            <!-- 编码 -->
            <v-text-field
              v-model="item.code"
              clearable
              density="compact"
              :rules="[v => !!v && v.length == 7 || '不能为空且长度为7']"
              type="text"
              variant="outlined"
              @paste="HandlepasteCode($event, index)"
            />
          </v-col>
          <v-col cols="5" lg="4">
            <!-- 数量 -->
            <v-text-field
              v-model.number="item.num"
              density="compact"
              :rules="numRules"
              type="number"
              variant="outlined"
              @paste="HandlepasteNum($event, index)"
            >
              <!-- 删除数据库行插槽 -->
              <template #append>
                <v-icon
                  color="error"
                  density="compact"
                  @click="Delcolumn(index)"
                >mdi-trash-can-outline</v-icon>
              </template>
            </v-text-field>
          </v-col>
        </v-row>
      </template>
    </v-form>
    <v-col cols="12" lg="8">
      <v-btn
        block
        color="primary"
        prepend-icon="mdi-plus-thick"
        rounded="xs"
        variant="outlined"
        @click="Addcolumns"
      >添加</v-btn>
    </v-col>
  </v-container>
</template>

<script lang="ts" setup>
  import { ref } from 'vue'
  // 定义粘贴数据的类型
  class pasteContent {
    code = '' // 编码
    num = 0 // 数量
    // 构造函数
    constructor (code: string, num: number) {
      this.code = code
      this.num = num
    }

    // 静态工厂方法
    static createDefault (): pasteContent {
      return new pasteContent('', 0)
    }
  }

  // 定义行数  默认生成初始化的一行数据
  // 手动添加行实现  columns.value?.push(pasteContent.createDefault())
  const columns = ref<pasteContent[]>([pasteContent.createDefault()])

  // 添加行
  function Addcolumns () {
    columns.value?.push(pasteContent.createDefault())
  }
  // 删除行
  function Delcolumn (index: number) {
    columns.value.splice(index, 1)
  }
  // 清空行
  function ClearColumns () {
    columns.value = []
    Addcolumns()
  }

  // 表单校验规则定义
  const numRules = [
    (v: number | string | null | undefined) => {
      // 如果值为空(null/undefined/空字符串),报错
      if (v === null || v === undefined || v === '') {
        return '产品数量不能为空'
      }
      // 转换为数字检查有效性 默认0转成数值0
      const num = Number(v)
      if (Number.isNaN(num)) {
        return '请输入有效数字'
      }
      // 可选:限制为非负数(根据业务需求)
      if (num < 0) {
        return '数量不能为负数'
      }
      return true
    }]

  // 监听粘贴编码事件
  function HandlepasteCode (event: any, _index: number) {
    // 阻止浏览器的默认粘贴行为
    event.preventDefault()
    // 获取格式化后的编码数据
    const codes = pasteFormat(event)
    // 遍历解析数据
    for (const [offset, value] of codes.entries()) {
      // 获取粘贴框的index下标
      const index = _index + offset
      while (index >= columns.value.length) {
        columns.value.push(pasteContent.createDefault())
      }
      if (columns.value[index]) {
        columns.value[index].code = value
      }
    }
  }

  // 监听粘贴数量事件
  function HandlepasteNum (event: any, _index: number) {
    // 阻止浏览器的默认粘贴行为
    event.preventDefault()
    // 获取格式化后的数量数据
    const nums = pasteFormat(event)
    // 遍历解析数据
    for (const [offset, value] of nums.entries()) {
      const index = _index + offset
      while (index >= columns.value.length) {
        columns.value.push(pasteContent.createDefault())
      }
      if (columns.value[index]) {
        columns.value[index].num = Number.parseFloat(value)
      }
    }
  }

  // 粘贴数据处理
  function pasteFormat (event: any) {
    // 获取粘贴数据
    const clipboardData = event.clipboardData || window.Clipboard
    const pastedData = clipboardData.getData('Text') // 获取粘贴的文本
    // 格式化数据
    return formatData(pastedData.trim())
  }

  // 格式化输入的数据
  function formatData (data: string) {
    return data.split(/\s+/).map(item => item.trim()).filter(item => item != '')
  }
</script>
相关推荐
Java后端的Ai之路2 小时前
React 快速入门到精通教程:从零基础到能写项目
前端·react.js·前端框架
是上好佳佳佳呀2 小时前
【前端(九)】CSS Transform 2D/3D 变换笔记:分清两个原点,搞懂多重变换
前端·css·笔记
|晴 天|11 小时前
Vue 3 + TypeScript + Element Plus 博客系统开发总结与思考
前端·vue.js·typescript
猫32811 小时前
v-cloak
前端·javascript·vue.js
旷世奇才李先生11 小时前
Vue 3\+Vite\+Pinia实战:企业级前端项目架构设计
前端·javascript·vue.js
空中海12 小时前
第五章:Pinia 状态管理
vue
SoaringHeart13 小时前
Flutter进阶:用OverlayEntry 实现所有弹窗效果
前端·flutter
IT_陈寒15 小时前
Vite静态资源加载把我坑惨了
前端·人工智能·后端
herinspace15 小时前
管家婆实用贴-如何分离和附加数据库
开发语言·前端·javascript·数据库·语音识别