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>