Handsontable - 案例(Vue)

简介: Handsontable - 案例(Vue)
<templatexmlns:v-clipboard="http://www.w3.org/1999/xhtml"><divclass="batch-import"><divclass="header"><divclass="flex-left"><el-buttonsize="mini"class="shade add"@click="handleFAQ"><iclass="el-icon-key"/>新手指南</el-button></div><divclass="flex-right"><el-buttonsize="mini"class="shade add"@click="importExcel" :disabled="importExcelDsiable"><iclass="el-icon-upload2"/>开始导入</el-button><el-buttonsize="mini"class="shade add"@click="beforeCheckExcel"><iclass="el-icon-document-checked"/>检查数据</el-button><el-buttonsize="mini"class="shade add"@click="cutExcelData"v-clipboard:copy="copyContent"v-clipboard:success="onCopy"v-clipboard:error="onError"><iclass="el-icon-files"/>整理数据</el-button><el-buttonsize="mini"class="shade add"id="copyExcel"><iclass="el-icon-document-copy"/>复制全部</el-button><!--<el-buttonsize="mini"class="shade add"@click="handleTest"><iclass="el-icon-document-checked"/>测试按钮</el-button>--><el-buttonsize="mini"class="shade add reset"@click="clearExcel"><iclass="el-icon-refresh"/>重置</el-button></div></div><divclass="shade body"v-loading="loading"><!--下面两层嵌套div必须加上,解决excel表头跑到外面去--><divclass="overf"><divclass="wrapper"><HotTableref="ht" :settings="hotSettings"licenseKey="9c56f-fc083-95630-36851-db045"></HotTable></div></div></div><!--对话框--><NumRangerref="numranger"@afterCheckExcel="afterCheckExcel"></NumRanger><FAQref="faq"op="excel_othermoney"></FAQ></div></template><script>import { HotTable } from'@handsontable/vue'import'handsontable/dist/handsontable.full.css'importfinanceApifrom'@/api/finance'import'handsontable/languages/zh-CN'importNumRangerfrom'@/components/tool/NumRanger'importFAQfrom'@/components/tool/FAQ'importimportBGfrom'@/api/importBG'importmenufrom'@/data/menu'importuserfrom'@/api/userBG'letothermoneytype_id2lbl= {}, othermoneytype_lbl2id= {}
letpaytype_id2lbl= {}, paytype_lbl2id= {}
letoperator_id2lbl= {}, operator_lbl2id= {}
letuser_id2lbl= {}, user_lbl2id= {}
letuser_id2phone= {}, user_phone2id= {}
letthat=null, custom= {}, arr= [], result= {}
exportdefault {
name: 'BatchImport',
components: {
HotTable,
NumRanger,
FAQ    },
data() {
return {
/* common */loading: false,
copyContent: "",
importExcelDsiable: true,
/* excel */sourceData: null,
jsonData: [],
colHeaders: ["导入结果", "项目名称", "关联用户", "用户姓名", "手机号", "项目类型", "项目数量", "收支类型", "订单日期", "订单时间", "项目原价", "签约金额", "支付金额", "支付方式", "付款日期", "付款时间", "经办人", "备注"],
columns: [
          { // -1 导入结果data: "result",
type: 'text',
readOnly: true          },
          { // 0 项目名称data: "othermoneyorderName",
type: 'text', // defvalidator (val, callback) {
if(that.util.isEmpty(val)) returncallback(false)
letpattern=/^.{1,100}$/// console.log(pattern.test(val))if(pattern.test(val)) returncallback(true)
returncallback(false)
            },
          },
          { // 1 关联用户data: "othermoneyorderUseridUserFk",
type: 'dropdown',
source: [],
validator: 'Vdropdown_Tnull'          },
          { // 2 用户姓名data: "othermoneyorderUsername",
type: 'text',
validator (val, callback) {
if(that.util.isEmpty(val)) returncallback(false)
letpattern=/^.{1,20}$/// console.log(pattern.test(val))if(pattern.test(val)) returncallback(true)
returncallback(false)
            },
          },
          { // 3 手机号data: "othermoneyorderPhone",
type: 'text',
validator (val, callback) {
if(that.util.isEmpty(val)) returncallback(true)
letpattern=/^([0-9]{0}|1[0-9]{10})$/// console.log(pattern.test(val))if(pattern.test(val)) returncallback(true)
returncallback(false)
            },
          },
          { // 4 项目类型data: "othermoneyorderOthermoneytypeidOthermoneytypeFk",
type: 'dropdown',
source: [],
validator: 'Vdropdown_Fnull'          },
          { // 5 项目数量data: "othermoneyorderNum",
type: 'text',
validator (val, callback) {
if(that.util.isEmpty(val)) returncallback(false)
letpattern=/^[1-9][0-9]{0,10}$/// console.log(pattern.test(val))if(pattern.test(val)) returncallback(true)
returncallback(false)
            },
          },
          { // 6 收支类型data: "othermoneyorderBudgettype",
type: 'dropdown',
source: ["收入", "支出"],
validator: "Vdropdown_Fnull"          },
          { // 7 订单日期data: "othermoneyorderTime_date",
type: 'date',
width: 130,
dateFormat: 'YYYY-MM-DD',
correctFormat: true          },
          { // 8 订单时间data: "othermoneyorderTime_time",
type: 'time',
timeFormat: 'HH:mm:ss',
correctFormat: true          },
          { // 9 项目原价data: "othermoneyorderOriginaltotalprice",
type: 'numeric',
numericFormat: {
pattern: '0.00',
            },
validator: 'VFnull'          },
          { // 10 签约金额data: "othermoneyorderFacttotalprice",
type: 'numeric',
numericFormat: {
pattern: '0.00',
            },
validator: 'VFnull'          },
          { // 11 支付金额data: "othermoneyorderpayMoney",
type: 'numeric',
numericFormat: {
pattern: '0.00',
            },
validator: 'VFnull'          },
          { // 12 支付方式data: "othermoneyorderpayPaytype",
type: 'dropdown',
source: ["现金", "支付宝", "微信", "网银", "其它"],
validator: 'Vdropdown_Fnull'          },
          { // 13 付款日期data: "othermoneyorderpayTime_date",
type: 'date',
width: 130,
dateFormat: 'YYYY-MM-DD',
correctFormat: true          },
          { // 14 付款时间data: "othermoneyorderpayTime_time",
type: 'time',
timeFormat: 'HH:mm:ss',
correctFormat: true          },
          { // 15 经办人data: "othermoneyorderUseroperatoridUserFk",
type: 'dropdown',
source: [],
validator: 'Vdropdown_Fnull'          },
          { // 16 备注data: "othermoneyorderRemark",
type: 'text',
validator (val, callback) {
if(that.util.isEmpty(val)) returncallback(true)
letpattern=/^.{0,200}$/// console.log(pattern.test(val))if(pattern.test(val)) returncallback(true)
returncallback(false)
            }
          }
        ],
hotSettings: {
readOnlyCellClassName: 'is-readOnly',
language: 'zh-CN', // 声明用中文的语言包// invalidCellClassName: 'htInvalid',// startCols: 10, // 初始行列数// startRows: 20,// minRows: 4, // 最小行// minCols: 3,maxRows: this.const.data().PAGE_SIZE_BIG_PLUS, // 最大行// 数据在这个里面,由数据填充表。当 data 为 null 时,startRows/startCols 生效,否则 data 有数据、[[]],都不会生效,而且不能直接为 []/{},会报错data: null,
minRows: 50,  // 最少行列// minCols: 30,// minSpareCols: 2, // 列留白// minSpareRows: this.const.data().PAGE_SIZE_BIG_PLUS, // 推介和 maxRows 一样minSpareRows: 50,
rowHeaders: true,
// persistentState: true, // 开启本地缓存// colHeaders: true,colHeaders: [], // 自定义列表头autoWrapRow: true, // 自动换行,默认:true;TAB在最后一列中按或向右箭头将移动到下一行的第一列fillHandle: true, // 单元格内容拖拽复制,可选值:true、false、vertical、horizontal;默认:true,垂直/水平都支持fixedColumnsLeft: 2, // 固定左边列数,从0开始为第1列fixedRowsTop: 0,
manualColumnFreeze: true, // 开启手动固定(取消)列manualColumnMove: false, // 关闭手动移动列manualRowMove: true,   // 开启手动移动行manualColumnResize: true, // 开启手工更改列距manualRowResize: true, // 开启手动更改行距comments: true, // 开启添加注释columnSorting: true, // 排序undoRedo: true, // 开启撤销copyPaste: true, // 开启剪切、复制filters: true, // 开启过滤,必须在开启dropdownMenu前提下才有用// columns: this.columns, // 设置每一列的数据类型和配置// columns: [],// dropdownMenu: true, // 开启默认筛选和常用操作dropdownMenu: {
items: {
"filter_by_condition": {
name: '主要筛选',
              },
"filter_operators": {
name: '动作筛选',
              },
"filter_by_condition2": {
name: '次要筛选',
              },
"filter_by_value": {
name: '值筛选',
              },
"filter_action_bar": {
name: '栏筛选',
              }
            }
          },
contextMenu: { // 自定义右键菜单items: {
"row_above": {
name:'向上插一行'              },
"row_below": {
name:'向下插一行'              },
"col_left": {
name:'向左插一列'              },
"col_right": {
name:'向右插一列'              },
"hsep1": "---------", // 分隔线"remove_row": {
name: '删除当前行',
              },
"remove_col": {
name: '删除当前列',
              },
"clear_column": {
name: '清空当前列',
              },
"hsep2": "---------", // 必须和上次的变量名不一样"undo": {
name: '撤销',
              },
"cut": {
name: '剪切',
              },
"copy": {
name: '复制',
              },
"alignment": {
name: '对齐',
              },
"hsep3": "---------",
"commentsAddEdit": { // 必须开启 comments: truename: '添加备注',
              },
"commentsRemove": { // 必须开启 comments: truename: '删除备注',
              },
"freeze_column": { // 必须开启 manualColumnFreeze: truename: '固定列',
              },
"unfreeze_column": { // 必须开启 manualColumnFreeze: truename: '取消固定列',
              }
            }
          },
afterChange (changes, source) {
console.log("changes: ", changes) // row, prop, oldVal, newValconsole.log("source: ", source) // opif(that) { // 第一次加载没必要执行,避免报错that.importExcelDsiable=truethat.sourceData=this.getSourceData()
            }
if(source&& ("CopyPaste.paste"==source||"edit"==source)) {
for(letitemofchanges) {
if(item[1] =="othermoneyorderUseridUserFk") {
letnewName=item[3]
letnewPhone=user_id2phone[user_lbl2id[[newName]]]
this.setDataAtRowProp(item[0], 'othermoneyorderUsername', newName, that.sourceData)
this.setDataAtRowProp(item[0], 'othermoneyorderPhone', newPhone, that.sourceData)
                }
elseif (item[1] =="othermoneyorderOriginaltotalprice"||item[1] =="othermoneyorderFacttotalprice"||item[1] =="othermoneyorderpayMoney") {
letnewName=parseFloat(item[3])
if(newName<0) {
newName*=-1                  }
this.setDataAtRowProp(item[0], item[1], newName, that.sourceData)
                }
              }
            }
          }
        }
      }
    },
methods: {
handleFAQ() {
this.$refs.faq.$data.dialogFAQVisible=true      },
getExcelSourceData() {
letarr=this.$refs.ht.hotInstance.getSourceData(), obj=nullthis.sourceData= []
for(letitemofarr) {
obj= {
result: item['result'],
othermoneyorderName: item['othermoneyorderName'],
othermoneyorderUseridUserFk: item['othermoneyorderUseridUserFk'],
othermoneyorderUsername: item['othermoneyorderUsername'],
othermoneyorderPhone: item['othermoneyorderPhone'],
othermoneyorderOthermoneytypeidOthermoneytypeFk: item['othermoneyorderOthermoneytypeidOthermoneytypeFk'],
othermoneyorderNum: item['othermoneyorderNum'],
othermoneyorderBudgettype: item['othermoneyorderBudgettype'],
othermoneyorderTime_date: item['othermoneyorderTime_date'],
othermoneyorderTime_time: item['othermoneyorderTime_time'],
othermoneyorderOriginaltotalprice: item['othermoneyorderOriginaltotalprice'],
othermoneyorderFacttotalprice: item['othermoneyorderFacttotalprice'],
othermoneyorderpayMoney: item['othermoneyorderpayMoney'],
othermoneyorderpayPaytype: item['othermoneyorderpayPaytype'],
othermoneyorderpayTime_date: item['othermoneyorderpayTime_date'],
othermoneyorderpayTime_time: item['othermoneyorderpayTime_time'],
othermoneyorderUseroperatoridUserFk: item['othermoneyorderUseroperatoridUserFk'],
othermoneyorderRemark: item['othermoneyorderRemark']
          }
/*for(let jtem in custom) {obj[jtem] = item[jtem]}*/this.sourceData.push(obj)
        }
      },
onCopy(e) {
// console.log(e)      },
onError(e) {
// console.log(e)      },
cutExcelData() {
this.loading=truethis.util.blur()
this.getExcelSourceData()
letobj= {}, str='', idx=0lettmpSourceData=this.util.copy(this.sourceData)
letsucc=0for(letitemintmpSourceData) {
if(tmpSourceData[item].result=='导入成功') {
succ++obj=tmpSourceData[item]
str+=obj['othermoneyorderName'] +'\t'str+=obj['othermoneyorderUseridUserFk'] +'\t'str+=obj['othermoneyorderUsername'] +'\t'str+=obj['othermoneyorderPhone'] +'\t'str+=obj['othermoneyorderOthermoneytypeidOthermoneytypeFk'] +'\t'str+=obj['othermoneyorderNum'] +'\t'str+=obj['othermoneyorderBudgettype'] +'\t'str+=obj['othermoneyorderTime_date'] +'\t'str+=obj['othermoneyorderTime_time'] +'\t'str+=obj['othermoneyorderOriginaltotalprice'] +'\t'str+=obj['othermoneyorderFacttotalprice'] +'\t'str+=obj['othermoneyorderpayMoney'] +'\t'str+=obj['othermoneyorderpayPaytype'] +'\t'str+=obj['othermoneyorderpayTime_date'] +'\t'str+=obj['othermoneyorderpayTime_time'] +'\t'str+=obj['othermoneyorderUseroperatoridUserFk'] +'\t'str+=obj['othermoneyorderRemark']
// console.log(Object.keys(custom).length)letlen=Object.keys(custom).length, i=0for(letjtemincustom) {
if(i==0) str+='\t'if(i==len-1) {
str+=obj[jtem] +'\n'              }
else {
str+=obj[jtem] +'\t'              }
i++            }
if(i==0) str+='\n'// console.log("idx: ", idx)this.sourceData.splice(idx, 1)
          }
else {
idx++          }
// console.log(this.sourceData)        }
this.copyContent=strthis.$refs.ht.hotInstance.updateSettings({
data: this.sourceData        })
if(succ>0) {
this.$message({type: 'success', message: '已成功剪切 '+succ+' 条记录,请粘贴到本地 Excel 保存,并处理剩余错误记录'})
        }
elseif(succ==0) {
this.$message({type: 'warning', message: '暂无导入成功记录,请处理错误记录或新数据', duration: 0, showClose: true})
        }
this.loading=false      },
clearExcel() {
this.util.blur()
this.$confirm('此操作将永久清空该 Excel,是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'        }).then(() => {
this.$refs.ht.hotInstance.loadData(null)
        }).catch(() => {
        })
      },
beforeCheckExcel() {
this.util.blur()
this.$refs.numranger.$data.op="beforeCheckExcel"this.$refs.numranger.$data.dialogNumRangerVisible=true      },
afterCheckExcel(val) {
this.loading=true// console.log("class_afterCheckExcel: ", val)arr= []
if(val.from!=-1) {
for(leti=val.from-1; i<val.to; i++) {
arr.push(i)
          }
        }
else {
letdata=this.$refs.ht.hotInstance.getSourceData()
letlen=this.$refs.ht.hotInstance.countRows()
// console.log(data)for(leti=0; i<len; i++) {
letname=data[i]["othermoneyorderName"]
if(!this.util.isEmpty(name)) {
arr.push(i)
            }
          }
        }
// console.log(arr)this.$refs.ht.hotInstance.validateRows(arr, (valid) => {
// console.log("validateRows", valid)if (valid) {
this.$message({type: 'success', message: "检查通过,无格式异常数据"})
this.importExcelDsiable=false          }
else {
this.$message({type: 'error', message: "检查失败,请注意格式,修复“红色区域”数据", duration: 0, showClose: true})
this.importExcelDsiable=true          }
this.loading=false        })
      },
handleSourceData() {
this.sourceData=this.$refs.ht.hotInstance.getSourceData()
letdata= [], rows= []
for(letitemofarr) {
data.push(this.sourceData[item])
rows.push(item)
        }
// console.log(this.sourceData)// console.log(data)letjsonObj=nullletorderDate=null, orderTime=null, orderDatetime=nullletpayDate=null, payTime=null, payDatetime=nullthis.jsonData= []
letrowIdx=0for(letitemofdata) {
orderDate=this.util.getDateOrNow(item['othermoneyorderTime_date'])
orderTime=this.util.getTimeOrNow(item['othermoneyorderTime_time'])
orderDatetime=this.util.getDatetimeOrNow(orderDate, orderTime)
payDate=this.util.getDateOrNow(item['othermoneyorderpayTime_date'])
payTime=this.util.getTimeOrNow(item['othermoneyorderpayTime_time'])
payDatetime=this.util.getDatetimeOrNow(payDate, payTime)
// console.log('row: ', rows[rowIdx])// console.log('row val: ', orderDate)this.$refs.ht.hotInstance.setDataAtRowProp(rows[rowIdx], 'othermoneyorderTime_date', orderDate, this.sourceData)
this.$refs.ht.hotInstance.setDataAtRowProp(rows[rowIdx], 'othermoneyorderTime_time', orderTime, this.sourceData)
this.$refs.ht.hotInstance.setDataAtRowProp(rows[rowIdx], 'othermoneyorderpayTime_date', payDate, this.sourceData)
this.$refs.ht.hotInstance.setDataAtRowProp(rows[rowIdx], 'othermoneyorderpayTime_time', payTime, this.sourceData)
jsonObj= {
othermoneyorderName: item['othermoneyorderName'],
othermoneyorderUseridUserFk: user_lbl2id[item['othermoneyorderUseridUserFk']],
othermoneyorderUsername: item['othermoneyorderUsername'],
othermoneyorderPhone: item['othermoneyorderPhone'],
othermoneyorderOthermoneytypeidOthermoneytypeFk: othermoneytype_lbl2id[item['othermoneyorderOthermoneytypeidOthermoneytypeFk']],
othermoneyorderNum: item['othermoneyorderNum'],
othermoneyorderBudgettype: item['othermoneyorderBudgettype'] =='收入'?'0' : '1',
othermoneyorderTime: orderDatetime,
othermoneyorderOriginaltotalprice: item['othermoneyorderOriginaltotalprice'],
othermoneyorderFacttotalprice: item['othermoneyorderFacttotalprice'],
othermoneyorderpayMoney: item['othermoneyorderpayMoney'],
othermoneyorderpayPaytype: paytype_lbl2id[item['othermoneyorderpayPaytype']],
othermoneyorderpayTime: payDatetime,
othermoneyorderUseroperatoridUserFk: operator_lbl2id[item['othermoneyorderUseroperatoridUserFk']],
othermoneyorderRemark: item['othermoneyorderRemark']
          }
for(letjtemincustom) {
jsonObj[jtem] =item[jtem]
          }
this.jsonData.push(jsonObj)
rowIdx++        }
// console.log('jsonData: ', this.jsonData)      },
asyncimportExcel() {
this.loading=truethis.util.blur()
this.handleSourceData() // 获取最新数据if(this.jsonData.length==0) { // 判空处理this.$message({type: 'warning', message: '导入失败,数据为空', duration: 0, showClose:true})
this.loading=falsethis.importExcelDsiable=truereturnfalse        }
awaitimportBG.importOtherMoneyOrder({
otherMoneyOrderInfoList: this.jsonData        })
        .then(res=> {
console.log(res)
if (res.success) {
this.$message({type: 'success', message: res.msg})
          }
elseif (res.error) {
this.$message({type: 'warning', message: res.msg})
          }
else {
this.$message({type: 'error', message: '导入失败,原因未知'})
          }
this.sourceData=this.$refs.ht.hotInstance.getSourceData()
letrs=res.data, msg=''result= {}
for(letitemofrs) {
result[''+item.errIndex] =item          }
for(letitemofarr) {
if(this.util.isEmpty(result[''+item])) {
msg='导入成功'            }
else {
msg=result[''+item].errMsg            }
this.$refs.ht.hotInstance.setDataAtRowProp(item, 'result', msg, this.sourceData)
          }
        })
        .catch(function (error) {
console.log(error)
this.$message({type: 'error', message: '系统错误'})
        })
this.loading=false      },
asyncinitExcel() {
this.loading=truethat=this// 为下面闭包做准备awaitfinanceApi.getOthermoneytypeListByOrgId({
needDel: 'needDel'// 删除的不展示,只是为了修复显示ID的BUG        })
          .then(res=> {
console.log(res)
if(res.success){
letdata=res.data, src= []
for(letitemofdata) {
src.push(item.othermoneytypeName)
othermoneytype_id2lbl[item.othermoneytypeId] =item.othermoneytypeNameothermoneytype_lbl2id[item.othermoneytypeName] =item.othermoneytypeId              }
this.columns[5].source=src            }
elseif(res.nullwarn) {
            }
else {
this.$message({type: 'error', message: res.msg})
            }
          })
          .catch(function (error) {
console.log(error)
this.$message({type: 'error', message: '系统错误'})
          })
// 关联用户awaituser.getUserCheckList({})
          .then(res=> {
console.log(res)
if(res.success){
letdata=res.data.data, src= []
for(letitemofdata) {
src.push(item.userName)
user_id2lbl[item.userId] =item.userNameuser_lbl2id[item.userName] =item.userIduser_id2phone[item.userId] =item.userMobileuser_phone2id[item.userMobile] =item.userId              }
this.columns[2].source=src            }
elseif(res.nullwarn) {
            }
else {
this.$message({type: 'error', message: res.msg})
            }
          })
          .catch(function (error) {
console.log(error)
this.$message({type: 'error', message: '系统错误'})
          })
// 经办人awaituser.getTeacherCheckList({})
          .then(res=> {
// console.log(res)if(res.success){
letdata=res.data.data, src= []
for(letitemofdata) {
src.push(item.userName)
operator_id2lbl[item.userId] =item.userNameoperator_lbl2id[item.userName] =item.userId              }
this.columns[16].source=src            }
elseif(res.nullwarn) {
            }
else {
this.$message({type: 'error', message: res.msg})
            }
          })
          .catch(function (error) {
console.log(error)
this.$message({type: 'error', message: '系统错误'})
          })
/*await eduApi.getCustomparam({ userType: 1 }).then(res => {// console.log('custom', res)if(res.success){let arr = res.datafor(let item of arr) {this.colHeaders.push(item.customparamName)this.columns.push({data: item.customparamId,type: 'text',validator: /^.{0,200}$/})custom[item.customparamId] = item.customparamName}}else if(res.nullwarn) {}else {this.$message({type: 'error', message: res.msg})}}).catch(function (error) {console.log(error)this.$message({type: 'error', message: '系统错误'})})*/this.hotSettings.startCols=this.colHeaders.lengththis.hotSettings.colHeaders=this.colHeadersthis.hotSettings.columns=this.columns// 初始化必要信息paytype_id2lbl['0'] ='现金'; paytype_lbl2id['现金'] ='0'paytype_id2lbl['1'] ='支付宝'; paytype_lbl2id['支付宝'] ='1'paytype_id2lbl['2'] ='微信'; paytype_lbl2id['微信'] ='2'paytype_id2lbl['3'] ='网银'; paytype_lbl2id['网银'] ='3'paytype_id2lbl['4'] ='其它'; paytype_lbl2id['其它'] ='4'this.loading=false      },
initEvent () {
// 复制全部按钮letcopyBtn=document.getElementById("copyExcel")
this.Handsontable.dom.addEvent(copyBtn, 'click', () => {
this.util.blur()
letrow1=-1, col1=-1, row2=-1, col2=-1that.$confirm('是否复制“导入结果”该列?', '提示', {
confirmButtonText: '是',
cancelButtonText: '否',
type: 'warning'          }).then(() => {
row1=col1=0          }).catch(() => {
row1=0col1=1          }).finally(() => {
row2=that.$refs.ht.hotInstance.countRows() -1col2=that.$refs.ht.hotInstance.countCols() -1// console.log(row1, col1, row2, col2)that.$refs.ht.hotInstance.selectCells([[row1,col1,row2,col2]])
document.execCommand('copy')
that.$message({type: 'success', message: '复制成功,请尽快粘贴到本地 Excel 进行保存'})
          })
        })
      },
handleTest() {
console.log(this.$refs.ht.hotInstance.countRows())
console.log(this.$refs.ht.hotInstance.countCols())
      }
    },
mounted () {
if(this.$route.fullPath===menu.root.child.finance.child.income.child.batchAdd.fullPath||this.$route.fullPath===menu.root.child.finance.child.spending.child.batchAdd.fullPath) {
this.initExcel()
this.initEvent()
      }
    }
  }
</script><stylelang="stylus"scoped>@import'~@/assets/styles/varibles.styl'>>> .is-readOnlyfont-weight: boldcolor: redfont-size: 14px>>> .handsontable    .changeTypebackground: transparentborder: nonecolor: #616161theadtr:first-childthborder-top: noneth:first-childborder-left: nonethcolor: #616161font-weight: normalbackground-color: #fff  .batch-importmargin-bottom: 30px    .headerdisplay: flexflex-wrap: nowrapflex-direction: rowjustify-content: space-betweenpadding: 0010px      .addmargin-right: 5pxmargin-left: 0border: 0pxheight: 28pxletter-spacing: 1px      .resetmargin-right: 0    .bodyheight: 1000px      .overfwidth: 100%height: 100%overflow: hidden        .wrapperwidth: 100%height: 100%overflow: auto</style>
目录
相关文章
|
3月前
|
人工智能 JavaScript 算法
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
Vue 中 key 属性的深入解析:改变 key 导致组件销毁与重建
491 0
|
3月前
|
JavaScript UED
用组件懒加载优化Vue应用性能
用组件懒加载优化Vue应用性能
|
3月前
|
JavaScript 前端开发 开发者
Vue 自定义进度条组件封装及使用方法详解
这是一篇关于自定义进度条组件的使用指南和开发文档。文章详细介绍了如何在Vue项目中引入、注册并使用该组件,包括基础与高级示例。组件支持分段配置(如颜色、文本)、动画效果及超出进度提示等功能。同时提供了完整的代码实现,支持全局注册,并提出了优化建议,如主题支持、响应式设计等,帮助开发者更灵活地集成和定制进度条组件。资源链接已提供,适合前端开发者参考学习。
339 17
|
3月前
|
JavaScript 前端开发 UED
Vue 表情包输入组件实现代码及详细开发流程解析
这是一篇关于 Vue 表情包输入组件的使用方法与封装指南的文章。通过安装依赖、全局注册和局部使用,可以快速集成表情包功能到 Vue 项目中。文章还详细介绍了组件的封装实现、高级配置(如自定义表情列表、主题定制、动画效果和懒加载)以及完整集成示例。开发者可根据需求扩展功能,例如 GIF 搜索或自定义表情上传,提升用户体验。资源链接提供进一步学习材料。
182 1
|
3月前
|
JavaScript API 开发者
Vue框架中常见指令的应用概述。
通过以上的详细解析,你应该已经初窥Vue.js的指令的威力了。它们是Vue声明式编程模型的核心之一,无论是构建简单的静态网站还是复杂的单页面应用,你都会经常用到。记住,尽管Vue提供了大量预定义的指令,你还可以创建自定义指令以满足特定的需求。为你的Vue应用程序加上这些功能增强器,让编码变得更轻松、更愉快吧!
63 1
|
3月前
|
存储 JavaScript 前端开发
如何高效实现 vue 文件批量下载及相关操作技巧
在Vue项目中,实现文件批量下载是常见需求。例如文档管理系统或图片库应用中,用户可能需要一次性下载多个文件。本文介绍了三种技术方案:1) 使用`file-saver`和`jszip`插件在前端打包文件为ZIP并下载;2) 借助后端接口完成文件压缩与传输;3) 使用`StreamSaver`解决大文件下载问题。同时,通过在线教育平台的实例详细说明了前后端的具体实现步骤,帮助开发者根据项目需求选择合适方案。
235 0
|
3月前
|
JavaScript 前端开发 UED
Vue 项目中如何自定义实用的进度条组件
本文介绍了如何使用Vue.js创建一个灵活多样的自定义进度条组件。该组件可接受进度段数据数组作为输入,动态渲染进度段,支持动画效果和内容展示。当进度超出总长时,超出部分将以红色填充。文章详细描述了组件的设计目标、实现步骤(包括props定义、宽度计算、模板渲染、动画处理及超出部分的显示),并提供了使用示例。通过此组件,开发者可根据项目需求灵活展示进度情况,优化用户体验。资源地址:[https://pan.quark.cn/s/35324205c62b](https://pan.quark.cn/s/35324205c62b)。
107 0
|
存储 前端开发 JavaScript
为什么我不再用Vue,改用React?
当我走进现代前端开发行业的时候,我做了一个每位开发人员都要做的决策:选择一个合适的框架。当时正逢 jQuery 被淘汰,前端开发者们不再用它编写难看的、非结构化的老式 JavaScript 程序了。
|
5月前
|
JavaScript
vue实现任务周期cron表达式选择组件
vue实现任务周期cron表达式选择组件
666 4
|
4月前
|
JavaScript 数据可视化 前端开发
基于 Vue 与 D3 的可拖拽拓扑图技术方案及应用案例解析
本文介绍了基于Vue和D3实现可拖拽拓扑图的技术方案与应用实例。通过Vue构建用户界面和交互逻辑,结合D3强大的数据可视化能力,实现了力导向布局、节点拖拽、交互事件等功能。文章详细讲解了数据模型设计、拖拽功能实现、组件封装及高级扩展(如节点类型定制、连接样式优化等),并提供了性能优化方案以应对大数据量场景。最终,展示了基础网络拓扑、实时更新拓扑等应用实例,为开发者提供了一套完整的实现思路和实践经验。
498 77