vue2,vue3基于elementUI的el-table实现复制粘贴功能
vue2,vue3基于elementUI的el-table实现复制粘贴功能
- vue2
- vue3
1、先声明一下,为啥又有vue2和vue3呢,因为老项目要改造成vite+ts+vue3,时间紧,来不及全部转换,所以就有了componentApi和optionsApi共存的情况
2、单页面使用,全局未实现
vue2
- 既然是基于el-table呢就有现成的methods可以使用
@row-contextmenu="rowContextmenu"
@row-click="rowClick"
<template>
<el-table
ref="multipleTableRef"
v-loading="loading"
:data="state.dataList"
style="width: 100%; margin-bottom: 20px"
border
:cell-style="cellStyle"
@sort-change="sortChange"
:header-cell-style="headerCellStyle"
@selection-change="handleSelectionChange"
@row-contextmenu="rowContextmenu"
@cell-mouse-leave="cellMouseLeave"
@row-click="rowClick"
>
<el-table-column align="center" prop="status" label="是否正确" show-overflow-tooltip width="130">
<template #default="{ row }">
<span v-if="row.status === 1">正确</span>
<span v-if="row.status === 2">错误</span>
</template>
</el-table-column>
</el-table>
<div v-if="position.show"
:style="{ position: 'fixed', top: position.y + 'px', left: position.x + 'px', zIndex: 9999, display: 'flex',justifyContent:'center',alignItems:'center',flexFlow:'column',borderRadius:'5px' }"
class="context-menu">
<!-- 菜单内容 -->
<span class="posItem" @click="copyText('column')"><el-icon><DocumentCopy/></el-icon>复制当前行</span>
<span class="posItem" @click="copyText('row')"><el-icon><DocumentCopy/></el-icon>复制当前列</span>
</div>
</template>
<script>
export default {
date() {
return {
position: {
x: 0,
y: 0,
show: false,
row: null,
column: null,
},
columnList: [ // 需要复制的 prop
'orderCodes',
'bookCode',
'orderExecManagerName',
'logisticsManagerName',
'customerName',
'startPortName',
'endPortName',
'endPortCountryName',
'outCountryName',
'contractDealstyle',
'shipMentName',
'requireWeek',
'expectEtd',
'bookArrivalDate',
'vendorName',
'bookShipName',
'preBookStatusName',
'actualVolumeBoxType',
'cntCount',
'forwardAgent',
'forwardAgentContact',
'goodTypeName',
'deptName',
'prodName',
'bookTime',
'bookTrustTime',
'requireMonth',
'requireYear',
'bookComments',
'forwardAgent',
'agentName',
'customsName',
'bookingRemark',
'originBillNum',
'standingBookRemark']
}
},
methods: {
/**
* @description: copyText 复制行或者列点击事件,
* @return {type} 需要打印的行或者列
* row和colume俩参数我给搞混了,后续考虑封装成npm插件的可行性,就没改,可以拿来直接用
* 下方只解释代码逻辑
* @return {row} 列:拿到点击的el-table的prop后,循环遍历出所有的值,然后调用复制
* @return {column} 行:因为后台返的不全是汉字,例如<template #default="{ row }"> 需要在行内重新组合,所以,要么在获取数据的时候就重新给后端返回的数据复制,要么就不复制。
* @return {columns} 需要知道要打印哪一个prop,从表格中拿数据,然后复制(想直接拿dom,有点复杂,暂时先实现功能)
*/
copyText(type){
if (type === 'row') {
const prop = this.position.column.property
const list =this.tableData?.map(item => {
return item[prop]
})
this.copyTextToClipboard(list.join('\n'))
}
if (type === 'column') {
let ls = []
for (let i = 0; i < this.columnList.length; i++) {
const columns = this.columnList[i]
ls.push(this.position.row[columns])
}
this.copyTextToClipboard(ls.join('\t'));
}
this.position.show = false;
},
/**
* @description: rowContextmenu 获取鼠标的右键点击事件,弹窗展示的位置
* @return {event.preventDefault()} 阻止浏览器的默认右键弹窗,展示咱的自定义弹窗
*/
rowContextmenu(row, column, event){
console.log(row, column, event, 'fffff')
event.preventDefault() // 阻止默认右键菜单
this.position.x = event.clientX + 20;
this.position.y = event.clientY + 20;
this.position.row = row;
this.position.column = column;
this.position.show = true;
},
/**
* @description: copyTextToClipboard 复制到浏览器的粘贴板
* @return {window.isSecureContext} 浏览器安全环境,如果项目的线上不是以https开头,而是以http开头的,就不安全,浏览器不让你复制
* else为啥这么做,,是为了绕过安全。具体请参考大佬的解释:https://blog.csdn.net/weixin_42190844/article/details/138336318
*/
async copyTextToClipboard(text) {
if (window.isSecureContext) {
try {
await navigator.clipboard.writeText(text);
this.$message.success('复制成功')
} catch (err) {
console.error('Failed to copy: ', err);
}
} else {
// copyText(text)
let textArea = document.createElement('textarea')
textArea.value = text;
textArea.style.position = 'absolute';
textArea.style.opacity = 0;
textArea.style.left = '-99999999px';
textArea.style.top = '-99999999xp';
document.body.appendChild(textArea);
textArea.focus;
textArea.select();
let res = new Promise((res,rej)=> {
res(() => {
document.execCommand('copy');
textArea.remove()
})
rej(false)
})
res.then(res => {
res()
this.$message.success('复制成功')
})
}
}
rowClick (row) {
this.position.show = false;
}
},
}
</script>
<style lang="scss" scoped>
.context-menu {
background-color: white;
border: 1px solid #ccc;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.posItem {
display: block; cursor: pointer;
padding: 0 20px;
height: 35px;
line-height: 35px;
&:hover {
background: #d5e7f9;
}
}
</style>
vue3
<template>
<el-table
ref="multipleTable"
v-loading="loading"
:data="tableData"
style="width: 100%; margin-bottom: 20px; margin-top: 20px"
:default-sort="{ prop: 'associatedOrder', order: 'descending' }"
border
@selection-change="handleSelectionChange"
@sort-change="sortChange"
@row-contextmenu="rowContextmenu"
@cell-mouse-leave="cellMouseLeave"
@row-click="rowClick"
>
</template>
<script setup lang="ts">
const columnList = [
'orderCodes',
'bookCode',
'orderExecManagerName',
'regionName',
'customerName',
'startPortName',
'endPortName',
'endPortCountryName',
'outCountryName',
'contractDealstyle',]
const position = reactive({
x: 0,
y: 0,
show: false,
row: null,
column: null,
})
const copyText = (type) => {
if (type === 'row') {
const prop = position.column.property
const list = state.dataList?.map(item => {
return item[prop]
})
copyTextToClipboard(list.join('\n'))
}
if (type === 'column') {
let ls = []
for (let i = 0; i < columnList.length; i++) {
const columns = columnList[i]
ls.push(position.row[columns])
}
copyTextToClipboard(ls.join('\t'));
}
position.show = false;
}
const rowContextmenu = (row, column, event) => {
console.log(row, column, event, 'fffff')
event.preventDefault() // 阻止默认右键菜单
position.x = event.clientX + 20;
position.y = event.clientY + 20;
position.row = row;
position.column = column;
position.show = true;
}
const copyTextToClipboard = async (text) => {
if(window.isSecureContext){
try {
await navigator.clipboard.writeText(text);
ElMessage.success('复制成功')
} catch (err) {
console.error('Failed to copy: ', err);
}
} else {
// copyText(text)
let textArea = document.createElement('textarea')
textArea.value = text;
textArea.style.position = 'absolute';
textArea.style.opacity = 0;
textArea.style.left = '-99999999px';
textArea.style.top = '-99999999xp';
document.body.appendChild(textArea);
textArea.focus;
textArea.select();
let res = new Promise((res,rej)=> {
res(() => {
document.execCommand('copy');
textArea.remove()
})
rej(false)
})
res.then(res => {
res()
ElMessage.success('复制成功')
})
}
}
const cellMouseLeave = () => {
// position.show = false;
}
const rowClick = (row) => {
position.show = false;
}
</script>
<style lang="scss" scoped>
.context-menu {
background-color: white;
border: 1px solid #ccc;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.posItem {
display: block; cursor: pointer;
padding: 0 20px;
height: 35px;
line-height: 35px;
&:hover {
background: #d5e7f9;
}
}
</style>
vue3的详解看vue2,后续关于dom和其他封装会怎么进行,看项目咋说吧,时间不太充裕。
演示视频