开发了一个设置日预算的需求,其中涉及到的技术点比较多,也很常用。现在对此次需求进行一下梳理,以便日后学习。
背景:对选择的计划批量设置/修改预算。修改方式有在线编辑、excel批量导入两种。
1、在线编辑
样式如下:
html代码如下:
<el-table :data="tableData" ref="budgetTable" :max-height="heightNumber">
<el-table-column prop="campaignName" label="计划名称" key="campaignName" fixed
width="200px" ></el-table-column>
<el-table-column prop="dayBudgetCustom" label="统一日预算" key="dayBudgetCustom" fixed min-width="140px" >
<template v-slot="scope">
<el-input v-model.trim="scope.row.dayBudgetCustom" style="width: 100px"
@paste.native.capture.prevent="onPaste"
@focus="onFocus(scope, 0)"
@keyup.native="handleNumberType"
@blur="handleNumberRange"
placeholder="不限"></el-input>
</template>
</el-table-column>
<!-- 今日预算 -->
<el-table-column prop="dayBudget" :label="startDate" key="dayBudget" fixed min-width="140px" >
<template v-slot="scope">
<el-input v-model.trim="scope.row.dayBudget" style="width: 100px"
@paste.native.capture.prevent="onPaste"
@focus="onFocus(scope, 1)"
@keyup.native="handleNumberType"
@blur="handleNumberRange"
placeholder="不限"></el-input>
</template>
</el-table-column>
<template v-for="(col, index) in colListBudget">
<el-table-column :label="col" :prop="col" :key="col+index" min-width="110px">
<template v-slot="scope">
<el-input v-model.trim="scope.row[col]"
@paste.native.capture.prevent="onPaste"
@focus="onFocus(scope, index+2)"
@keyup.native="handleNumberType"
@blur="handleNumberRange"
placeholder="不限"></el-input>
</template>
</el-table-column>
</template>
</el-table>
1、选择要操作的日期,开始日期始终是今天,结束日期是未来30天。
startDate: dayjs().format('YYYY-MM-DD'),
endDate: dayjs().add(30,'day').format('YYYY-MM-DD'),
<el-date-picker v-model="startDate"
type="date"
:readonly="true"
style="width:130px"
placeholder="选择日期"></el-date-picker>
至
<el-date-picker v-model="endDate"
type="date"
:clearable="false"
:editable="false"
:picker-options="pickerOptions"
style="width:130px"
@change="handleChangeData"></el-date-picker>
同时对结束日期做限制:只能选择今天及未来30天。
pickerOptions: {
disabledDate: (time) => {
let startDate, endDate
endDate = dayjs().add(30,'day')
startDate = dayjs().add(-1,'day')
return (
time.getTime() < startDate || time.getTime() > endDate
);
},
},
2、对所选计划,配合要设置预算的日期,制造出相应的表格
当弹框值drawerShow为真(即弹窗打开)时,将外面传过来的值campaignArray深拷贝一份赋值给campaignData,遍历campaignData返回一个我们需要哪些值的新数组tableData(即表格数组)。
watch: {
drawerShow: {
handler: function (val, oldval) {
if(val) {
this.heightNumber = window.innerHeight - 220;
this.campaignData = JSON.parse(JSON.stringify(this.campaignArray))
this.tableData = this.campaignData.map(item => {
item.dateRange = JSON.parse(item.dateRange)
return {
id: item.campaignId,
campaignName: item.campaignName,
dayBudgetCustom: item.dayBudgetCustom, //统一日预算
dayBudget: item.dayBudget, //今日预算
...item.dateRange,
}
})
this.handleChangeData() //日期填充表格
this.checkDayBudgetTask()//检查是否有日预算任务进行
}
},
},
immediate: true,
},
遍历日期填充表格:自定义日期是用dayjs的diff方法计算开始日期(加一天,即明天)和结束日期的时间差(比如开始日期21号,结束日期24号,计算的是22号到24号的时间差,为2天),for循环将每一天添加到一个新数组temp,赋值给colListBudget,即自定义列的表头数据源。
处理表格数据:遍历tableData的每一项,再for of遍历temp数组,通过对象的hasOwnProperty方法判断表格的当前项item是否有自定义日期属性,如果有,就给item添加此属性并赋值;若没有这个属性,则添加属性并赋统一日预算dayBudgetCustom的值。
methods: {
//改变日期
handleChangeData() {
//计算结束日期与开始日期的时间差
let dys = dayjs(this.endDate).diff(dayjs(this.startDate).add(1,'day'), 'day');
let temp = []
// Math.abs() 返回一个数的绝对值
// add(1,'day')是为了表格渲染自定义列的时候从“明天”开始,
// 因为“今天”在表格中要被处理成“今日预算”,虽然看起来是“202x-xx-xx”格式,但赋值的字段是dayBudget
// 为何要这样处理? 原因之一是:“今日预算”要固定
for (let i = 0; i <= Math.abs(dys); ++i) {
temp.push(dayjs(this.startDate).add(1,'day').add(i, 'day').format('YYYY-MM-DD'));
}
this.colListBudget = temp;
//处理成一个完整的table
this.tableData = this.tableData.map(item => {
var obj = {}
for (let p of temp) {
item[p] = item.hasOwnProperty(p) ? item[p] : (item.dayBudgetCustom||null)
obj[p] = item[p]
}
return {
id: item.id,
dayBudgetCustom: item.dayBudgetCustom,
dayBudget: item.dayBudget,
campaignName: item.campaignName,
...obj,
}
})
},
}
3、粘贴复制功能
功能描述:支持从 EXCEL 中复制多行多列数据,选中开始输入框,自动向右向下填充复制内容
首先给input框绑定focus事件,获取当前框的横、纵坐标
onFocus()两个参数,第一个是当前行对象(可获取x坐标),第二个是当前纵坐标
<el-input v-model.trim="scope.row.dayBudgetCustom" style="width: 100px"
@paste.native.capture.prevent="onPaste"
@focus="onFocus(scope, 0)"
@keyup.native="handleNumberType"
@blur="handleNumberRange"
placeholder="不限"></el-input>
onFocus(scope, index) {
this.xIndex = scope.$index;
this.yIndex = index;
}
代码解析:replace(new RegExp(',','gm'),'')用于将粘贴内容中的“,”去掉。 将处理好的内容全部整理到strArr数组中。parseInt方法将小数取整。
onPaste(e) {
if (e.clipboardData) {
var item = e.clipboardData.items[0];
if (item && item.kind === 'string') {
item.getAsString(str =>{
let nb = str.replace(/[\t]/g, '|').replace(/[\r\n]/g, ';').replace(new RegExp(',','gm'),'');
let strArr = [];
let nbs = nb.split(';');
for (let i = 0; i < nbs.length; i++) {
if (nbs[i] !== '') {
strArr.push(nbs[i]);
}
}
for (let i = 0, x = this.xIndex, y = this.yIndex;i < strArr.length && x < this.tableData.length && y < this.colListBudget.length + 2; i++, x++) {
if(y === 0) {
this.tableData[x]['dayBudgetCustom'] = parseInt(strArr[i].split('|')[0]);
this.tableData[x]['dayBudget'] = parseInt(strArr[i].split('|')[1]);
} else if(y === 1) {
this.tableData[x]['dayBudget'] = parseInt(strArr[i].split('|')[0]);
}
for (let m = 0; m < strArr[i].split('|').length && y + m < this.colListBudget.length; m++) {
if(strArr[i].split('|')[m] && y>1) { //从自定义列区域 复制
this.tableData[x][this.colListBudget[m+y-2]] = parseInt(strArr[i].split('|')[m]);//从明天开始的自定义列
}
if(strArr[i].split('|')[m+2-y] && y<2) {
this.tableData[x][this.colListBudget[m]] = parseInt(strArr[i].split('|')[m+2-y]);//从明天开始的自定义列
}
this.$set(this.tableData, x, this.tableData[x]);
}
}
});
}
}
},
2、Excel 批量导入
样式如下:
html代码如下:
<div v-else>
<el-tag class="upload-tag" :disable-transitions="true">
<i class="el-alert__icon el-icon-warning" style="color: #E6A23C;"></i>
<span class="ml10">请先下载需要设置次日预算的导入模板</span>
<el-button type="plain" size="mini" class="ml15" @click="handleDownload">下载导入模板</el-button>
</el-tag>
<div class="upload-box select-item">
<span class="select-lable">上传文件</span>
<div class="up-body">
<el-upload action=""
:file-list="fileList"
:on-remove="handleRemove"
:before-upload="beforeUpload"
:on-change="handleUploadChange"
accept=".xls,.xlsx"
:auto-upload="false">
<el-button type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">
<p>· 支持格式:xls,xlsx</p>
<p>· 单个文件大小不超过10M</p>
</div>
<span v-show="isShowIcon" class="load-icon"><i class="el-icon-loading"></i></span>
</el-upload>
</div>
</div>
</div>
1、下载模板
handleDownload() {
let ids = this.campaignData.map(item => item.campaignId)
this.$axios.post('/jdkc-biz-ad/jdkc/campaign/dayBudgetTemplate',
ids,
{responseType: "blob",}
).then(response => {
if(response.data) {
let blob = new Blob([response.data]);
let fileName = '京东快车_计划日预算_上传模板.xlsx';
if(navigator.userAgent.indexOf("Edge") > -1) {
window.navigator.msSaveOrOpenBlob(blob, fileName);
}
let url = window.URL.createObjectURL(blob);
// 生成一个a标签
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
// 生成时间戳
link.download = decodeURI(fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link); // 下载完成移除元素
window.URL.revokeObjectURL(url); // 释放掉blob对象
}
})
},
2、导入上传
confirmUpload() {
this.isUpload = false; //不禁用按钮
let formData = new FormData();
let file = this.fileList[0].raw;
formData.append("file", file);
let config = {
headers: {
"Content-Type": "multipart/form-data",
},
};
this.$axios.post('/jdkc-biz-ad/jdkc/campaign/dayBudgetImport',
formData,
config
).then((response) => {
if (response.data.status===0) {
this.$message.success("上传成功");
this.handleCloseDrawer('refresh')
} else {
this.isUpload = true; //失败则禁用按钮
}
})
},