Files
tms-erp-web/src/views/base/fee-item.vue
赵忠林 7fb8113c8e feat(core): 统一全站时间列加排序并优化司机管理显示
- 所有含时间的列表列(创建时间、更新时间、起止日期等)均增加排序功能,兼顾Avue option和手写el-table-column
- 司机管理「姓名身份证号」列拆分为独立的「姓名」「身份证号」两列,分别使用插槽渲染
- 修复车辆/船舶/司机管理「所属组织」列前导空格问题,value 使用无缩进原始标签
- 调整司机管理页到期筛选标签位置,移至搜索菜单行并优化样式,删除过渡占位筛选字段
- 增加组织名称列自定义插槽,去除已入库旧数据的前导空白
- 更新相关样式,删除无用样式代码,增加表格列排序样式支持
2026-08-07 15:11:08 +08:00

516 lines
15 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<basic-container class="fee-item-page">
<avue-crud
:option="option"
:table-loading="loading"
:data="data"
v-model:page="page"
v-model="form"
ref="crud"
:permission="permissionList"
:before-open="beforeOpen"
@row-save="rowSave"
@row-update="rowUpdate"
@row-del="rowDel"
@search-change="searchChange"
@search-reset="searchReset"
@current-change="currentChange"
@size-change="sizeChange"
@refresh-change="refreshChange"
@on-load="onLoad"
>
<template #menu-left>
<el-button type="primary" plain v-if="permission.fee_item_import" @click="handleImport">
<el-icon class="el-icon--left"><Upload /></el-icon>批量导入
</el-button>
<el-button type="primary" plain v-if="permission.fee_item_template" @click="handleTemplate">
<el-icon class="el-icon--left"><Download /></el-icon>下载模板
</el-button>
<el-button type="primary" plain v-if="permission.fee_item_export" @click="handleExport">
<el-icon class="el-icon--left"><Download /></el-icon>批量导出
</el-button>
</template>
<template #status="{ row }">
<el-tag :type="row.status === 1 ? 'primary' : 'info'">
{{ row.status === 1 ? '启用' : '停用' }}
</el-tag>
</template>
<template #feeCategory-form>
<el-select
v-model="form.feeCategory"
class="fee-item-form-control"
clearable
filterable
placeholder="请选择费用类型"
@change="handleFeeCategoryChange"
>
<el-option
v-for="item in feeCategoryOptions"
:key="item.id || item.dictKey"
:label="item.dictValue"
:value="item.dictKey"
/>
</el-select>
</template>
<template #englishName-form>
<el-input
v-model="form.englishName"
:maxlength="feeItemCodeMaxlength"
class="fee-item-form-control"
clearable
placeholder="请输入费用项代码"
@change="handleFeeItemCodeChange"
>
<template #prepend>{{ feeCategoryPrefix || '费用类型' }}</template>
</el-input>
</template>
<template #menu="{ row, index }">
<el-link
type="primary"
v-if="permission.fee_item_edit"
@click="$refs.crud.rowEdit(row, index)"
>
编辑
</el-link>
<el-link type="primary" v-if="permission.fee_item_status" @click="handleStatus(row)">
{{ row.status === 1 ? '停用' : '启用' }}
</el-link>
</template>
</avue-crud>
<el-dialog title="费用项导入" append-to-body v-model="excelBox" width="555px">
<avue-form :option="excelOption" v-model="excelForm">
<template #excelTemplate>
<el-button type="primary" @click="handleTemplate">
点击下载<i class="el-icon-download el-icon--right"></i>
</el-button>
</template>
</avue-form>
</el-dialog>
</basic-container>
</template>
<script>
import { getList, getDetail, submit, remove, changeStatus } from '@/api/base/fee-item';
import { exportBlob } from '@/api/common';
import { getDictionary } from '@/api/system/dictbiz';
import { getDeptTree } from '@/api/system/dept';
import { downloadXls } from '@/utils/util';
import { openImportDialog } from '@/utils/import-excel';
import { getToken } from '@/utils/auth';
import { getUploadHeaders } from '@/utils/upload';
import { Upload, Download } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
export default {
components: { Upload, Download },
data() {
return {
form: {},
query: {},
loading: true,
data: [],
excelBox: false,
excelForm: {},
feeCategoryOptions: [],
page: {
pageSize: 10,
currentPage: 1,
total: 0,
},
option: {
height: 'auto',
calcHeight: 32,
dialogWidth: 980,
labelPosition: 'right',
labelWidth: 'auto',
tip: false,
searchShow: true,
searchMenuSpan: 24,
searchIcon: true,
searchIndex: 4,
searchMenuPosition: 'right',
border: true,
index: true,
indexLabel: '序号',
indexWidth: 70,
viewBtn: false,
delBtn: false,
editBtn: false,
selection: false,
dialogClickModal: false,
menuWidth: 180,
column: [
{
label: '费用类型',
prop: 'feeCategory',
type: 'select',
search: true,
searchOrder: 3,
filterable: true,
formslot: true,
span: 12,
dicData: [],
props: {
label: 'dictValue',
value: 'dictKey',
},
minWidth: 120,
rules: [{ required: true, message: '请选择费用类型', trigger: 'change' }],
},
{
label: '费用项代码',
prop: 'englishName',
formslot: true,
span: 12,
minWidth: 160,
maxlength: 100,
rules: [{ required: true, message: '请输入费用项代码', trigger: 'blur' }],
},
{
label: '费用项',
prop: 'name',
minWidth: 140,
search: true,
searchOrder: 4,
span: 12,
maxlength: 50,
rules: [{ required: true, message: '请输入费用项', trigger: 'blur' }],
},
{
label: '组织',
prop: 'createDeptName',
minWidth: 160,
addDisplay: false,
editDisplay: false,
viewDisplay: false,
},
{
label: '组织',
prop: 'createDept',
type: 'select',
search: true,
searchOrder: 1,
hide: true,
display: false,
filterable: true,
dicData: [],
props: {
label: 'label',
value: 'value',
},
},
{
label: '更新人',
prop: 'updateUserName',
minWidth: 120,
addDisplay: false,
editDisplay: false,
viewDisplay: false,
display: false,
},
{
label: '更新时间',
prop: 'updateTime',
sortable: true,
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',
sortable: true,
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: 'status',
type: 'select',
search: true,
searchOrder: 2,
slot: true,
dataType: 'number',
dicData: [
{ label: '启用', value: 1 },
{ label: '停用', value: 2 },
],
addDisplay: false,
editDisplay: false,
value: 1,
minWidth: 100,
},
],
},
excelOption: {
submitBtn: false,
emptyBtn: false,
column: [
{
label: '模板上传',
prop: 'excelFile',
type: 'upload',
drag: true,
loadText: '模板上传中,请稍等',
span: 24,
propsHttp: {
res: 'data',
},
headers: getUploadHeaders(),
tip: '请上传 .xls,.xlsx 标准格式文件',
action: '/blade-system/fee-item/import-fee-item',
},
{
label: '模板下载',
prop: 'excelTemplate',
formslot: true,
span: 24,
},
],
},
};
},
created() {
this.initFeeCategoryOptions();
this.initDeptTree();
},
computed: {
...mapGetters(['permission', 'userInfo']),
feeCategoryPrefix() {
return this.getFeeCategoryPrefix(this.form.feeCategory);
},
feeItemCodeMaxlength() {
return Math.max(1, 100 - this.feeCategoryPrefix.length);
},
permissionList() {
return {
addBtn: this.validData(this.permission.fee_item_add, false),
};
},
},
methods: {
initFeeCategoryOptions() {
getDictionary({ code: 'fee_category' }).then(res => {
this.feeCategoryOptions = res.data.data || [];
const column = this.findColumn(this.option.column, 'feeCategory');
column.dicData = this.feeCategoryOptions;
});
},
initDeptTree() {
getDeptTree(this.userInfo.tenantId).then(res => {
const column = this.findColumn(this.option.column, 'createDept');
column.dicData = this.flattenDept(res.data.data || []);
});
},
flattenDept(tree, level = 0) {
const result = [];
tree.forEach(item => {
result.push({
label: `${' '.repeat(level)}${item.title || item.deptName || item.name}`,
value: item.id,
});
if (item.children && item.children.length) {
result.push(...this.flattenDept(item.children, level + 1));
}
});
return result;
},
getFeeCategoryPrefix(value) {
const feeCategory = String(value || '').trim();
const option = this.feeCategoryOptions.find(
item => String(item.dictKey || '') === feeCategory
);
return String((option && option.dictKey) || feeCategory);
},
getFeeItemCodeSuffix(code, feeCategory) {
const value = String(code || '').trim();
const prefix = this.getFeeCategoryPrefix(feeCategory);
if (prefix && value.startsWith(prefix)) {
return value.slice(prefix.length);
}
return value;
},
handleFeeCategoryChange(value) {
this.form.feeCategory = value;
this.form.englishName = this.getFeeItemCodeSuffix(this.form.englishName, value);
},
handleFeeItemCodeChange(value) {
this.form.englishName = this.getFeeItemCodeSuffix(value, this.form.feeCategory);
},
normalizeRow(row) {
const values = { ...row };
const prefix = this.getFeeCategoryPrefix(values.feeCategory);
const code = this.getFeeItemCodeSuffix(values.englishName, values.feeCategory);
values.feeCategory = String(values.feeCategory || '').trim();
values.name = String(values.name || '').trim();
values.englishName = prefix ? `${prefix}${code}` : code;
if (!values.status) values.status = 1;
return values;
},
normalizeFormForEdit(row) {
const values = { ...row };
values.feeCategory = String(values.feeCategory || '').trim();
values.englishName = this.getFeeItemCodeSuffix(values.englishName, values.feeCategory);
return values;
},
rowSave(row, done, loading) {
submit(this.normalizeRow(row)).then(
() => {
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
done();
},
error => {
window.console.log(error);
loading();
}
);
},
rowUpdate(row, index, done, loading) {
submit(this.normalizeRow(row)).then(
() => {
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
done();
},
error => {
window.console.log(error);
loading();
}
);
},
rowDel(row) {
this.$confirm('确定将选择数据删除?', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => remove(row.id))
.then(() => {
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
});
},
handleStatus(row) {
const nextStatus = row.status === 1 ? 2 : 1;
const actionName = nextStatus === 1 ? '启用' : '停用';
this.$confirm(`是否确认${actionName}该条数据?`, {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => changeStatus(row.id, nextStatus))
.then(() => {
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
});
},
beforeOpen(done, type) {
if (['edit', 'view'].includes(type)) {
getDetail(this.form.id).then(res => {
this.form = this.normalizeFormForEdit(res.data.data || {});
done();
});
return;
}
this.form.status = 1;
this.form.englishName = '';
done();
},
searchReset() {
this.query = {};
this.onLoad(this.page);
},
searchChange(params, done) {
this.query = params;
this.page.currentPage = 1;
this.onLoad(this.page, params);
done();
},
currentChange(currentPage) {
this.page.currentPage = currentPage;
},
sizeChange(pageSize) {
this.page.pageSize = pageSize;
},
refreshChange() {
this.onLoad(this.page, this.query);
},
handleImport() {
openImportDialog(this, '费用项');
},
buildExportParams() {
return {
...this.query,
[this.website.tokenHeader]: getToken(),
};
},
handleExport() {
this.$confirm('是否导出费用项数据?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
NProgress.start();
exportBlob('/blade-system/fee-item/export-fee-item', this.buildExportParams(), {
feedback: true,
})
.then(res => {
downloadXls(res.data, `费用项${this.$dayjs().format('YYYY-MM-DD HH:mm:ss')}.xlsx`);
})
.finally(() => {
NProgress.done();
});
});
},
handleTemplate() {
exportBlob(
`/blade-system/fee-item/export-template?${this.website.tokenHeader}=${getToken()}`,
undefined,
{ feedback: true }
).then(res => {
downloadXls(res.data, '费用项模板.xlsx');
});
},
onLoad(page, params = {}) {
this.loading = true;
getList(page.currentPage, page.pageSize, Object.assign(params, this.query)).then(res => {
const data = res.data.data;
this.page.total = data.total;
this.data = data.records;
this.loading = false;
});
},
},
};
</script>
<style scoped>
.fee-item-page :deep(.avue-crud__dialog .avue-form__row),
.fee-item-page :deep(.avue-crud__dialog .el-row) {
margin-left: -36px;
margin-right: -36px;
}
.fee-item-page :deep(.avue-crud__dialog .el-col) {
padding-left: 36px;
padding-right: 36px;
}
.fee-item-form-control {
width: 100%;
}
</style>