文章目录
项目效果图
一、剪切板的用处?
为满足业务的快速操作便捷,往往业务只知道商品的编码和需要的对应数量,此时复制简单内容往剪切板一丢,就能填写想要的对应数据。
二、封装剪切板
1.封装button
<template>
<span>
<template v-else-if="type === 'copyPaste'">
<el-button
size="small"
type="primary"
:disabled="disabled"
@click="handleCopyPaste"
>
剪切板
</el-button>
</template>
</span>
<CopyPaste
ref="copyPasteDialog"
:paste="paste"
:copy-paste-function="copyPasteFunction"
@close-callback="$emit('getProductData')"
/>
</template>
<script>
props: {
copyPasteFunction: {
//copyPaste 剪贴板判断使用方式 byTransfer:调拨单剪切板
type: String,
default: '',
},
paste: {
type: Function,
default: () => {
return Function
},
},
beforePasteOpen: {
type: Function,
default: () => Function,
},
}
//点击事件
handleCopyPaste() {
if (!this.beforePasteOpen()) return
this.$refs.copyPasteDialog.open()
},
handleCopyPaste() {
if (!this.beforePasteOpen()) return
this.$refs.copyPasteDialog.open()
},
</script>
2.封装剪切板组件(弹窗可以直接用原生)
<template>
<DragDialog
:is-need-footer="true"
:dialog-visible.sync="visible"
:title="$t('common.copyPaste')"
:append-to-body="true"
@submit="handleOk"
>
<template slot="dialog-main">
<div class="tiplabel">
请粘贴产品编码/数量到此处
<span class="download" @click="handleExport()">模版下载</span>
</div>
<el-input
v-model="textarea"
type="textarea"
:autosize="{ minRows: 6, maxRows: 10 }"
placeholder="请粘贴内容"
@change="change"
>
>
</el-input>
<div v-if="err.length">
产品编号:
<span
v-for="(item, index) in err"
:key="index"
style="color: red; padding: 0 5px"
>
{{ item }}
</span>
不存在,请修改Excel再进行尝试!
</div>
</template>
</DragDialog>
</template>
<script>
import DragDialog from '@/components/DragDialog'
import { baseURL } from '@/config' //项目的基础地址
import axios from 'axios'
import store from '@/store' //仓库 获取token
import { details } from '@/api/commodity/product' //获取商品的详情接口 后续看你们自己什么业务
import { tenantId } from '@/utils/token'
export default {
name: 'CopyPaste',
components: { DragDialog },
props: {
paste: {
type: Function,
default: () => {
return Function
},
},
copyPasteFunction: {
//copyPaste 剪贴板判断使用方式 byTransfer:调拨单剪切板
type: String,
default: '',
},
},
data() {
return {
err: [],
textarea: '',
visible: false,
}
},
computed: {
uploadHeaders() {
return {
Authorization: `bearer ${store.getters['user/token']}`, //获取token
}
},
templateUrl() { //模板下载地址
let fileName = '剪贴板模板.xls'
this.copyPasteFunction === 'byTransfer' &&
(fileName = `调拨单剪贴板模板.xls`)
return `${baseURL}/tfle/v1/files/download-by-key?fileKey=inventory/template/product/${tenantId()}/${fileName}`
},
},
methods: {
change() {
//处理你需要的数据结构
let str = this.textarea
let target = str.split('\n') //切割拿到input框的值进行下面操作
let list = []
target.map((item) => {
let itemArr = item.split('\t')
if (itemArr[0]) {
let obj =
this.copyPasteFunction === 'byTransfer'
? {
outLogicWarehouseName: itemArr[0],
inLogicWarehouseName: itemArr[1],
productCode: itemArr[2],
applyQuantity: itemArr[3] || 0,
}
: {
productCode: itemArr[0],
applyQuantity: itemArr[1] || 0,
}
list.push(obj)
}
})
//判断你传进来的是什么类型,这里可以根据你传进来的剪贴板判断使用方式 进行不同的操作
if (this.copyPasteFunction === 'byTransfer') {
this.data = list
} else {
//不是调拨单剪切板进行接口请求操作
this.verify(list)
}
},
verify(list) {
let data = [],
err = []
list.map((item) => {
details({ productCode: item.productCode }).then((res) => {
if (res.success == false) {
err.push(item.productCode)
} else {
res.applyQuantity = item.applyQuantity
data.push(res)
}
})
})
this.err = err
if (err.length) {
return false
}
this.data = data
},
open() {
// if (!this.checkUrl()) {
this.visible = true
// }
},
close() {
this.visible = false
},
handleOk() {
if (this.data.length) {
console.log('这里的', this.data)
this.visible = false
this.paste(this.data) //传进来的函数接收处理完的数据进行操作
}
},
//模板下载操作
handleExport() {
axios
.get(this.templateUrl, {
responseType: 'blob',
headers: { ...this.uploadHeaders },
})
.then((res) => {
if (res.status === 200) {
var dom = document.createElement('a')
dom.download = '剪贴板模板.xls'
dom.style.display = 'none'
dom.href = window.URL.createObjectURL(res.data)
dom.click()
this.$message({
type: 'success',
message: '下载成功',
})
}
})
.catch((err) => {
this.$message.error(`下载失败,${err.message}`)
})
},
},
}
</script>
<style scoped lang="scss">
.export-line {
background: #e8e8e8;
height: 1px;
width: 100%;
margin-bottom: 24px;
clear: both;
}
.export-title {
margin: 12px auto;
line-height: 24px;
font-size: 16px;
color: #333;
}
.el-tree {
color: #333;
}
.download {
color: red;
cursor: pointer;
}
.tiplabel {
margin-bottom: 7px;
}
</style>
3.弹窗封装(附带,有国际化操作 自己使用需要去除 或者引用国际化)
<template>
<el-dialog
v-if="destroyOnClose"
v-el-drag-dialog
:title="title"
:visible.sync="visible"
:close-on-click-modal="false"
:width="width"
:append-to-body="appendToBody"
:show-close="!isLoading"
:fullscreen="dialogFull"
@dragDialog="handleDrag"
@close="close"
>
<template slot="title">
<div class="avue-crud__dialog__header">
<span class="el-dialog__title">
<span
style="
display: inline-block;
background-color: #3478f5;
width: 3px;
height: 20px;
margin-right: 5px;
float: left;
margin-top: 2px;
"
></span>
{{ title }}
</span>
<div
v-if="isShowFullScreen"
class="avue-crud__dialog__menu"
@click="dialogFull ? (dialogFull = false) : (dialogFull = true)"
>
<i class="el-icon-full-screen"></i>
</div>
</div>
</template>
<div class="drag-dialog-container">
<div
ref="dragDialogContainerBody"
class="main-bar"
:style="{ maxHeight: maxHeight, minHeight: minHeight }"
>
<slot name="dialog-main" />
</div>
<div v-if="isNeedFooter" class="dialog-footer">
<slot name="dialog-footer">
<!-- 有默认的button,如果需要自定义,在外面通过slot传入也可以,如果使用默认按钮,记得传入相应按钮事件 -->
<el-button
v-show="isMore"
type="primary"
size="small"
:loading="isLoading"
@click="moreButton"
>
{{ moreButtonText || $t('common.ok') }}
</el-button>
<el-button
v-show="isHidden"
type="primary"
size="small"
:loading="isLoading"
@click="submit"
>
{{ submitText || $t('common.ok') }}
</el-button>
<!-- 审批流 提交时禁止取消-->
<el-button plain size="small" :disabled="isLoading" @click="cancel">
{{ cancelText || $t('buttonTxt.cancel') }}
</el-button>
</slot>
</div>
</div>
</el-dialog>
</template>
<script>
import elDragDialog from '@/directive/el-drag-dialog'
import { Debounce } from '../../utils/public.js'
export default {
name: 'DragDialog',
directives: { elDragDialog },
props: {
title: {
type: String,
default: '',
},
dialogVisible: {
type: Boolean,
default: false,
},
isNeedFooter: {
// 是否需要footer, 默认需要
type: Boolean,
default: true,
},
maxHeight: {
type: String,
default: '70vh',
},
minHeight: {
type: String,
default: '350px',
},
width: {
type: String,
default: '50%',
},
cancelText: {
type: String,
default: '',
},
submitText: {
type: String,
default: '',
},
appendToBody: {
// 是否是嵌套
type: Boolean,
default: false,
},
destroyOnClose: {
// 是否关闭时销毁 Dialog 中的元素
type: Boolean,
default: true,
},
isLoading: {
type: Boolean,
default: false,
},
moreButtonText: {
type: String,
default: '',
},
isMore: {
type: Boolean,
default: false,
},
isHidden: {
type: Boolean,
default: true,
},
isShowFullScreen: {
type: Boolean,
default: false,
},
},
data() {
return {
dialogFull: false,
}
},
computed: {
visible: {
get() {
return this.dialogVisible
},
set(val) {
this.$emit('update:dialogVisible', val)
this.$emit('resetThContent') // 关闭弹窗的时候进行重置
},
},
},
methods: {
// v-el-drag-dialog onDrag callback function
handleDrag() {
console.log('you had handle drag')
},
close() {
this.$emit('close')
},
submit: Debounce(function () {
this.$emit('submit')
}, 500),
cancel() {
this.visible = false
this.$emit('cancel')
},
getBodyRefs() {
return this.$refs.dragDialogContainerBody
},
moreButton() {
this.$emit('moreButton')
},
},
}
</script>
<style lang="scss" scoped>
.drag-dialog-container {
position: relative;
padding-bottom: 52px;
// overflow: auto;
.main-bar {
overflow-y: auto;
overflow-x: hidden;
padding: 24px 24px 0;
min-height: 200px !important;
}
.dialog-footer {
width: 100%;
height: 52px;
position: absolute;
bottom: 0;
left: 0;
text-align: right;
padding: 0 20px 0;
border-top: 1px solid $dashed-01;
line-height: 52px;
.el-button--default {
border: 1px solid #d9d9d9;
}
button {
margin-left: 24px;
margin-right: 0 !important;
}
}
}
.el-dialog__header {
padding: 15px 20px 15px;
}
.el-dialog__headerbtn {
top: 15px;
}
/*dialog header*/
.el-dialog__header {
background: #e3eaed;
}
.avue-crud__dialog__header {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: justify;
-ms-flex-pack: justify;
justify-content: space-between;
}
.el-dialog__title {
color: rgba(0, 0, 0, 0.85);
font-weight: 500;
word-wrap: break-word;
}
.avue-crud__dialog__menu {
padding-right: 20px;
float: left;
}
.avue-crud__dialog__menu i {
color: #909399;
font-size: 15px;
}
.el-icon-full-screen {
cursor: pointer;
}
.el-icon-full-screen:before {
content: '\e719';
color: #fff;
font-size: 22px;
margin-right: 6px;
}
</style>
三、使用剪切板
<OperateButton
type="copyPaste"
copy-paste-function="byTransfer" //传入的使用方式
:before-paste-open="beforePasteOpen" //传入前的函数判断
:paste="addDetail"
/>
addDetail(data) {
//data就是封装那边返回来的数据填写的 需要统一格式可以封装好数据
//这里若是还需要调接口进行其它操作
//若是不进行其它操作 只需要商品数据,下面即可
let list = data.map((item) => {
return {
productName: item.productName,
productCode: item.productCode,
applyQuantity: item.applyQuantity,
isSet: true,
}
})
this.$refs.Detailed.list = this.$refs.Detailed.list.concat(list)
}
beforePasteOpen() {
if (!this.schema_model.businessType) {
this.$message.error('请先选择业务类型')
return false
}
return true
},
总结
剪切板有需要的可以自取,有问题可以咨询