feat(PageContent): ✨ 支持默认工具栏的导入
This commit is contained in:
@@ -87,13 +87,23 @@
|
|||||||
<!-- 导出 -->
|
<!-- 导出 -->
|
||||||
<template v-else-if="item === 'exports'">
|
<template v-else-if="item === 'exports'">
|
||||||
<el-button
|
<el-button
|
||||||
icon="FolderOpened"
|
icon="download"
|
||||||
circle
|
circle
|
||||||
title="导出"
|
title="导出"
|
||||||
v-hasPerm="[`${contentConfig.pageName}:export`]"
|
v-hasPerm="[`${contentConfig.pageName}:export`]"
|
||||||
@click="handleToolbar(item)"
|
@click="handleToolbar(item)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
<!-- 导入 -->
|
||||||
|
<template v-else-if="item === 'imports'">
|
||||||
|
<el-button
|
||||||
|
icon="upload"
|
||||||
|
circle
|
||||||
|
title="导入"
|
||||||
|
v-hasPerm="[`${contentConfig.pageName}:import`]"
|
||||||
|
@click="handleToolbar(item)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
<!-- 搜索 -->
|
<!-- 搜索 -->
|
||||||
<template v-else-if="item === 'search'">
|
<template v-else-if="item === 'search'">
|
||||||
<el-button
|
<el-button
|
||||||
@@ -111,7 +121,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
:icon="item.icon"
|
:icon="item.icon"
|
||||||
circle
|
circle
|
||||||
:title="item.text"
|
:title="item.title"
|
||||||
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
|
v-hasPerm="[`${contentConfig.pageName}:${item.auth}`]"
|
||||||
@click="handleToolbar(item.name)"
|
@click="handleToolbar(item.name)"
|
||||||
/>
|
/>
|
||||||
@@ -329,7 +339,9 @@
|
|||||||
<!-- 导出弹窗 -->
|
<!-- 导出弹窗 -->
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="exportsModalVisible"
|
v-model="exportsModalVisible"
|
||||||
|
:align-center="true"
|
||||||
title="导出数据"
|
title="导出数据"
|
||||||
|
width="600px"
|
||||||
style="padding-right: 0"
|
style="padding-right: 0"
|
||||||
@close="handleCloseExportsModal"
|
@close="handleCloseExportsModal"
|
||||||
>
|
>
|
||||||
@@ -390,6 +402,72 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<!-- 导入弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
v-model="importsModalVisible"
|
||||||
|
:align-center="true"
|
||||||
|
title="导入数据"
|
||||||
|
width="600px"
|
||||||
|
style="padding-right: 0"
|
||||||
|
@close="handleCloseImportsModal"
|
||||||
|
>
|
||||||
|
<!-- 滚动 -->
|
||||||
|
<el-scrollbar max-height="60vh">
|
||||||
|
<!-- 表单 -->
|
||||||
|
<el-form
|
||||||
|
ref="importsFormRef"
|
||||||
|
label-width="auto"
|
||||||
|
style="padding-right: var(--el-dialog-padding-primary)"
|
||||||
|
:model="importsFormData"
|
||||||
|
:rules="importsFormRules"
|
||||||
|
>
|
||||||
|
<el-form-item label="文件名" prop="files">
|
||||||
|
<el-upload
|
||||||
|
class="w-full"
|
||||||
|
ref="uploadRef"
|
||||||
|
v-model:file-list="importsFormData.files"
|
||||||
|
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
|
||||||
|
:drag="true"
|
||||||
|
:limit="1"
|
||||||
|
:auto-upload="false"
|
||||||
|
:on-exceed="handleFileExceed"
|
||||||
|
>
|
||||||
|
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||||
|
<div class="el-upload__text">
|
||||||
|
将文件拖到此处,或<em>点击上传</em>
|
||||||
|
</div>
|
||||||
|
<template #tip>
|
||||||
|
<div class="el-upload__tip">
|
||||||
|
*.xlsx / *.xls
|
||||||
|
<el-link
|
||||||
|
v-if="contentConfig.importsTemplate"
|
||||||
|
type="primary"
|
||||||
|
icon="download"
|
||||||
|
:underline="false"
|
||||||
|
@click="handleDownloadTemplate"
|
||||||
|
>
|
||||||
|
下载模板
|
||||||
|
</el-link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-scrollbar>
|
||||||
|
<!-- 弹窗底部操作按钮 -->
|
||||||
|
<template #footer>
|
||||||
|
<div style="padding-right: var(--el-dialog-padding-primary)">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:disabled="importsFormData.files.length === 0"
|
||||||
|
@click="handleImportsSubmit"
|
||||||
|
>
|
||||||
|
确 定
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="handleCloseImportsModal">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -399,11 +477,15 @@ import { ref, reactive } from "vue";
|
|||||||
import { useDateFormat, useThrottleFn } from "@vueuse/core";
|
import { useDateFormat, useThrottleFn } from "@vueuse/core";
|
||||||
import { hasAuth } from "@/plugins/permission";
|
import { hasAuth } from "@/plugins/permission";
|
||||||
import SvgIcon from "@/components/SvgIcon/index.vue";
|
import SvgIcon from "@/components/SvgIcon/index.vue";
|
||||||
import type {
|
import {
|
||||||
TableProps,
|
type TableProps,
|
||||||
PaginationProps,
|
type PaginationProps,
|
||||||
FormInstance,
|
type FormInstance,
|
||||||
FormRules,
|
type FormRules,
|
||||||
|
type UploadInstance,
|
||||||
|
type UploadUserFile,
|
||||||
|
type UploadRawFile,
|
||||||
|
genFileId,
|
||||||
} from "element-plus";
|
} from "element-plus";
|
||||||
|
|
||||||
// 对象类型
|
// 对象类型
|
||||||
@@ -442,18 +524,22 @@ export interface IContentConfig<T = any> {
|
|||||||
list: IObject[];
|
list: IObject[];
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
// 删除的网络请求函数(需返回promise)
|
|
||||||
deleteAction?: (ids: string) => Promise<any>;
|
|
||||||
// 后端导出的网络请求函数(需返回promise)
|
|
||||||
exportAction?: (queryParams: T) => Promise<any>;
|
|
||||||
// 前端全量导出的网络请求函数(需返回promise)
|
|
||||||
exportsAction?: (queryParams: T) => Promise<IObject[]>;
|
|
||||||
// 修改属性的网络请求函数(需返回promise)
|
// 修改属性的网络请求函数(需返回promise)
|
||||||
modifyAction?: (data: {
|
modifyAction?: (data: {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
field: string;
|
field: string;
|
||||||
value: boolean | string | number;
|
value: boolean | string | number;
|
||||||
}) => Promise<any>;
|
}) => Promise<any>;
|
||||||
|
// 删除的网络请求函数(需返回promise)
|
||||||
|
deleteAction?: (ids: string) => Promise<any>;
|
||||||
|
// 后端导出的网络请求函数(需返回promise)
|
||||||
|
exportAction?: (queryParams: T) => Promise<any>;
|
||||||
|
// 前端全量导出的网络请求函数(需返回promise)
|
||||||
|
exportsAction?: (queryParams: T) => Promise<IObject[]>;
|
||||||
|
// 前端导入模板
|
||||||
|
importsTemplate?: string | (() => Promise<any>);
|
||||||
|
// 前端导入的网络请求函数(需返回promise)
|
||||||
|
importsAction?: (data: IObject[]) => Promise<any>;
|
||||||
// 主键名(默认为id)
|
// 主键名(默认为id)
|
||||||
pk?: string;
|
pk?: string;
|
||||||
// 表格工具栏(默认支持add,delete,export,也可自定义)
|
// 表格工具栏(默认支持add,delete,export,也可自定义)
|
||||||
@@ -472,6 +558,7 @@ export interface IContentConfig<T = any> {
|
|||||||
defaultToolbar?: Array<
|
defaultToolbar?: Array<
|
||||||
| "refresh"
|
| "refresh"
|
||||||
| "filter"
|
| "filter"
|
||||||
|
| "imports"
|
||||||
| "exports"
|
| "exports"
|
||||||
| "search"
|
| "search"
|
||||||
| {
|
| {
|
||||||
@@ -561,8 +648,6 @@ const toolbar = props.contentConfig.toolbar ?? ["add", "delete"];
|
|||||||
const defaultToolbar = props.contentConfig.defaultToolbar ?? [
|
const defaultToolbar = props.contentConfig.defaultToolbar ?? [
|
||||||
"refresh",
|
"refresh",
|
||||||
"filter",
|
"filter",
|
||||||
"exports",
|
|
||||||
"search",
|
|
||||||
];
|
];
|
||||||
// 表格列
|
// 表格列
|
||||||
const cols = ref(
|
const cols = ref(
|
||||||
@@ -716,36 +801,147 @@ function handleExports() {
|
|||||||
if (props.contentConfig.exportsAction) {
|
if (props.contentConfig.exportsAction) {
|
||||||
props.contentConfig.exportsAction(lastFormData).then((res) => {
|
props.contentConfig.exportsAction(lastFormData).then((res) => {
|
||||||
worksheet.addRows(res);
|
worksheet.addRows(res);
|
||||||
downloadXlsx(workbook, filename);
|
workbook.xlsx
|
||||||
|
.writeBuffer()
|
||||||
|
.then((buffer) => {
|
||||||
|
saveXlsx(buffer, filename);
|
||||||
|
})
|
||||||
|
.catch((error) => console.log(error));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error("未配置exportsAction");
|
ElMessage.error("未配置exportsAction");
|
||||||
}
|
}
|
||||||
} else if (exportsFormData.origin === ExportsOriginEnum.SELECTED) {
|
|
||||||
worksheet.addRows(selectionData.value);
|
|
||||||
downloadXlsx(workbook, filename);
|
|
||||||
} else {
|
} else {
|
||||||
worksheet.addRows(pageData.value);
|
worksheet.addRows(
|
||||||
downloadXlsx(workbook, filename);
|
exportsFormData.origin === ExportsOriginEnum.SELECTED
|
||||||
|
? selectionData.value
|
||||||
|
: pageData.value
|
||||||
|
);
|
||||||
|
workbook.xlsx
|
||||||
|
.writeBuffer()
|
||||||
|
.then((buffer) => {
|
||||||
|
saveXlsx(buffer, filename);
|
||||||
|
})
|
||||||
|
.catch((error) => console.log(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function downloadXlsx(workbook: ExcelJS.Workbook, filename: string) {
|
// 导入表单
|
||||||
workbook.xlsx
|
const uploadRef = ref<UploadInstance>();
|
||||||
.writeBuffer()
|
const importsModalVisible = ref(false);
|
||||||
.then((buffer) => {
|
const importsFormRef = ref<FormInstance>();
|
||||||
const blob = new Blob([buffer], {
|
const importsFormData = reactive<{
|
||||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
files: UploadUserFile[];
|
||||||
});
|
}>({
|
||||||
const downloadUrl = window.URL.createObjectURL(blob);
|
files: [],
|
||||||
const downloadLink = document.createElement("a");
|
});
|
||||||
downloadLink.href = downloadUrl;
|
const importsFormRules: FormRules = {
|
||||||
downloadLink.download = filename;
|
files: [{ required: true, message: "请选择文件" }],
|
||||||
document.body.appendChild(downloadLink);
|
};
|
||||||
downloadLink.click();
|
// 打开导入弹窗
|
||||||
document.body.removeChild(downloadLink);
|
function handleOpenImportsModal() {
|
||||||
window.URL.revokeObjectURL(downloadUrl);
|
importsModalVisible.value = true;
|
||||||
})
|
}
|
||||||
.catch((error) => console.log("Error writing excel export", error));
|
// 覆盖前一个文件
|
||||||
|
function handleFileExceed(files: File[]) {
|
||||||
|
uploadRef.value!.clearFiles();
|
||||||
|
const file = files[0] as UploadRawFile;
|
||||||
|
file.uid = genFileId();
|
||||||
|
uploadRef.value!.handleStart(file);
|
||||||
|
}
|
||||||
|
// 下载导入模板
|
||||||
|
function handleDownloadTemplate() {
|
||||||
|
const importsTemplate = props.contentConfig.importsTemplate;
|
||||||
|
if (typeof importsTemplate === "string") {
|
||||||
|
window.open(importsTemplate);
|
||||||
|
} else if (typeof importsTemplate === "function") {
|
||||||
|
importsTemplate().then((response) => {
|
||||||
|
const fileData = response.data;
|
||||||
|
const fileName = decodeURI(
|
||||||
|
response.headers["content-disposition"].split(";")[1].split("=")[1]
|
||||||
|
);
|
||||||
|
saveXlsx(fileData, fileName);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ElMessage.error("未配置importsTemplate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 导入确认
|
||||||
|
const handleImportsSubmit = useThrottleFn(() => {
|
||||||
|
importsFormRef.value?.validate((valid: boolean) => {
|
||||||
|
valid && handleImports();
|
||||||
|
});
|
||||||
|
}, 3000);
|
||||||
|
// 关闭导入弹窗
|
||||||
|
function handleCloseImportsModal() {
|
||||||
|
importsModalVisible.value = false;
|
||||||
|
importsFormRef.value?.resetFields();
|
||||||
|
nextTick(() => {
|
||||||
|
importsFormRef.value?.clearValidate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 导入
|
||||||
|
function handleImports() {
|
||||||
|
const importsAction = props.contentConfig.importsAction;
|
||||||
|
if (importsAction === undefined) {
|
||||||
|
ElMessage.error("未配置importsAction");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 获取选择的文件
|
||||||
|
const file = importsFormData.files[0].raw as File;
|
||||||
|
// 创建Workbook实例
|
||||||
|
const workbook = new ExcelJS.Workbook();
|
||||||
|
// 使用FileReader对象来读取文件内容
|
||||||
|
const fileReader = new FileReader();
|
||||||
|
// 二进制字符串的形式加载文件
|
||||||
|
fileReader.readAsArrayBuffer(file);
|
||||||
|
fileReader.onload = (ev) => {
|
||||||
|
if (ev.target !== null && ev.target.result !== null) {
|
||||||
|
const result = ev.target.result as ArrayBuffer;
|
||||||
|
// 从 buffer中加载数据解析
|
||||||
|
workbook.xlsx
|
||||||
|
.load(result)
|
||||||
|
.then((workbook) => {
|
||||||
|
// 解析后的数据
|
||||||
|
const data = [];
|
||||||
|
// 获取第一个worksheet内容
|
||||||
|
const worksheet = workbook.getWorksheet(1);
|
||||||
|
if (worksheet) {
|
||||||
|
// 获取第一行的标题
|
||||||
|
const fields: any[] = [];
|
||||||
|
worksheet.getRow(1).eachCell((cell) => {
|
||||||
|
fields.push(cell.value);
|
||||||
|
});
|
||||||
|
// 遍历工作表的每一行(从第二行开始,因为第一行通常是标题行)
|
||||||
|
for (
|
||||||
|
let rowNumber = 2;
|
||||||
|
rowNumber <= worksheet.rowCount;
|
||||||
|
rowNumber++
|
||||||
|
) {
|
||||||
|
const rowData: IObject = {};
|
||||||
|
const row = worksheet.getRow(rowNumber);
|
||||||
|
// 遍历当前行的每个单元格
|
||||||
|
row.eachCell((cell, colNumber) => {
|
||||||
|
// 获取标题对应的键,并将当前单元格的值存储到相应的属性名中
|
||||||
|
rowData[fields[colNumber - 1]] = cell.value;
|
||||||
|
});
|
||||||
|
// 将当前行的数据对象添加到数组中
|
||||||
|
data.push(rowData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.length === 0) {
|
||||||
|
ElMessage.error("未解析到数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
importsAction(data).then(() => {
|
||||||
|
ElMessage.success("导入数据成功");
|
||||||
|
handleCloseImportsModal();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => console.log(error));
|
||||||
|
} else {
|
||||||
|
ElMessage.error("读取文件失败");
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
// 操作栏
|
// 操作栏
|
||||||
function handleToolbar(name: string) {
|
function handleToolbar(name: string) {
|
||||||
@@ -756,6 +952,9 @@ function handleToolbar(name: string) {
|
|||||||
case "exports":
|
case "exports":
|
||||||
handleOpenExportsModal();
|
handleOpenExportsModal();
|
||||||
break;
|
break;
|
||||||
|
case "imports":
|
||||||
|
handleOpenImportsModal();
|
||||||
|
break;
|
||||||
case "search":
|
case "search":
|
||||||
emit("searchClick");
|
emit("searchClick");
|
||||||
break;
|
break;
|
||||||
@@ -878,23 +1077,30 @@ function exportPageData(formData: IObject = {}) {
|
|||||||
const fileName = decodeURI(
|
const fileName = decodeURI(
|
||||||
response.headers["content-disposition"].split(";")[1].split("=")[1]
|
response.headers["content-disposition"].split(";")[1].split("=")[1]
|
||||||
);
|
);
|
||||||
const fileType =
|
saveXlsx(fileData, fileName);
|
||||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
|
||||||
|
|
||||||
const blob = new Blob([fileData], { type: fileType });
|
|
||||||
const downloadUrl = window.URL.createObjectURL(blob);
|
|
||||||
const downloadLink = document.createElement("a");
|
|
||||||
downloadLink.href = downloadUrl;
|
|
||||||
downloadLink.download = fileName;
|
|
||||||
document.body.appendChild(downloadLink);
|
|
||||||
downloadLink.click();
|
|
||||||
document.body.removeChild(downloadLink);
|
|
||||||
window.URL.revokeObjectURL(downloadUrl);
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
ElMessage.error("未配置exportAction");
|
ElMessage.error("未配置exportAction");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 浏览器保存文件
|
||||||
|
function saveXlsx(fileData: BlobPart, fileName: string) {
|
||||||
|
const fileType =
|
||||||
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
|
||||||
|
|
||||||
|
const blob = new Blob([fileData], { type: fileType });
|
||||||
|
const downloadUrl = window.URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
const downloadLink = document.createElement("a");
|
||||||
|
downloadLink.href = downloadUrl;
|
||||||
|
downloadLink.download = fileName;
|
||||||
|
|
||||||
|
document.body.appendChild(downloadLink);
|
||||||
|
downloadLink.click();
|
||||||
|
|
||||||
|
document.body.removeChild(downloadLink);
|
||||||
|
window.URL.revokeObjectURL(downloadUrl);
|
||||||
|
}
|
||||||
|
|
||||||
// 暴露的属性和方法
|
// 暴露的属性和方法
|
||||||
defineExpose({ fetchPageData, exportPageData, getFilterParams });
|
defineExpose({ fetchPageData, exportPageData, getFilterParams });
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ function usePage() {
|
|||||||
searchRef.value?.toggleVisible();
|
searchRef.value?.toggleVisible();
|
||||||
}
|
}
|
||||||
// 涮选数据
|
// 涮选数据
|
||||||
function handleFilterChange(filterParams) {
|
function handleFilterChange(filterParams: IObject) {
|
||||||
const queryParams = searchRef.value?.getQueryParams();
|
const queryParams = searchRef.value?.getQueryParams();
|
||||||
contentRef.value?.fetchPageData({ ...queryParams, ...filterParams }, true);
|
contentRef.value?.fetchPageData({ ...queryParams, ...filterParams }, true);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user