为基础资料维护页面增加导入导出
This commit is contained in:
@@ -442,6 +442,20 @@ const expectedButtonPermissions = [
|
||||
'master:pallet:add',
|
||||
'master:pallet:edit',
|
||||
'master:pallet:print',
|
||||
'master:strain:import',
|
||||
'master:strain:export',
|
||||
'master:customer:import',
|
||||
'master:customer:export',
|
||||
'master:site:import',
|
||||
'master:site:export',
|
||||
'master:line:import',
|
||||
'master:line:export',
|
||||
'master:warehouse:import',
|
||||
'master:warehouse:export',
|
||||
'master:pallet:import',
|
||||
'master:pallet:export',
|
||||
'master:tunnel:import',
|
||||
'master:tunnel:export',
|
||||
'business:inbound:query',
|
||||
'business:inbound:add',
|
||||
'business:inbound:edit',
|
||||
@@ -1221,8 +1235,8 @@ test('窄屏模式控件仍保留正式退出与测试批次切换入口', async
|
||||
|
||||
test('17 个页面的真实权限门禁逐项匹配独立硬编码契约', async () => {
|
||||
assert.equal(expectedPagePermissions.length, 17)
|
||||
assert.equal(expectedButtonPermissions.length, 38)
|
||||
assert.equal(new Set(expectedPermissions).size, 55)
|
||||
assert.equal(expectedButtonPermissions.length, 52)
|
||||
assert.equal(new Set(expectedPermissions).size, 69)
|
||||
const relativePaths = [
|
||||
...Object.values(masterPageContracts),
|
||||
...Object.values(businessPageContracts),
|
||||
@@ -1231,7 +1245,7 @@ test('17 个页面的真实权限门禁逐项匹配独立硬编码契约', async
|
||||
assert.equal(relativePaths.length, 17)
|
||||
|
||||
const actualPermissions = []
|
||||
for (const relativePath of relativePaths) {
|
||||
for (const relativePath of [...relativePaths, 'src/components/MasterExcelActions/index.vue']) {
|
||||
const source = await readFile(path.join(frontendRoot, relativePath), 'utf8')
|
||||
const guarded = extractGuardedVuePermissions(source)
|
||||
assert.ok(guarded.length > 0, `${relativePath} 缺少真实权限门禁`)
|
||||
@@ -1840,6 +1854,16 @@ test('六个基础资料页面逐页满足 API、权限、分页和安全删除
|
||||
assert.doesNotMatch(source, /\.catch\(\(\) => \{\}\)/, `${resource} 页面禁止空 catch`)
|
||||
assert.doesNotMatch(source, />\s*(?:导入|导出)\s*<\/el-button>/)
|
||||
assert.doesNotMatch(source, /\bhandle(?:Import|Export)\b|proxy\.download\(/)
|
||||
assert.match(
|
||||
masterToolbar,
|
||||
new RegExp(`<master-excel-actions resource="${resource}" :allow-import="isMasterWriteAllowed" :query-params="queryParams" @imported="getList"\\s*/>`),
|
||||
`${resource} 必须复用基础资料导入导出组件`
|
||||
)
|
||||
assert.match(
|
||||
masterToolbar,
|
||||
/<\/template>\s*<master-excel-actions\b[\s\S]*?\/>\s*<right-toolbar\b/,
|
||||
`${resource} 导入导出应放在写操作之后、刷新工具栏之前`
|
||||
)
|
||||
|
||||
assert.match(source, /let listGeneration = 0;/)
|
||||
assert.match(source, /function isCurrentListRequest\(listContext\)\s*{\s*return listContext\.generation === listGeneration;?\s*}/)
|
||||
@@ -1950,6 +1974,18 @@ test('六个基础资料页面逐页满足 API、权限、分页和安全删除
|
||||
}
|
||||
})
|
||||
|
||||
test('隧道资料页复用基础资料导入导出组件', async () => {
|
||||
const source = await readFile(path.join(frontendRoot, 'src/views/master/tunnel/index.vue'), 'utf8')
|
||||
const masterToolbar = source.match(/<el-row\b[^>]*class="[^"]*\bmaster-toolbar\b[^"]*"[^>]*>[\s\S]*?<\/el-row>/)?.[0] || ''
|
||||
assert.match(
|
||||
masterToolbar,
|
||||
/<master-excel-actions resource="tunnel" :allow-import="isMasterWriteAllowed" :query-params="queryParams" @imported="getList"\s*\/>/
|
||||
)
|
||||
assert.match(masterToolbar, /<\/template>\s*<master-excel-actions\b[\s\S]*?\/>\s*<right-toolbar\b/)
|
||||
assert.doesNotMatch(source, />\s*(?:导入|导出)\s*<\/el-button>/)
|
||||
assert.doesNotMatch(source, /\bhandle(?:Import|Export)\b|proxy\.download\(/)
|
||||
})
|
||||
|
||||
test('五个业务页面逐页满足 API、精确权限、列表和交互基础契约', async (t) => {
|
||||
for (const [resource, contract] of Object.entries(businessPageContracts)) {
|
||||
await t.test(contract.relativePath, async () => {
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<el-col v-if="allowImport && resource === 'strain'" :span="1.5">
|
||||
<el-button type="info" plain icon="Upload" @click="openImport" v-hasPermi="['master:strain:import']">导入</el-button>
|
||||
</el-col>
|
||||
<el-col v-if="resource === 'strain'" :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['master:strain:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col v-if="allowImport && resource === 'customer'" :span="1.5">
|
||||
<el-button type="info" plain icon="Upload" @click="openImport" v-hasPermi="['master:customer:import']">导入</el-button>
|
||||
</el-col>
|
||||
<el-col v-if="resource === 'customer'" :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['master:customer:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col v-if="allowImport && resource === 'site'" :span="1.5">
|
||||
<el-button type="info" plain icon="Upload" @click="openImport" v-hasPermi="['master:site:import']">导入</el-button>
|
||||
</el-col>
|
||||
<el-col v-if="resource === 'site'" :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['master:site:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col v-if="allowImport && resource === 'line'" :span="1.5">
|
||||
<el-button type="info" plain icon="Upload" @click="openImport" v-hasPermi="['master:line:import']">导入</el-button>
|
||||
</el-col>
|
||||
<el-col v-if="resource === 'line'" :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['master:line:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col v-if="allowImport && resource === 'warehouse'" :span="1.5">
|
||||
<el-button type="info" plain icon="Upload" @click="openImport" v-hasPermi="['master:warehouse:import']">导入</el-button>
|
||||
</el-col>
|
||||
<el-col v-if="resource === 'warehouse'" :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['master:warehouse:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col v-if="allowImport && resource === 'pallet'" :span="1.5">
|
||||
<el-button type="info" plain icon="Upload" @click="openImport" v-hasPermi="['master:pallet:import']">导入</el-button>
|
||||
</el-col>
|
||||
<el-col v-if="resource === 'pallet'" :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['master:pallet:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col v-if="allowImport && resource === 'tunnel'" :span="1.5">
|
||||
<el-button type="info" plain icon="Upload" @click="openImport" v-hasPermi="['master:tunnel:import']">导入</el-button>
|
||||
</el-col>
|
||||
<el-col v-if="resource === 'tunnel'" :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['master:tunnel:export']">导出</el-button>
|
||||
</el-col>
|
||||
|
||||
<el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
:limit="1"
|
||||
accept=".xlsx, .xls"
|
||||
:headers="uploadHeaders"
|
||||
:action="uploadAction + '?updateSupport=' + upload.updateSupport"
|
||||
:disabled="upload.isUploading"
|
||||
:on-progress="handleFileUploadProgress"
|
||||
:on-success="handleFileSuccess"
|
||||
:on-error="handleFileError"
|
||||
:on-change="handleFileChange"
|
||||
:on-remove="handleFileRemove"
|
||||
:auto-upload="false"
|
||||
drag
|
||||
>
|
||||
<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 text-center">
|
||||
<div class="el-upload__tip">
|
||||
<el-checkbox v-model="upload.updateSupport" />是否更新已经存在的{{ resourceLabel }}数据
|
||||
</div>
|
||||
<span>仅允许导入xls、xlsx格式文件。</span>
|
||||
<el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="downloadTemplate">下载模板</el-link>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitFileForm">确 定</el-button>
|
||||
<el-button @click="upload.open = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, getCurrentInstance, reactive } from "vue";
|
||||
import { getToken } from "@/utils/auth";
|
||||
|
||||
const props = defineProps({
|
||||
resource: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
allowImport: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
queryParams: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
const emit = defineEmits(["imported"]);
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const resourceLabels = {
|
||||
strain: "菌种",
|
||||
customer: "客户",
|
||||
site: "客户现场",
|
||||
line: "产线",
|
||||
warehouse: "仓库",
|
||||
pallet: "托盘",
|
||||
tunnel: "隧道"
|
||||
};
|
||||
const resourceLabel = computed(() => resourceLabels[props.resource] || "基础资料");
|
||||
const uploadHeaders = computed(() => ({ Authorization: "Bearer " + getToken() }));
|
||||
const uploadAction = computed(() => `${import.meta.env.VITE_APP_BASE_API}/master/${props.resource}/importData`);
|
||||
const upload = reactive({
|
||||
open: false,
|
||||
title: "",
|
||||
isUploading: false,
|
||||
updateSupport: false,
|
||||
selectedFile: null
|
||||
});
|
||||
|
||||
function openImport() {
|
||||
upload.title = `${resourceLabel.value}导入`;
|
||||
upload.open = true;
|
||||
upload.selectedFile = null;
|
||||
upload.isUploading = false;
|
||||
}
|
||||
|
||||
function downloadTemplate() {
|
||||
proxy.download(
|
||||
`master/${props.resource}/importTemplate`,
|
||||
{},
|
||||
`${props.resource}_template_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
}
|
||||
|
||||
function handleExport() {
|
||||
proxy.download(
|
||||
`master/${props.resource}/export`,
|
||||
{ ...props.queryParams },
|
||||
`${props.resource}_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
}
|
||||
|
||||
function handleFileUploadProgress() {
|
||||
upload.isUploading = true;
|
||||
}
|
||||
|
||||
function handleFileChange(file) {
|
||||
upload.selectedFile = file;
|
||||
}
|
||||
|
||||
function handleFileRemove() {
|
||||
upload.selectedFile = null;
|
||||
}
|
||||
|
||||
function showImportResult(message) {
|
||||
const safe = String(message ?? "")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/\n/g, "<br/>");
|
||||
proxy.$alert(
|
||||
"<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + safe + "</div>",
|
||||
"导入结果",
|
||||
{ dangerouslyUseHTMLString: true }
|
||||
);
|
||||
}
|
||||
|
||||
function handleFileSuccess(response, file) {
|
||||
upload.open = false;
|
||||
upload.isUploading = false;
|
||||
proxy.$refs.uploadRef.handleRemove(file);
|
||||
showImportResult(response.msg);
|
||||
emit("imported");
|
||||
}
|
||||
|
||||
function handleFileError() {
|
||||
upload.isUploading = false;
|
||||
proxy.$modal.msgError("导入失败,请稍后重试");
|
||||
}
|
||||
|
||||
function submitFileForm() {
|
||||
const file = upload.selectedFile;
|
||||
const fileName = file && file.name ? file.name.toLowerCase() : "";
|
||||
if (!file || (!fileName.endsWith(".xls") && !fileName.endsWith(".xlsx"))) {
|
||||
proxy.$modal.msgError("请选择后缀为 “xls”或“xlsx”的文件。");
|
||||
return;
|
||||
}
|
||||
proxy.$refs.uploadRef.submit();
|
||||
}
|
||||
</script>
|
||||
@@ -43,6 +43,7 @@ import ImageUpload from "@/components/ImageUpload"
|
||||
import ImagePreview from "@/components/ImagePreview"
|
||||
// 字典标签组件
|
||||
import DictTag from '@/components/DictTag'
|
||||
import MasterExcelActions from '@/components/MasterExcelActions'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
@@ -65,6 +66,7 @@ app.component('ImageUpload', ImageUpload)
|
||||
app.component('ImagePreview', ImagePreview)
|
||||
app.component('RightToolbar', RightToolbar)
|
||||
app.component('Editor', Editor)
|
||||
app.component('MasterExcelActions', MasterExcelActions)
|
||||
|
||||
app.use(router)
|
||||
app.use(store)
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
<master-excel-actions resource="customer" :allow-import="isMasterWriteAllowed" :query-params="queryParams" @imported="getList" />
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
<master-excel-actions resource="line" :allow-import="isMasterWriteAllowed" :query-params="queryParams" @imported="getList" />
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
<master-excel-actions resource="pallet" :allow-import="isMasterWriteAllowed" :query-params="queryParams" @imported="getList" />
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
<master-excel-actions resource="site" :allow-import="isMasterWriteAllowed" :query-params="queryParams" @imported="getList" />
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
<master-excel-actions resource="strain" :allow-import="isMasterWriteAllowed" :query-params="queryParams" @imported="getList" />
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
<master-excel-actions resource="tunnel" :allow-import="isMasterWriteAllowed" :query-params="queryParams" @imported="getList" />
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
<el-button type="warning" plain icon="FolderAdd" :disabled="!selectedIds.length" @click="openAdoptionDialog" v-hasPermi="['system:testbatch:adopt']">纳入测试批次</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
<master-excel-actions resource="warehouse" :allow-import="isMasterWriteAllowed" :query-params="queryParams" @imported="getList" />
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
||||
</el-row>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user