const ExcelJS = require('exceljs');
/**
* 多级获取数据
* @param key 用点分开
* @param data 数据对象
*/
export function getData(key, data) {
if (data != null && key) {
if (key.indexOf('.') > -1) {
let keys = key.split('.');
let nextKey = key.substr(keys[0].length + 1);
let currKey = keys[0];
if (currKey.indexOf('[') > -1) {
return getData(nextKey, getData(currKey, data))
}
return getData(nextKey, data[keys[0]]);
} else if (key.indexOf('[') > -1) {
let currKeyPre = key.substr(0, key.indexOf('['));
let indexStart = key.indexOf('[') + 1;
let i = Number(key.substr(indexStart, key.length - indexStart - 1));
let d = data[currKeyPre];
if (d instanceof Array) {
return d[i];
}
return d;
} else {
return data[key];
}
}
}
ExcelReader.instance().setDateFormat(this.dateFormat).parse(file).then(reader => {
let data = reader.getData(); // 可以指定获取哪个Sheet和标题位置
this.titles = data.titles;
this.list = data.list;
});
export function formatDate(date, fmt) {
if (!date) {
return "";
}
if (!fmt) {
fmt = 'yyyy-MM-dd';
}
try {
if (typeof date === 'string' && date.indexOf("-") > -1) {
date = new Date(date.replace(/-/g, '/'));
} else {
date = new Date(date);
}
} catch (e) {
console.log(e);
}
if (date == null || date === '') {
return null;
}
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
}
let o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
"h+": date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, //小时
"H+": date.getHours(), //小时
'm+': date.getMinutes(),
's+': date.getSeconds()
};
for (let k in o) {
if (new RegExp(`(${k})`).test(fmt)) {
let str = o[k] + '';
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str));
}
}
return fmt;
}
export class ExcelReader {
static instance() {
return new ExcelReader();
}
workbook = {};
sheets = [];
dateFormat = "yyyy-MM-dd HH:mm:ss";
setDateFormat(format) {
this.dateFormat = format;
return this;
}
parse(file) {
return new Promise((ok, cancel) => {
let loading = Loading.service({
lock: true,
text: `读取${file.name}...`,
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
this.workbook = new ExcelJS.Workbook();
this.workbook.xlsx.load(file, {}).then(res => {
let sheets = this.workbook.worksheets;
for (let i = 0; i < sheets.length; i++) {
let sheet = sheets[i];
let sheetData = {name: sheet.name, index: i, rows: []};
let rowCount = sheet.rowCount;
for (let rowIndex = 1; rowIndex <= rowCount; rowIndex++) {
let row = sheet.getRow(rowIndex);
let rowData = [];
for (let cellIndex = 1; cellIndex <= row.cellCount; cellIndex++) {
let cell = row.getCell(cellIndex);
// type:2数值,3字符串,4日期,6公式
if (cell.type === 4 && cell.value && this.dateFormat) {
if (this.dateFormat.toLowerCase() === 'timestamp') {
rowData.push(new Date(cell.value).getTime());
} else {
rowData.push(formatDate(new Date(cell.value), this.dateFormat));
}
} else {
rowData.push(cell.text.trim());
}
}
sheetData.rows.push(rowData);
}
this.sheets.push(sheetData);
}
console.log(this.sheets);
loading.close();
ok(this);
}).catch(err => {
loading.close();
cancel(err);
})
});
}
/**
@param sheetIndex 指定Sheet 0开始
@param titleIndex 指定标题行0开始
@param sheetName 如果不为空则sheetIndex失效
*/
getData(sheetIndex, titleIndex = 0, sheetName) {
// 解析
let readIndex = 0;
if (sheetIndex != null) {
readIndex = sheetIndex;
}
if (sheetName) {
readIndex = this.sheets.findIndex(sheet => sheet.name.toLowerCase() === sheetName.toLowerCase());
}
let sheet = this.sheets[readIndex];
if (sheet.data) {
return sheet.data;
}
let titleData = sheet.rows[titleIndex];
let list = [];
for (let i = titleIndex + 1; i < sheet.rows.length; i++) {
let data = {message: '', loading: false};
for (let ci = 0; ci < titleData.length; ci++) {
data[titleData[ci]] = sheet.rows[i][ci];
}
list.push(data);
}
sheet.data = {titles: titleData, list: list};
return sheet.data;
}
}
export function imageToBase64(url) {
return new Promise((ok, cancel) => {
api.base64(url).then(res => {
const extension = url.split('?')[0].substring(url.split('?')[0].lastIndexOf('.') + 1).toLowerCase();
ok({base64: res.data, extension});
}).catch(err => {
ok();
})
})
}
function openDownloadDialog(url, saveName) {
if (typeof url == 'object' && url instanceof Blob) {
url = URL.createObjectURL(url); // 创建blob地址
}
let aLink = document.createElement('a');
aLink.href = url;
aLink.download = saveName || ''; // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效
let event;
if (window.MouseEvent) event = new MouseEvent('click');
else {
event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
}
aLink.dispatchEvent(event);
}
// 使用方式:ExcelBuilder.buildSheet([],sheetName:'test',titles:[{'key':'name'}],cloWidth:[{col:15}]).write(fileName);
export class ExcelBuilder {
static instance() {
return new ExcelBuilder();
}
workbook = new ExcelJS.Workbook();
currSheet = null;
loading = Loading.service({
lock: true,
text: `生成中...`,
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
// 生成标题
buildTitle(titles) {
let sheet = this.currSheet;
if (!sheet) {
throw Error("不能直接生成标题,请设置currSheet变量");
}
if (titles) {
let keys = Object.keys(titles);
let columns = [];
let row = sheet.getRow(1);
row.height = 25;
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
let config = titles[key];
let header = config.header || config;
let width = config.width || header.length * 3;
let image = config.image || false;
let hidden = config.hidden || false;
columns.push({header, key, width, image, hidden});
let cell = row.getCell(i + 1);
cell.fill = {type: 'pattern', pattern: 'solid', fgColor: {argb: 'FFB6DDE8'}};
cell.font = {size: 15, bold: true};
cell.alignment = {vertical: 'middle', horizontal: 'center'};
cell.border = {
top: {style: 'thin'},
left: {style: 'thin'},
bottom: {style: 'thin'},
right: {style: 'thin'}
};
}
console.log("标题:", columns);
sheet.columns = columns;
return columns;
}
}
addImage(cell, images, index, config) {
let workbook = this.workbook;
let sheet = this.currSheet;
return new Promise((o, c) => {
if (index >= images.length) {
return o();
}
let width = 1 / images.length;
imageToBase64(images[index]).then(data => {
if (data) {
let image = workbook.addImage(data);
// TODO 这的布局可能还有问题
sheet.addImage(image, {
tl: {col: cell.col - 1 + width * index, row: cell.row - 1},
br: {col: cell.col - 1 + width * (index + 1), row: cell.row}
});
} else {
cell.value = {text: images.join(",")};
}
this.addImage(cell, images, index + 1, config).then(r => o());
})
})
}
addCell(value, cell, config) {
return new Promise((ok, cancel) => {
if (value) {
if (config.image) {
if (value instanceof Array) {
// 如果需要下载图片
this.addImage(cell, value, 0, config).then(r => ok());
} else {
this.addImage(cell, [value], 0, config).then(r => ok());
}
} else {
cell.value = value;
return ok();
}
} else {
return ok();
}
})
}
addRow(data, row) {
let keys = this.currSheet.columns ? this.currSheet.columns.map(c => c.key) : Object.keys(data);
let _this = this;
let call = (i) => {
return new Promise((ok, cancel) => {
if (i >= keys.length) {
return ok();
}
let cell = row.getCell(i + 1);
cell.alignment = {vertical: 'middle', horizontal: 'center'};
let config = _this.currSheet.titles[i];
_this.addCell(getData(keys[i], data), cell, config).then(_ => call(i + 1).then(_ => ok()));
});
};
return new Promise((ok, cancel) => call(0).then(_ => ok()));
}
buildSheet(list = [], {sheetName, titles, defaultRowHeight = 15}) {
if (this.loading) {
this.loading = Loading.service({
lock: true,
text: `生成中...`,
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
}
let _this = this;
sheetName = sheetName || 'Sheet' + (this.workbook.worksheets.length);
return new Promise((ok, cancel) => {
let sheet = this.workbook.getWorksheet(sheetName);
sheet = sheet || this.workbook.addWorksheet(sheetName);
// sheet.properties.defaultRowHeight = defaultRowHeight;
this.currSheet = sheet;
this.currSheet.titles = this.buildTitle(titles);
let startRowNumber = sheet.lastRow.number + 1;
if (list && list.length) {
let addList = (i) => {
return new Promise((ok, cancel) => {
if (i >= list.length) {
return ok();
}
let row = sheet.getRow(startRowNumber + i);
row.height = defaultRowHeight;
let data = list[i];
_this.loading.text = `${sheetName},进度:${i + 1}/${list.length}`;
_this.addRow(data, row).then(_ => addList(i + 1).then(_ => ok(_this)))
});
};
addList(0).then(_ => {
console.log(`${sheetName}生成完成`);
_this.loading.close();
ok(_this);
});
} else {
_this.loading.close();
ok(_this);
}
})
}
write(fileName) {
console.log("开始下载");
let buffer = this.workbook.xlsx.writeBuffer();
buffer.then(res => {
let blob = new Blob([res], {type: "application/octet-stream"});
openDownloadDialog(blob, fileName + '.xlsx');
})
}
}
// 主要用于下载模板
export function downloadFile(url, fileName = '') {
axios.create().request({url: url, responseType: 'arraybuffer'}).then(res => {
let blob = new Blob([res.data], {type: 'application/stream'});
let objectUrl = URL.createObjectURL(blob);
downloadURL(objectUrl, fileName)
})
}
<read-excel-button :post="doImport"
:columns="importColumns"
template-name="合同导入模板.xlsx"
date-format="yyyy-MM-dd"
template="导入模板base64格式,带前缀">
</read-excel-button>
<script>
data(){
return {
importColumns: {
"商品名": {required: true, key: 'name', reg: /\s+/,validator:(data,config,value)=>true},
}
}
doImport(data, index) {
return new Promise((ok, cancel) => {
console.log(data);
product.add(data).then(res => {
console.log(res);
ok();
}).catch(err => {
console.log(err);
cancel(err.msg);
});
// 解析合同分成
})
}
</script>
<template>
<div style="margin-left:10px;margin-right:10px;display: inline-block;">
<el-button @click="selectFile" type="info">{{name}}</el-button>
<input type="file" ref="file" @change="getFile" style="display: none;" accept=".xlsx"/>
<el-dialog :title="title" :visible.sync="show" width="90%" @opened="selectAll" top="20px" :close-on-click-modal="false">
<el-table id="importTable" :data="list" height="400px" @selection-change="selectionChange" ref="importTable"
:row-class-name="({row,index})=>row.message?'warning-row':row.loading?'current-row':row.success?'success-row ':''">
<el-table-column type="selection" width="55" :selectable="(row)=>!row.disabled"></el-table-column>
<el-table-column type="index"></el-table-column>
<el-table-column v-for="title in titles" :label="title" :prop="title" :key="title" :min-width="title.length*15+20"></el-table-column>
<el-table-column label="状态" prop="message" min-width="100px">
<template slot-scope="scope">
<div v-if="scope.row.message" style="color:red;">{{scope.row.message}}</div>
<div v-else-if="scope.row.loading"><i class="el-icon-loading"></i></div>
<div v-else-if="scope.row.success">完成</div>
</template>
</el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button @click="exportError" v-if="errorList.length && !importing" type="warning">导出错误({{errorList.length}})</el-button>
<el-button @click="show = false" type="danger">关 闭</el-button>
<el-button type="primary" @click="doImport" :loading="importing">导入</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {ExcelBuilder, ExcelReader} from "../utils/excel";
import {downloadFile} from "../utils/download";
export default {
name: "ReadExcelButton",
props: {
name: {type: String, default: "上传数据"},
title: {type: String, default: "数据内容"},
post: Function,
postAll: Function,
template: String, // 模板base64
templateName: {
type: String,
default: '模板下载.xlsx',
},
dateFormat: {
type: String,
default: 'yyyy-MM-dd HH:mm:ss', // timestamp
},
columns: Object, //字段设置与校验 excel标题:{required:false,reg:'正则校验',validator:function(value),key:'将数据复制到字段',formatter:function(value),message:'错误提示'}
},
data() {
return {
show: false,
titles: [],
list: [],
selectList: [],
importing: false,
fileName: this.name,
}
},
computed: {
errorList() {
return this.list.filter(i => i.error)
}
},
methods: {
// 选择文件
selectFile() {
/// 下载模板提示
this.$confirm("请选择操作", '导入数据', {
confirmButtonText: '导入数据', cancelButtonText: '下载模板', distinguishCancelAndClose: true, callback: (action, instance) => {
if (action === 'confirm') {
this.$refs.file.dispatchEvent(new MouseEvent('click'));
} else if (action === 'cancel' && this.template) {
downloadFile(this.template, this.templateName);
}
}
});
},
validAndFormat(list) {
if (this.columns) {
// 验证数据
let keys = Object.keys(this.columns);
for (let i = 0; i < list.length; i++) {
let data = list[i]; // 当前验证数据
for (let k = 0; k < keys.length; k++) {
let key = keys[k]; // 当前验证字段
let value = data[key];
let config = this.columns[key]; // 字段配置;reg,validator,key,formatter,message
let valid = true; // 是否是有效的数据
let empty = (value == null || value === '');
if (config.required && empty) {
valid = false;
} else if (config.reg != null && (config.required || !empty)) {
if (!RegExp(config.reg).test(value)) {
valid = false;
}
}
if (config.validator != null && typeof config.validator === 'function' && valid) {
valid = config.validator(data, config, value);
}
if (!valid) {
data.disabled = true;
data.error = true;
data.message = config.message || `${key},无效数据`;
}
// 数据格式化
if (config.formatter != null && typeof config.formatter === 'function' && valid) {
value = config.formatter(data, config, value);
}
if (config.key != null && config.key) {
if (config.key.includes('.')) {
let ks = config.key.split(".");
if (data[ks[0]] == null) {
data[ks[0]] = {};
}
let lastValue = data[ks[0]];
for (let ki = 0; ki < ks.length; ki++) {
if (lastValue == null) {
data[ks[ki]] = {};
}
if (ki === ks.length - 1) {
lastValue[ks[ki]] = value;
} else {
lastValue = data[ks[ki]];
}
}
}
data[config.key] = value;
} else {
data[key] = value;
}
}
}
console.log("格式化后结果:", list);
}
},
getFile() {
let file = this.$refs.file.files[0];
this.fileName = file.name.substr(0, file.name.lastIndexOf("."));
ExcelReader.instance().setDateFormat(this.dateFormat).parse(file).then(reader => {
let data = reader.getData();
this.titles = data.titles;
this.list = data.list;
this.validAndFormat(this.list);
this.show = true;
// 清空选中文件,下次可以继续选择同样文件
this.$refs.file.value = null;
});
},
selectAll() {
this.$refs.importTable.toggleAllSelection(this.list.filter(i => !i.disabled));
},
doImport() {
if (this.selectList.length <= 0) {
this.$message({message: '请选择数据', type: "warning"});
return;
}
console.log("选中数据:", this.selectList);
this.importing = true;
if (this.post != null) {
let success = [];
let tableRowsEle = null;
try {
let tableEle = document.getElementById('importTable');
let tableBodyEle = tableEle.getElementsByClassName('el-table__body')[0];
tableRowsEle = tableBodyEle.getElementsByClassName('el-table__row');
} catch (e) {
console.error(e);
}
let doPost = (data, index, call) => {
if (index >= this.selectList.length) {
call();
return;
}
// 滚动到可显示的底部区域
if (tableRowsEle) {
tableRowsEle[index].scrollIntoView(false);
}
data.loading = true;
this.post(data, index).then(res => {
success.push(data);
doPost(this.selectList[index + 1], index + 1, call)
}).catch(err => {
data.message = err;
data.error = true;
doPost(this.selectList[index + 1], index + 1, call)
}).finally(() => {
data.loading = false;
});
};
doPost(this.selectList[0], 0, res => {
success.forEach((suc) => {
this.$refs.importTable.toggleRowSelection(suc);
suc.disabled = true;
suc.success = true;
});
this.importing = false;
this.$message({type: 'success', message: '导入完成'});
this.$emit("success", success);
})
} else if (this.postAll != null) {
this.postAll(this.selectList);
}
},
selectionChange(list) {
this.selectList = list;
},
exportError() {
if (this.errorList && this.errorList.length) {
let titles = {};
for (let i = 0; i < this.titles.length; i++) {
let key = this.titles[i];
titles[key] = key;
}
titles['message'] = '状态';
ExcelBuilder.instance().buildSheet(this.errorList, {titles}).then(workbook => {
workbook.write(this.fileName + '-错误数据');
})
} else {
this.$message({type: 'error', message: '没有错误字体'})
}
}
}
}
</script>
<style scoped>
.el-table >>> .warning-row {
background: #f7b4ad;
}
.el-table >>> .current-row {
background-color: #ceffaa;
}
.el-table >>> .success-row {
background-color: #e9fbe6;
}
</style>
export function setData(key, data, value) {
if (key != null) {
if (key.indexOf(".") > -1) {
let keys = key.split('.');
let nextKey = key.substr(keys[0].length + 1);
let currKey = keys[0];
if (currKey.indexOf('[') > -1) {
let d = getData(currKey, data);
if (!d) {
d = setData(currKey, data, nextKey ? {} : value);
}
return setData(nextKey, d, value)
}
if (!data[keys[0]]) {
data[keys[0]] = {};
}
return setData(nextKey, data[keys[0]], value);
} else if (key.indexOf('[') > -1) {
let currKeyPre = key.substr(0, key.indexOf('['));
let indexStart = key.indexOf('[') + 1;
let i = Number(key.substr(indexStart, key.length - indexStart - 1));
if (!data[currKeyPre]) {
data[currKeyPre] = [];
}
if (data[currKeyPre] instanceof Array) {
if (data[currKeyPre].length < i) {
for (let j = data[currKeyPre].length; j <= i; j++) {
data[currKeyPre][j] = {};
}
}
data[currKeyPre][i] = value;
return data[currKeyPre][i];
}
data[currKeyPre] = value;
return data;
} else {
data[key] = value;
return data;
}
}
}
<template>
<el-button @click="doExport">导出</el-button>
</template>
<script>
import {ExcelBuilder} from "../utils/excel";
import {getData, setData} from "../utils";
export default {
name: "BuilderExcelButton",
props: {
// 导出配置:{sheetName:'Sheet1',data:[Function,Array],columns,expand,tableRef}
config: {
type: [Object, Array, Function],
default() {
return {
sheetName: 'Sheet1',
data: [{
name: '测试',
product: {price: 1},
channels: [
{name: '渠道1', channelId: 1},
{name: '渠道2', channelId: 2}
]
}], // 数组或查询方法
tableRef: 'list', // 用于获取表格字段,需要配置对应prop,label属性
image: false, // 是否导出图片
columns: { // 与tableRef冲突,但是优先级大于tableRef
'name': {
header: '标题名称',
image: false, // 是否下载图片
formatter: function (data, column, value) {
console.log('格式化数据:', value);
return data['name'];
}
}
},
// 扩展字段,用于导出不在tableRef中的字段配置
expand: {
'product.price': {header: '价格'}
}
}
}
},
name: String, // 导出文件名
},
data() {
return {
loading: false,
}
},
methods: {
doExport() {
this.loading = false;
// 解析配置到数组
let loadConfig = () => new Promise((ok, cancel) => ok(this.config));
if (typeof this.config === 'function') {
loadConfig = this.config;
}
loadConfig().then(config => {
let configs = config instanceof Array ? config : [config];
console.log("开始导出", config);
if (!config) {
return;
}
// 解析标题配置到config.titles
configs.forEach((c, i) => this.parseHeaders(c, i));
this.exportSheet(configs).then(workbook => {
console.log("完成", workbook);
this.loading = false;
let name = this.name ? this.name : "导出列表";
workbook.write(name);
}).catch(err => {
this.$message({type: 'error', message: '导出失败:' + err})
})
})
},
// 解析表格对应的列配置
parseTableColumns(tableRef) {
if (tableRef) {
let refObject = this.parentRefObject(tableRef, this.$parent);
if (refObject) {
let columns = refObject.tableColumns();
let titles = {};
columns.filter(c => c.property).forEach(c => {
titles[c.property] = {header: c.label, image: c.label.includes("图"), formatter: c.formatter};
});
return titles;
}
}
return null;
},
// 解析单个配置文件
parseHeaders(config, index) {
config.sheetName = config.sheetName || `Sheet${index + 1}`;
let titles = config.columns || this.parseTableColumns(config.tableRef);
if (titles) {
if (config.expand) {
let entries = Object.entries(titles);
entries.forEach((e, i) => e.index = i);
Object.entries(config.expand).forEach(e => entries.push(e));
// TODO 根据索引排序标题
config.titles = Object.fromEntries(entries);
} else {
config.titles = titles;
}
}
if (config.titles) {
// 重新设置图片属性
let keys = Object.keys(config.titles);
for (let i = 0; i < keys.length; i++) {
let title = config.titles[keys[i]];
if (title.image) {
title.image = config.image || false;
}
}
}
},
// 创建Sheet
exportSheet(configs, index = 0, workbook) {
return new Promise((ok, cancel) => {
if (index >= configs.length) {
return ok(workbook);
}
console.log("创建数据:", configs[index]);
let config = configs[index];
let data = config.data;
let defaultRowHeight = Object.values(config.titles).find(t => t.image) ? 50 : 15;
if (typeof data === 'function') {
data().then(list => {
data = list;
this.formatterData(config.titles, list);
(workbook || ExcelBuilder.instance()).buildSheet(list, {titles: config.titles, sheetName: config.sheetName, defaultRowHeight}).then(workbook => {
this.exportSheet(configs, index + 1, workbook).then(r => ok(r));
})
}).catch(err => {
this.$message({type: 'error', message: config.sheetName + '导出失败'});
this.exportSheet(configs, index + 1, workbook).then(r => ok(r));
})
} else if (data instanceof Array) {
this.formatterData(config.titles, data);
(workbook || ExcelBuilder.instance()).buildSheet(data, {titles: config.titles, sheetName: config.sheetName, defaultRowHeight}).then(workbook => {
this.exportSheet(configs, index + 1, workbook).then(r => ok(r));
})
} else {
cancel();
}
})
},
formatterData(titles, list) {
let formatterColumns = Object.entries(titles).filter(c => c[1].formatter != null);
for (let i = 0; i < formatterColumns.length; i++) {
let key = formatterColumns[i][0];
let column = formatterColumns[i][1];
let formatter = column.formatter;
for (let di = 0; di < list.length; di++) {
let data = list[di];
let value = getData(key, data);
if (formatter != null) {
setData(key, data, formatter(data, column, value));
}
}
}
},
exportList() {
let titles = this.columns || this.parseTableColumns();
console.log(titles);
if (!titles) {
this.$message({type: 'error', message: '导出字段配置错误,请联系管理员'});
return;
}
// this.loading = true;
this.loadData(titles).then(list => {
// 格式化数据
let formatterColumns = Object.entries(titles).filter(c => c[1].formatter != null);
for (let i = 0; i < formatterColumns.length; i++) {
let key = formatterColumns[i][0];
let column = formatterColumns[i][1];
let formatter = column.formatter;
for (let di = 0; di < list.length; di++) {
let data = list[di];
let value = data[key];
if (formatter != null) {
data[key] = formatter(data, column, value)
}
}
}
console.log(list);
console.log(titles);
ExcelBuilder.instance().buildSheet(list, {titles}).then(workbook => {
this.loading = false;
let name = this.name ? this.name : "字体列表";
workbook.write(name);
})
}).catch(err => {
});
},
parentRefObject(ref, parent) {
if (parent) {
if (parent.$refs[ref]) {
return parent.$refs[ref];
} else {
return this.parentRefObject(ref, parent.$parent)
}
}
return null;
}
}
}
</script>
<style scoped>
</style>
写完了,电脑好卡.有疑问请直接留言,我会不定期查看.