Merge remote-tracking branch 'websoft/master'
This commit is contained in:
182
src/option/base/cargo-type.js
Normal file
182
src/option/base/cargo-type.js
Normal file
@@ -0,0 +1,182 @@
|
||||
const typeLevelDic = [
|
||||
{ label: '一级货物类型', value: 1 },
|
||||
{ label: '二级货物类型', value: 2 },
|
||||
];
|
||||
|
||||
const validateCargoName = (rule, value, callback) => {
|
||||
const v = String(value || '').trim();
|
||||
if (!v) {
|
||||
callback(new Error('请输入货物类型名称'));
|
||||
} else if (v.length > 50) {
|
||||
callback(new Error('货物类型名称不能超过50字符'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const validateRemark = (rule, value, callback) => {
|
||||
if (String(value || '').length > 200) {
|
||||
callback(new Error('备注不能超过200字'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
export const getCargoTypeOption = ctx => ({
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 760,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: false,
|
||||
searchIcon: false,
|
||||
searchMenuSpan: 24,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
indexWidth: 70,
|
||||
viewBtn: false,
|
||||
delBtn: false,
|
||||
editBtn: false,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '类型',
|
||||
prop: 'typeLevel',
|
||||
type: 'radio',
|
||||
dataType: 'number',
|
||||
dicData: typeLevelDic,
|
||||
search: true,
|
||||
searchSpan: 12,
|
||||
value: 2,
|
||||
span: 24,
|
||||
minWidth: 120,
|
||||
editDisabled: true,
|
||||
rules: [{ required: true, message: '请选择货物类型级别', trigger: 'change' }],
|
||||
change: ({ value }) => ctx.handleTypeLevelChange(value),
|
||||
},
|
||||
{
|
||||
label: '上级货物类型',
|
||||
prop: 'parentId',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
display: false,
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请选择上级货物类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '上级货物类型',
|
||||
prop: 'parentCargoName',
|
||||
slot: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
label: '上级货物类型编码',
|
||||
prop: 'parentCargoCode',
|
||||
slot: true,
|
||||
readonly: true,
|
||||
editDisabled: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 160,
|
||||
rules: [{ required: true, message: '请选择上级货物类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '货物类型',
|
||||
prop: 'cargoName',
|
||||
minWidth: 150,
|
||||
maxlength: 50,
|
||||
showWordLimit: true,
|
||||
rules: [{ validator: validateCargoName, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '货物类型编码',
|
||||
prop: 'cargoCode',
|
||||
minWidth: 140,
|
||||
search: true,
|
||||
searchSpan: 12,
|
||||
maxlength: 4,
|
||||
editDisabled: true,
|
||||
rules: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
const form = ctx.form || {};
|
||||
const code = String(value || '').trim();
|
||||
const pattern = form.typeLevel === 1 ? /^\d{2}$/ : /^\d{4}$/;
|
||||
if (!code) {
|
||||
callback(new Error('请输入货物类型编码'));
|
||||
} else if (!pattern.test(code)) {
|
||||
callback(new Error('货物类型编码格式不正确'));
|
||||
} else if (
|
||||
form.typeLevel === 2 &&
|
||||
form.parentCargoCode &&
|
||||
!code.startsWith(form.parentCargoCode)
|
||||
) {
|
||||
callback(new Error('二级编码前2位必须与上级货物类型编码一致'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
change: ({ value }) => ctx.handleCargoCodeChange(value),
|
||||
},
|
||||
{
|
||||
label: '创建人',
|
||||
prop: 'createUserName',
|
||||
slot: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
minWidth: 180,
|
||||
maxlength: 200,
|
||||
showWordLimit: true,
|
||||
overHidden: true,
|
||||
rules: [{ validator: validateRemark, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '更新时间',
|
||||
prop: 'updateTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
prop: 'createTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 160,
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -15,14 +15,45 @@ const parseBlobJson = async blob => {
|
||||
return text ? JSON.parse(text) : {};
|
||||
};
|
||||
|
||||
export const openImportDialog = (vm, businessName, onSuccess) => {
|
||||
/**
|
||||
* 加工后端返回的导入失败明细 Excel。
|
||||
* 若传入 failDetailDecorator(函数),则动态加载 exceljs 读取 blob 流,
|
||||
* 交由 decorator 完成改写原因列、调整列宽等加工,再导出新的 blob;
|
||||
* 否则原样返回 blob(保持旧行为,不影响未使用 decorator 的模块)。
|
||||
* @param {Blob} blob 后端返回的失败明细 Excel 流
|
||||
* @param {String} businessName 业务名称(如「货物类型」)
|
||||
* @param {Function} [failDetailDecorator] async (workbook, { businessName }) => void
|
||||
* @returns {Promise<Blob>}
|
||||
*/
|
||||
const decorateFailExcel = async (blob, businessName, failDetailDecorator) => {
|
||||
if (typeof failDetailDecorator !== 'function') return blob;
|
||||
const ExcelJS = await import('exceljs');
|
||||
const buf = await blob.arrayBuffer();
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
await workbook.xlsx.load(buf);
|
||||
await failDetailDecorator(workbook, { businessName });
|
||||
const out = await workbook.xlsx.writeBuffer();
|
||||
return new Blob([out], { type: 'application/vnd.ms-excel' });
|
||||
};
|
||||
|
||||
/**
|
||||
* 打开导入弹窗
|
||||
* @param {Object} vm 组件实例
|
||||
* @param {String} businessName 业务名称,用于失败明细文件命名
|
||||
* @param {Function} [onSuccess] 导入成功回调
|
||||
* @param {Object} [options] 扩展项
|
||||
* @param {Function} [options.failDetailDecorator] 失败明细加工函数,用于对后端返回的失败 Excel 标红/改写原因
|
||||
*/
|
||||
export const openImportDialog = (vm, businessName, onSuccess, options = {}) => {
|
||||
const { failDetailDecorator } = options;
|
||||
const column = vm.findColumn(vm.excelOption.column, 'excelFile');
|
||||
column.httpRequest = (option, uploadColumn) =>
|
||||
handleImportExcel(vm, option, uploadColumn, businessName, onSuccess);
|
||||
handleImportExcel(vm, option, uploadColumn, businessName, onSuccess, { failDetailDecorator });
|
||||
vm.excelBox = true;
|
||||
};
|
||||
|
||||
export const handleImportExcel = async (vm, option, column, businessName, onSuccess) => {
|
||||
export const handleImportExcel = async (vm, option, column, businessName, onSuccess, options = {}) => {
|
||||
const { failDetailDecorator } = options;
|
||||
const file = option.file;
|
||||
const fileName = file && file.name ? file.name.toLowerCase() : '';
|
||||
if (!excelPattern.test(fileName)) {
|
||||
@@ -37,8 +68,9 @@ export const handleImportExcel = async (vm, option, column, businessName, onSucc
|
||||
try {
|
||||
const res = await importBlob(column.action, file);
|
||||
if (isExcelResponse(res)) {
|
||||
const decorated = await decorateFailExcel(res.data, businessName, failDetailDecorator);
|
||||
downloadXls(
|
||||
res.data,
|
||||
decorated,
|
||||
`${businessName}导入失败明细${vm.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`
|
||||
);
|
||||
vm.$message.warning('部分数据导入失败,已下载失败明细');
|
||||
|
||||
@@ -1,5 +1,52 @@
|
||||
<template>
|
||||
<basic-container class="cargo-type-page">
|
||||
<!-- 紧凑搜索栏 -->
|
||||
<div class="compact-search">
|
||||
<div class="compact-search__form">
|
||||
<el-select
|
||||
v-model="query.typeLevel"
|
||||
placeholder="选择类型"
|
||||
clearable
|
||||
class="compact-search__select"
|
||||
@change="handleCompactSearch"
|
||||
>
|
||||
<el-option label="一级货物类型" :value="1" />
|
||||
<el-option label="二级货物类型" :value="2" />
|
||||
</el-select>
|
||||
<el-input
|
||||
v-model="query.keyword"
|
||||
placeholder="搜索分类名称"
|
||||
clearable
|
||||
class="compact-search__input"
|
||||
@keyup.enter="handleCompactSearch"
|
||||
@clear="handleCompactSearch"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="handleCompactSearch">
|
||||
<el-icon><Search /></el-icon>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="compact-search__actions">
|
||||
<el-button type="primary" v-if="permission.cargo_type_add" @click="$refs.crud.rowAdd()">
|
||||
<el-icon class="el-icon--left"><Plus /></el-icon>添加分类
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
v-if="permission.cargo_type_import"
|
||||
@click="handleImport"
|
||||
>批量导入</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
plain
|
||||
v-if="permission.cargo_type_delete"
|
||||
@click="handleDelete"
|
||||
>批量删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<avue-crud
|
||||
:option="option"
|
||||
:table-loading="loading"
|
||||
@@ -20,40 +67,6 @@
|
||||
@refresh-change="refreshChange"
|
||||
@on-load="onLoad"
|
||||
>
|
||||
<template #menu-left>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-upload"
|
||||
plain
|
||||
v-if="permission.cargo_type_import"
|
||||
@click="handleImport"
|
||||
>批量导入
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="permission.cargo_type_template"
|
||||
@click="handleTemplate"
|
||||
>下载模板
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
v-if="permission.cargo_type_export"
|
||||
@click="handleExport"
|
||||
>批量导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
plain
|
||||
v-if="permission.cargo_type_delete"
|
||||
@click="handleDelete"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<template #parentCargoName="{ row }">
|
||||
{{ row.typeLevel === 1 ? '/' : row.parentCargoName || '/' }}
|
||||
@@ -73,7 +86,7 @@
|
||||
clearable
|
||||
:remote-method="loadParentOptions"
|
||||
:loading="parentLoading"
|
||||
:disabled="form.typeLevel !== 2"
|
||||
:disabled="dialogType !== 'add' || form.typeLevel !== 2"
|
||||
placeholder="请输入名称或编码搜索"
|
||||
style="width: 100%"
|
||||
@change="handleParentChange"
|
||||
@@ -91,7 +104,6 @@
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-view"
|
||||
v-if="permission.cargo_type_view"
|
||||
@click="$refs.crud.rowView(row, index)"
|
||||
>
|
||||
@@ -100,7 +112,6 @@
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-edit"
|
||||
v-if="permission.cargo_type_edit"
|
||||
@click="$refs.crud.rowEdit(row, index)"
|
||||
>
|
||||
@@ -109,7 +120,6 @@
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
icon="el-icon-delete"
|
||||
v-if="permission.cargo_type_delete"
|
||||
@click="rowDel(row)"
|
||||
>
|
||||
@@ -146,6 +156,7 @@ import {
|
||||
remove,
|
||||
submit,
|
||||
} from '@/api/base/cargo-type';
|
||||
import { getCargoTypeOption } from '@/option/base/cargo-type';
|
||||
import { exportBlob } from '@/api/common';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { downloadXls } from '@/utils/util';
|
||||
@@ -153,46 +164,105 @@ import { openImportDialog } from '@/utils/import-excel';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
import { Search, Plus } from '@element-plus/icons-vue';
|
||||
|
||||
const typeLevelDic = [
|
||||
{ label: '一级货物类型', value: 1 },
|
||||
{ label: '二级货物类型', value: 2 },
|
||||
// 导入失败明细中,每个错误文本对应的「字段列」与「规则说明」
|
||||
const CARGO_TYPE_FAIL_RULES = [
|
||||
{ match: '请选择货物类型级别', col: '类型', rule: '仅允许:一级货物类型/二级货物类型' },
|
||||
{ match: '货物类型编码格式不正确', col: '货物类型编码', rule: '一级2位数字,二级4位数字' },
|
||||
{ match: '该货物类型编码已存在', col: '货物类型编码', rule: '请更换唯一编码' },
|
||||
{ match: '请选择上级货物类型', col: '上级货物类型', rule: '二级货物类型必须选择上级' },
|
||||
{
|
||||
match: '二级编码前2位必须与上级货物类型编码一致',
|
||||
col: '货物类型编码',
|
||||
rule: '二级编码前2位必须与上级货物类型编码一致',
|
||||
},
|
||||
{ match: '货物类型名称不能超过50字符', col: '货物类型', rule: '货物类型名称不能超过50字符' },
|
||||
{ match: '备注不能超过200字', col: '备注', rule: '备注不能超过200字' },
|
||||
];
|
||||
|
||||
// 失败明细各列目标宽度(Excel 字符数),key 与后端 CargoTypeImportFailureExcel 表头完全一致
|
||||
const CARGO_TYPE_FAIL_COL_WIDTH = {
|
||||
'*类型': 16,
|
||||
'上级货物类型(如为一级则不需填写)': 22,
|
||||
'上级货物类型编码(如为一级则不需填写)': 26,
|
||||
'*货物类型': 18,
|
||||
'*货物类型编码': 18,
|
||||
'备注': 24,
|
||||
'导入失败原因': 70,
|
||||
};
|
||||
|
||||
/**
|
||||
* 加工后端返回的导入失败明细 Excel:
|
||||
* - 「导入失败原因」列按 A. B. C. 编号列出,多条以分号隔开
|
||||
* - 每个错误后追加对应规则说明,仅失败原因列标红
|
||||
* - 适当加宽各列、原因列自动换行,避免内容拥挤
|
||||
*/
|
||||
export const decorateCargoTypeFailDetail = async workbook => {
|
||||
const ws = workbook.worksheets[0];
|
||||
if (!ws) return;
|
||||
const headerRow = ws.getRow(1);
|
||||
const colMap = {};
|
||||
headerRow.eachCell((cell, colNumber) => {
|
||||
const name = String(cell.value == null ? '' : cell.value).trim();
|
||||
if (name) colMap[name] = colNumber;
|
||||
});
|
||||
const reasonCol = colMap['导入失败原因'];
|
||||
if (!reasonCol) return;
|
||||
ws.eachRow((row, rowNumber) => {
|
||||
if (rowNumber === 1) return; // 跳过表头
|
||||
// 仅「导入失败原因」列允许红色,其他列红色字体统一清除
|
||||
row.eachCell((cell, colNumber) => {
|
||||
if (colNumber === reasonCol) return;
|
||||
if (cell.font && cell.font.color) {
|
||||
const font = { ...cell.font };
|
||||
delete font.color;
|
||||
cell.font = font;
|
||||
}
|
||||
});
|
||||
const reasonCell = row.getCell(reasonCol);
|
||||
let raw = String(reasonCell.value == null ? '' : reasonCell.value).trim();
|
||||
if (!raw) return;
|
||||
// 去掉后端可能已经存在的「第N行:」前缀,避免重复
|
||||
raw = raw.replace(/^第\s*\d+\s*行[::]\s*/, '');
|
||||
const parts = raw
|
||||
.split(/[;;\n\r]+/)
|
||||
.map(s => s.trim().replace(/^第\s*\d+\s*行[::]\s*/, ''))
|
||||
.filter(Boolean);
|
||||
if (parts.length === 0) return;
|
||||
const decorated = parts.map((part, index) => {
|
||||
const label = `${String.fromCharCode(65 + index)}.`; // A. B. C.
|
||||
const rule = CARGO_TYPE_FAIL_RULES.find(r => part.includes(r.match));
|
||||
if (rule) {
|
||||
// 规则说明若已包含在错误文本中则不再重复拼接
|
||||
return part.includes(rule.rule) ? `${label}${part}` : `${label}${part},${rule.rule}`;
|
||||
}
|
||||
return `${label}${part}`;
|
||||
});
|
||||
// 兜底:失败行的备注列超长(>200字)时,补充备注超长提示
|
||||
const remarkCol = colMap['备注'];
|
||||
if (remarkCol) {
|
||||
const remarkVal = String(row.getCell(remarkCol).value == null ? '' : row.getCell(remarkCol).value).trim();
|
||||
if (remarkVal.length > 200 && !decorated.some(d => d.includes('备注不能超过200字'))) {
|
||||
decorated.push(`${String.fromCharCode(65 + decorated.length)}.备注不能超过200字`);
|
||||
}
|
||||
}
|
||||
reasonCell.value = `第${rowNumber}行:${decorated.join(';')}`;
|
||||
// 仅操作 G 列:黑色字体、无背景填充,其余列保持后端原样
|
||||
reasonCell.fill = { type: 'pattern', pattern: 'none' };
|
||||
reasonCell.font = { color: { argb: 'FF000000' } };
|
||||
reasonCell.alignment = { wrapText: true, vertical: 'top' };
|
||||
});
|
||||
// 加宽各列,避免内容拥挤(仅改列宽,不改动单元格样式)
|
||||
Object.keys(colMap).forEach(name => {
|
||||
const col = ws.getColumn(colMap[name]);
|
||||
col.width = CARGO_TYPE_FAIL_COL_WIDTH[name] || 20;
|
||||
});
|
||||
};
|
||||
|
||||
export default {
|
||||
components: { Search, Plus },
|
||||
data() {
|
||||
const validateCargoName = (rule, value, callback) => {
|
||||
const cargoName = String(value || '').trim();
|
||||
if (!cargoName) {
|
||||
callback(new Error('请输入货物类型名称'));
|
||||
} else if (cargoName.length > 50) {
|
||||
callback(new Error('货物类型名称不能超过50字符'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const validateCargoCode = (rule, value, callback) => {
|
||||
const cargoCode = String(value || '').trim();
|
||||
const pattern = this.form.typeLevel === 1 ? /^\d{2}$/ : /^\d{4}$/;
|
||||
if (!pattern.test(cargoCode)) {
|
||||
callback(new Error('货物类型编码格式不正确'));
|
||||
} else if (
|
||||
this.form.typeLevel === 2 &&
|
||||
this.form.parentCargoCode &&
|
||||
!cargoCode.startsWith(this.form.parentCargoCode)
|
||||
) {
|
||||
callback(new Error('二级编码前2位必须与上级货物类型编码一致'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const validateRemark = (rule, value, callback) => {
|
||||
if (String(value || '').length > 200) {
|
||||
callback(new Error('备注不能超过200字'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
return {
|
||||
form: {},
|
||||
query: {},
|
||||
@@ -202,6 +272,7 @@ export default {
|
||||
excelForm: {},
|
||||
parentOptions: [],
|
||||
parentLoading: false,
|
||||
dialogType: 'add',
|
||||
page: {
|
||||
pageSize: 10,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
@@ -209,137 +280,7 @@ export default {
|
||||
total: 0,
|
||||
},
|
||||
selectionList: [],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 32,
|
||||
dialogWidth: 760,
|
||||
labelPosition: 'right',
|
||||
labelWidth: 'auto',
|
||||
tip: false,
|
||||
searchBtnText: '查询',
|
||||
emptyBtnText: '重置',
|
||||
saveBtnText: '提交',
|
||||
updateBtnText: '提交',
|
||||
searchShow: true,
|
||||
searchMenuSpan: 24,
|
||||
searchIcon: true,
|
||||
searchIndex: 8,
|
||||
searchMenuPosition: 'right',
|
||||
border: true,
|
||||
index: true,
|
||||
indexLabel: '序号',
|
||||
indexWidth: 70,
|
||||
viewBtn: false,
|
||||
delBtn: false,
|
||||
editBtn: false,
|
||||
selection: true,
|
||||
dialogClickModal: false,
|
||||
menuWidth: 220,
|
||||
column: [
|
||||
{
|
||||
label: '类型',
|
||||
prop: 'typeLevel',
|
||||
type: 'radio',
|
||||
dataType: 'number',
|
||||
dicData: typeLevelDic,
|
||||
search: true,
|
||||
value: 1,
|
||||
span: 24,
|
||||
minWidth: 120,
|
||||
rules: [{ required: true, message: '请选择货物类型级别', trigger: 'change' }],
|
||||
change: ({ value }) => this.handleTypeLevelChange(value),
|
||||
},
|
||||
{
|
||||
label: '上级货物类型',
|
||||
prop: 'parentId',
|
||||
formslot: true,
|
||||
hide: true,
|
||||
display: false,
|
||||
span: 24,
|
||||
rules: [{ required: true, message: '请选择上级货物类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '上级货物类型',
|
||||
prop: 'parentCargoName',
|
||||
slot: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
label: '上级货物类型编码',
|
||||
prop: 'parentCargoCode',
|
||||
slot: true,
|
||||
readonly: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 160,
|
||||
rules: [{ required: true, message: '请选择上级货物类型', trigger: 'change' }],
|
||||
},
|
||||
{
|
||||
label: '货物类型',
|
||||
prop: 'cargoName',
|
||||
minWidth: 150,
|
||||
maxlength: 50,
|
||||
showWordLimit: true,
|
||||
rules: [{ validator: validateCargoName, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '货物类型编码',
|
||||
prop: 'cargoCode',
|
||||
minWidth: 140,
|
||||
search: true,
|
||||
maxlength: 4,
|
||||
rules: [{ validator: validateCargoCode, trigger: 'blur' }],
|
||||
change: ({ value }) => this.handleCargoCodeChange(value),
|
||||
},
|
||||
{
|
||||
label: '创建人',
|
||||
prop: 'createUserName',
|
||||
slot: true,
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
prop: 'remark',
|
||||
type: 'textarea',
|
||||
minRows: 4,
|
||||
span: 24,
|
||||
minWidth: 180,
|
||||
maxlength: 200,
|
||||
showWordLimit: true,
|
||||
overHidden: true,
|
||||
rules: [{ validator: validateRemark, trigger: 'blur' }],
|
||||
},
|
||||
{
|
||||
label: '更新时间',
|
||||
prop: 'updateTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 160,
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
prop: 'createTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
addDisplay: false,
|
||||
editDisplay: false,
|
||||
display: false,
|
||||
minWidth: 160,
|
||||
},
|
||||
],
|
||||
},
|
||||
option: getCargoTypeOption(this),
|
||||
excelOption: {
|
||||
submitBtn: false,
|
||||
emptyBtn: false,
|
||||
@@ -506,7 +447,6 @@ export default {
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
@@ -524,7 +464,6 @@ export default {
|
||||
done();
|
||||
},
|
||||
error => {
|
||||
window.console.log(error);
|
||||
loading();
|
||||
}
|
||||
);
|
||||
@@ -565,6 +504,7 @@ export default {
|
||||
this.onLoad(this.page, this.query);
|
||||
},
|
||||
beforeOpen(done, type) {
|
||||
this.dialogType = type || 'add';
|
||||
if (['edit', 'view'].includes(type)) {
|
||||
getDetail(this.form.id).then(res => {
|
||||
this.form = res.data.data || {};
|
||||
@@ -575,7 +515,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
this.form = {
|
||||
typeLevel: 1,
|
||||
typeLevel: 2,
|
||||
cargoName: '',
|
||||
cargoCode: '',
|
||||
parentId: undefined,
|
||||
@@ -586,6 +526,17 @@ export default {
|
||||
this.syncColumnDisplay();
|
||||
done();
|
||||
},
|
||||
handleCompactSearch() {
|
||||
this.page.currentPage = 1;
|
||||
const params = {};
|
||||
if (this.query.typeLevel != null) {
|
||||
params.typeLevel = this.query.typeLevel;
|
||||
}
|
||||
if (this.query.keyword) {
|
||||
params.cargoName = this.query.keyword;
|
||||
}
|
||||
this.onLoad(this.page, params);
|
||||
},
|
||||
searchReset() {
|
||||
this.query = {};
|
||||
this.page.currentPage = 1;
|
||||
@@ -627,7 +578,9 @@ export default {
|
||||
});
|
||||
},
|
||||
handleImport() {
|
||||
openImportDialog(this, '货物类型');
|
||||
openImportDialog(this, '货物类型', undefined, {
|
||||
failDetailDecorator: decorateCargoTypeFailDetail,
|
||||
});
|
||||
},
|
||||
buildExportParams() {
|
||||
const params = { ...this.query };
|
||||
@@ -654,9 +607,19 @@ export default {
|
||||
},
|
||||
handleTemplate() {
|
||||
NProgress.start();
|
||||
exportBlob('/blade-system/cargo-type/export-template', {})
|
||||
// 直接下载工程内置的标准导入模板(public 目录静态资源),表头与可正常导入的模板一致,
|
||||
// 含一级(19 类)及全部二级示例,便于用户参照填写层级关系与编码规则
|
||||
const templateUrl = `${import.meta.env.BASE_URL}cargo-type-import-template.xlsx`;
|
||||
fetch(templateUrl)
|
||||
.then(res => {
|
||||
downloadXls(res.data, '货物类型导入模板.xlsx');
|
||||
if (!res.ok) throw new Error('模板文件不存在');
|
||||
return res.blob();
|
||||
})
|
||||
.then(blob => {
|
||||
downloadXls(blob, '货物类型导入模板.xlsx');
|
||||
})
|
||||
.catch(() => {
|
||||
this.$message.error('模板下载失败,请稍后重试');
|
||||
})
|
||||
.finally(() => {
|
||||
NProgress.done();
|
||||
@@ -677,15 +640,47 @@ export default {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
:deep(.avue-crud__search) {
|
||||
padding: 12px 12px 4px;
|
||||
margin-bottom: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
// 隐藏表格工具栏中的搜索图标按钮
|
||||
:deep(.avue-crud__search-btn) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:deep(.avue-crud__search .el-form-item__label) {
|
||||
min-width: 160px;
|
||||
white-space: nowrap;
|
||||
// 紧凑搜索栏
|
||||
.compact-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 8px;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
gap: 16px;
|
||||
|
||||
&__form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
gap: 12px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__select {
|
||||
width: 160px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__input {
|
||||
width: 280px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
|
||||
Reference in New Issue
Block a user