Merge remote-tracking branch 'refs/remotes/origin/master' into feature/bfi-api-rate

# Conflicts:
#	pnpm-lock.yaml
#	src/axios.js
#	src/views/settlement/formal-settlement.vue
#	src/views/vehicle/customer-archive.vue
This commit is contained in:
刘泉佳
2026-10-01 13:46:44 +08:00
139 changed files with 10731 additions and 2593 deletions
+31 -11
View File
@@ -157,7 +157,7 @@ import { getDeptTree } from '@/api/system/dept';
import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
import { openImportDialog } from '@/utils/import-excel';
import { getToken } from '@/utils/auth';
import { getToken, isAdministrator } from '@/utils/auth';
import { normalizeSearchRangeParams } from '@/utils/search-range';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
import { mapGetters } from 'vuex';
@@ -204,7 +204,7 @@ export default {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
return isAdministrator(this.userInfo);
},
permissionList() {
return {
@@ -332,15 +332,34 @@ export default {
? values.vehicleNo.trim()
: values.vehicleNo.trim().toUpperCase();
}
// 非必填金额未填写时不要提交 -1 占位值
values.directEconomicLoss = this.normalizeOptionalAmount(values.directEconomicLoss);
values.insuranceClaimAmount = this.normalizeOptionalAmount(values.insuranceClaimAmount);
values.accidentLocation = this.normalizeOptionalText(values.accidentLocation);
values.accidentReasonDamage = this.normalizeOptionalText(values.accidentReasonDamage);
values.remark = this.normalizeOptionalText(values.remark);
values.attachments = this.stringifyAttachments(values.attachments);
return values;
},
normalizeOptionalAmount(value) {
if (value === undefined || value === null || value === '' || Number(value) === -1) {
return null;
}
return value;
},
normalizeOptionalText(value) {
if (value === undefined || value === null) return null;
const text = String(value).trim();
return !text || text === '-1' ? null : text;
},
validateRow(row) {
if (Number(row.directEconomicLoss) < 0) {
const directEconomicLoss = this.normalizeOptionalAmount(row.directEconomicLoss);
const insuranceClaimAmount = this.normalizeOptionalAmount(row.insuranceClaimAmount);
if (directEconomicLoss !== null && Number(directEconomicLoss) < 0) {
this.$message.warning('直接经济损失不能小于 0');
return false;
}
if (Number(row.insuranceClaimAmount) < 0) {
if (insuranceClaimAmount !== null && Number(insuranceClaimAmount) < 0) {
this.$message.warning('保险理赔金额不能小于 0');
return false;
}
@@ -353,13 +372,9 @@ export default {
return false;
}
if (
row.directEconomicLoss !== undefined &&
row.directEconomicLoss !== null &&
row.directEconomicLoss !== '' &&
row.insuranceClaimAmount !== undefined &&
row.insuranceClaimAmount !== null &&
row.insuranceClaimAmount !== '' &&
Number(row.insuranceClaimAmount) > Number(row.directEconomicLoss)
directEconomicLoss !== null &&
insuranceClaimAmount !== null &&
Number(insuranceClaimAmount) > Number(directEconomicLoss)
) {
this.$message.warning('保险理赔金额不能超过直接经济损失金额');
return false;
@@ -448,6 +463,11 @@ export default {
const detail = res.data.data;
detail.attachments = this.parseAttachments(detail.attachments);
detail.vehicleType = detail.vehicleType || '车辆';
detail.directEconomicLoss = this.normalizeOptionalAmount(detail.directEconomicLoss);
detail.insuranceClaimAmount = this.normalizeOptionalAmount(detail.insuranceClaimAmount);
detail.accidentLocation = this.normalizeOptionalText(detail.accidentLocation) || '';
detail.accidentReasonDamage = this.normalizeOptionalText(detail.accidentReasonDamage) || '';
detail.remark = this.normalizeOptionalText(detail.remark) || '';
this.form = detail;
this.updateVehicleTypeDisplays(detail.vehicleType);
this.vehicleOptions = [];
+30 -3
View File
@@ -171,7 +171,7 @@ import { getDeptTree } from '@/api/system/dept';
import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
import { openImportDialog } from '@/utils/import-excel';
import { getToken } from '@/utils/auth';
import { getToken, isAdministrator } from '@/utils/auth';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
import { mapGetters } from 'vuex';
import { excelOption, exportColumns, option } from '@/option/vehicle/annual-inspection-record';
@@ -214,7 +214,7 @@ export default {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
return isAdministrator(this.userInfo);
},
permissionList() {
return {
@@ -367,11 +367,33 @@ export default {
} else {
values.shipInspectionType = undefined;
}
// 费用必填,-1 转 null 后由校验拦截;非必填文本清除 "-1"
values.fee = this.normalizeOptionalAmount(values.fee);
values.inspectionUnit = this.normalizeOptionalText(values.inspectionUnit);
values.assessmentUnit = this.normalizeOptionalText(values.assessmentUnit);
values.passengerTypeLevel = this.normalizeOptionalText(values.passengerTypeLevel);
values.remark = this.normalizeOptionalText(values.remark);
values.attachments = this.stringifyAttachments(values.attachments);
return values;
},
normalizeOptionalAmount(value) {
if (value === undefined || value === null || value === '' || Number(value) === -1) {
return null;
}
return value;
},
normalizeOptionalText(value) {
if (value === undefined || value === null) return null;
const text = String(value).trim();
return !text || text === '-1' ? null : text;
},
validateRow(row) {
if (Number(row.fee) < 0) {
const fee = this.normalizeOptionalAmount(row.fee);
if (fee === null) {
this.$message.warning('请输入费用');
return false;
}
if (Number(fee) < 0) {
this.$message.warning('费用不能小于 0');
return false;
}
@@ -465,6 +487,11 @@ export default {
getDetail(this.form.id).then(res => {
const detail = res.data.data;
detail.attachments = this.parseAttachments(detail.attachments);
detail.fee = this.normalizeOptionalAmount(detail.fee);
detail.inspectionUnit = this.normalizeOptionalText(detail.inspectionUnit) || '';
detail.assessmentUnit = this.normalizeOptionalText(detail.assessmentUnit) || '';
detail.passengerTypeLevel = this.normalizeOptionalText(detail.passengerTypeLevel) || '';
detail.remark = this.normalizeOptionalText(detail.remark) || '';
this.form = detail;
this.updateVehicleTypeDisplays(detail.vehicleType);
this.vehicleOptions = [];
@@ -470,6 +470,7 @@ import {
} from '@/api/vehicle/credit-score-quantification';
import { normalizeSearchRangeParams } from '@/utils/search-range';
import { mapGetters } from 'vuex';
import { isAdministrator } from '@/utils/auth';
const createTimeRangeMap = {
createTimeRange: ['createTimeStart', 'createTimeEnd'],
@@ -599,7 +600,7 @@ export default {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
return isAdministrator(this.userInfo);
},
permissionList() {
return {
+139 -53
View File
@@ -161,9 +161,21 @@
>
复评
</el-link>
<el-link
type="primary"
v-if="
mkEnabled &&
hasPermission('customer_archive_approve') &&
row.approvalStatus === 'reviewing'
"
@click="handleMkAudit(row)"
>
审核
</el-link>
<el-link
type="success"
v-if="
!mkEnabled &&
hasPermission('customer_archive_approve') &&
row.approvalStatus === 'reviewing' &&
row.reviewStatus === '已完成'
@@ -174,7 +186,11 @@
</el-link>
<el-link
type="warning"
v-if="hasPermission('customer_archive_reject') && row.approvalStatus === 'reviewing'"
v-if="
!mkEnabled &&
hasPermission('customer_archive_reject') &&
row.approvalStatus === 'reviewing'
"
@click="handleReject(row)"
>
审核不通过
@@ -404,7 +420,7 @@
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="所在地区" prop="registeredDetailAddress" required>
<el-form-item label="所在地区" prop="registeredRegionName" required>
<div class="archive-form__address">
<el-cascader
ref="registeredRegionCascader"
@@ -1544,12 +1560,13 @@ import {
getDetail as getCreditScoreQuantificationDetail,
} from '@/api/vehicle/credit-score-quantification';
import { getDictionary } from '@/api/system/dictbiz';
import { submitMkApprovalFlow } from '@/utils/mk-approval';
import { getMKApprovalUrl } from '@/api/system/business-process';
import { loadMkEnabled, submitMkApprovalFlow } from '@/utils/mk-approval';
import { getDeptTree } from '@/api/system/dept';
import { getLazyTree } from '@/api/base/region';
import { exportBlob } from '@/api/common';
import { downloadFileByUrl, downloadXls } from '@/utils/util';
import { getToken } from '@/utils/auth';
import { getToken, isAdministrator } from '@/utils/auth';
import { getUploadHeaders } from '@/utils/upload';
import { normalizeSearchRangeParams } from '@/utils/search-range';
import { ArrowRight } from '@element-plus/icons-vue';
@@ -1565,6 +1582,10 @@ import SectionCard from '@/components/section-card/main.vue';
import PdfPreview from '@/components/pdf-preview/main.vue';
import ChangeRecordDetailDialog from '@/components/change-record-detail-dialog/main.vue';
const customerArchiveListRoute = '/vehicle/customer-archive';
const customerArchiveFormRoute = '/vehicle/customer-archive/form';
const customerArchiveListRefreshKey = 'customer-archive-list-refresh';
const createTimeRangeMap = {
createTimeRange: ['createTimeStart', 'createTimeEnd'],
};
@@ -1631,19 +1652,6 @@ export default {
}
callback();
};
const validateRegisteredAddress = (rule, value, callback) => {
const regionName = (this.archiveForm.registeredRegionName || '').trim();
const detailAddress = String(value || '').trim();
if (!regionName) {
callback(new Error('请选择省市区'));
} else if (!detailAddress) {
callback(new Error('请输入详细地址'));
} else if (detailAddress.length > 255) {
callback(new Error('详细地址最多255个字符'));
} else {
callback();
}
};
return {
form: {},
query: {},
@@ -1665,6 +1673,8 @@ export default {
readonly: false,
reviewMode: false,
listActivated: false,
// 表单页实例标记:创建时按路由落定,避免 keep-alive 缓存后随全局 $route 翻转
isFormPageInstance: false,
originalAccessType: '',
activeTab: 'scores',
archiveForm: this.emptyArchive(),
@@ -1775,6 +1785,8 @@ export default {
approved: { label: '审核通过', type: 'success' },
rejected: { label: '审核不通过', type: 'danger' },
},
/** 业务字典 mk_template「开启MK」=1 */
mkEnabled: false,
formRules: {
customerCode: [{ max: 20, message: '客商编号最多20个字符', trigger: 'blur' }],
fullName: [{ required: true, message: '请输入客商名称', trigger: 'blur' }],
@@ -1797,8 +1809,10 @@ export default {
registeredCapital: [{ validator: validateNonNegative, trigger: 'blur' }],
maxCreditLimit: [{ validator: validateNonNegative, trigger: 'blur' }],
applyCreditLimit: [{ validator: validateNonNegative, trigger: 'blur' }],
registeredRegionName: [{ required: true, message: '请选择省市区', trigger: 'change' }],
registeredDetailAddress: [
{ validator: validateRegisteredAddress, trigger: ['change', 'blur'] },
{ required: true, message: '请输入详细地址', trigger: 'blur' },
{ max: 255, message: '详细地址最多255个字符', trigger: 'blur' },
],
businessScope: [
{
@@ -2041,6 +2055,24 @@ export default {
],
clearable: true,
},
{
label: '创建人',
prop: 'createUserName',
addDisplay: false,
editDisplay: false,
minWidth: 120,
},
{
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,
minWidth: 170,
},
],
},
};
@@ -2057,9 +2089,11 @@ export default {
this.openArchive({ id }, true);
return;
}
this.isFormPageInstance = this.$route.path === customerArchiveFormRoute;
this.initDeptTree();
this.initRegionOptions();
this.initBusinessDictionaries();
this.initMkEnabled();
this.initScoreQuantificationOptions();
if (this.isArchivePage) {
this.reviewMode = this.$route.query.review === '1';
@@ -2069,7 +2103,13 @@ export default {
}
},
activated() {
if (this.isArchivePage || this.isPublicViewPage) return;
if (this.isFormPageInstance || this.isPublicViewPage) return;
if (sessionStorage.getItem(customerArchiveListRefreshKey)) {
sessionStorage.removeItem(customerArchiveListRefreshKey);
this.onLoad(this.page, this.query);
this.listActivated = true;
return;
}
if (!this.listActivated) {
this.listActivated = true;
return;
@@ -2080,7 +2120,7 @@ export default {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
return isAdministrator(this.userInfo);
},
kingdeeLocked() {
return this.archiveForm.kingdeeStatus === 'exist';
@@ -2091,9 +2131,7 @@ export default {
};
},
isArchivePage() {
return (
this.$route.path === '/vehicle/customer-archive/form' || this.isPublicViewPage
);
return this.isFormPageInstance || this.isPublicViewPage;
},
isPublicViewPage() {
return (
@@ -2282,6 +2320,16 @@ export default {
},
},
watch: {
$route(to, from) {
if (this.isFormPageInstance || this.isPublicViewPage) return;
if (
to.path === customerArchiveListRoute &&
from?.path?.includes('/customer-archive/form')
) {
sessionStorage.removeItem(customerArchiveListRefreshKey);
this.onLoad(this.page, this.query);
}
},
'archiveForm.unifiedCreditCode'(val) {
this.handleKingdeeCreditCodeChange(val);
},
@@ -2293,6 +2341,9 @@ export default {
},
},
methods: {
markCustomerArchiveListNeedRefresh() {
sessionStorage.setItem(customerArchiveListRefreshKey, '1');
},
hasPermission(code) {
return this.isAdmin || this.validData(this.permission[code], false);
},
@@ -3017,12 +3068,12 @@ export default {
handleRegisteredRegionChange(value) {
if (!value || !value.length) {
this.archiveForm.registeredRegionName = '';
this.$refs.archiveForm?.validateField('registeredDetailAddress');
this.$refs.archiveForm?.validateField('registeredRegionName');
return;
}
this.$nextTick(() => {
this.archiveForm.registeredRegionName = this.getRegisteredRegionLabels(value).join('');
this.$refs.archiveForm?.validateField('registeredDetailAddress');
this.$refs.archiveForm?.validateField('registeredRegionName');
});
},
handleInvoiceRegisteredRegionChange(value) {
@@ -3053,6 +3104,15 @@ export default {
this.sortQualificationFiles();
});
},
initMkEnabled() {
loadMkEnabled()
.then(enabled => {
this.mkEnabled = !!enabled;
})
.catch(() => {
this.mkEnabled = false;
});
},
initScoreQuantificationOptions() {
this.scoreQuantificationLoading = true;
return getCreditScoreQuantificationList(1, 9999, { status: 1 })
@@ -4152,27 +4212,28 @@ export default {
openArchivePage(row) {
const isEdit = Boolean(row?.id);
this.$router.push({
path: '/vehicle/customer-archive/form',
path: customerArchiveFormRoute,
query: isEdit ? { id: row.id, name: '编辑客商档案' } : { name: '新增客商档案' },
});
},
viewArchive(row) {
this.$router.push({
path: '/vehicle/customer-archive/form',
path: customerArchiveFormRoute,
query: { id: row.id, name: '查看客商档案', view: '1' },
});
},
openReview(row) {
this.$router.push({
path: '/vehicle/customer-archive/form',
path: customerArchiveFormRoute,
query: { id: row.id, name: '客商复评', review: '1' },
});
},
closeArchive() {
if (this.isPublicViewPage) return;
this.archiveBox = false;
this.markCustomerArchiveListNeedRefresh();
this.$router.$avueRouter?.closeTag?.();
this.$router.push('/vehicle/customer-archive');
this.$router.push(customerArchiveListRoute);
},
openArchive(row, readonly = false) {
this.readonly = readonly;
@@ -4370,30 +4431,37 @@ export default {
// 仅提交时落变更记录;保存不记录
archive.recordChange = true;
if (!this.validateScoreForApproval(archive)) return;
submit(archive).then(res => {
const data = res.data.data;
const id = (data && typeof data === 'object' ? data.id : data) || this.archiveForm.id;
if (!id) {
this.closeArchive();
this.onLoad(this.page);
this.$message({ type: 'success', message: '保存成功,请在列表提交审批' });
return;
}
const fullName =
(data && typeof data === 'object' ? data.fullName : '') ||
archive.fullName ||
this.archiveForm.fullName ||
'';
this.submitCustomerApproval(
id,
fullName,
archive.approvalStatus || this.archiveForm.approvalStatus
).then(() => {
this.closeArchive();
this.onLoad(this.page);
this.$message({ type: 'success', message: '提交成功!' });
});
const loading = ElLoading.service({
lock: true,
text: '提交中',
background: 'rgba(255, 255, 255, 0.7)',
});
submit(archive)
.then(res => {
const data = res.data.data;
const id = (data && typeof data === 'object' ? data.id : data) || this.archiveForm.id;
if (!id) {
this.closeArchive();
this.onLoad(this.page);
this.$message({ type: 'success', message: '保存成功,请在列表提交审批' });
return;
}
const fullName =
(data && typeof data === 'object' ? data.fullName : '') ||
archive.fullName ||
this.archiveForm.fullName ||
'';
return this.submitCustomerApproval(
id,
fullName,
archive.approvalStatus || this.archiveForm.approvalStatus
).then(() => {
this.closeArchive();
this.onLoad(this.page);
this.$message({ type: 'success', message: '提交成功!' });
});
})
.finally(() => loading.close());
});
},
/**
@@ -4402,7 +4470,7 @@ export default {
submitCustomerApproval(id, fullName = '', approvalStatus = '') {
return submitMkApprovalFlow({
bizType: 'customer-archive',
formInstanceId: id,
bizId: id,
subjectName: fullName,
approvalStatus,
}).then(() => submitApproval(id));
@@ -5092,6 +5160,24 @@ export default {
this.$message({ type: 'success', message: '已撤回至草稿状态!' });
});
},
handleMkAudit(row) {
if (!row?.id) {
this.$message.warning('客商ID为空,无法打开审核页');
return;
}
getMKApprovalUrl({ bizId: String(row.id) })
.then(res => {
const url = res?.data?.data;
if (!url) {
this.$message.error('获取审核跳转地址失败');
return;
}
window.open(url, '_blank');
})
.catch(() => {
this.$message.error('获取审核跳转地址失败');
});
},
handleApprove(row) {
this.$confirm('是否确认审核通过?', '提示', {
confirmButtonText: '确定',
+2 -2
View File
@@ -152,7 +152,7 @@ import { getList as getShipList } from '@/api/transportCapacity/transport-ship';
import { exportBlob } from '@/api/common';
import { getDeptTree } from '@/api/system/dept';
import { getDictionary } from '@/api/system/dictbiz';
import { getToken } from '@/utils/auth';
import { getToken, isAdministrator } from '@/utils/auth';
import { openImportDialog } from '@/utils/import-excel';
import { downloadXls } from '@/utils/util';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
@@ -186,7 +186,7 @@ export default {
computed: {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
return (this.userInfo.authority || '').includes('admin');
return isAdministrator(this.userInfo);
},
permissionList() {
return {
+2 -2
View File
@@ -139,7 +139,7 @@ import { getDeptTree } from '@/api/system/dept';
import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
import { openImportDialog } from '@/utils/import-excel';
import { getToken } from '@/utils/auth';
import { getToken, isAdministrator } from '@/utils/auth';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
import { mapGetters } from 'vuex';
import { excelOption, exportColumns, option } from '@/option/vehicle/etc-record';
@@ -180,7 +180,7 @@ export default {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
return isAdministrator(this.userInfo);
},
permissionList() {
return {
+44 -6
View File
@@ -218,7 +218,7 @@ import { getDeptTree } from '@/api/system/dept';
import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
import { handleImportExcel } from '@/utils/import-excel';
import { getToken } from '@/utils/auth';
import { getToken, isAdministrator } from '@/utils/auth';
import { getUploadHeaders } from '@/utils/upload';
import { normalizeSearchRangeParams } from '@/utils/search-range';
import { mapGetters } from 'vuex';
@@ -265,7 +265,7 @@ export default {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
return isAdministrator(this.userInfo);
},
permissionList() {
return {
@@ -381,8 +381,26 @@ export default {
if (values.policyNo) {
values.policyNo = values.policyNo.trim();
}
// 非必填项未填写时不要提交 -1 占位值
values.insuredAmount = this.normalizeOptionalAmount(values.insuredAmount);
values.premium = this.normalizeOptionalAmount(values.premium);
values.invoiceNo = this.normalizeOptionalText(values.invoiceNo);
values.invoiceDate = this.normalizeOptionalText(values.invoiceDate);
values.remark = this.normalizeOptionalText(values.remark);
values.policyFile = this.normalizeOptionalText(values.policyFile);
return values;
},
normalizeOptionalAmount(value) {
if (value === undefined || value === null || value === '' || Number(value) === -1) {
return null;
}
return value;
},
normalizeOptionalText(value) {
if (value === undefined || value === null) return null;
const text = String(value).trim();
return !text || text === '-1' ? null : text;
},
normalizeDecimalInput(prop, value) {
const sanitized = String(value ?? '').replace(/[^\d.]/g, '');
const [integerPart, ...decimalParts] = sanitized.split('.');
@@ -398,8 +416,18 @@ export default {
this.$message.warning('结束日期不能早于开始日期');
return false;
}
if (Number(row.insuredAmount) < 0 || Number(row.premium) < 0) {
this.$message.warning('保额、保费不能小于 0');
const insuredAmount = this.normalizeOptionalAmount(row.insuredAmount);
const premium = this.normalizeOptionalAmount(row.premium);
if (insuredAmount !== null && Number(insuredAmount) < 0) {
this.$message.warning('保额不能小于 0');
return false;
}
if (premium === null) {
this.$message.warning('请输入保费');
return false;
}
if (Number(premium) < 0) {
this.$message.warning('保费不能小于 0');
return false;
}
if (row.remark && row.remark.length > 200) {
@@ -479,15 +507,25 @@ export default {
this.vehicleOptions = [];
this.loadVehicleOptions('');
this.form.vehicleType = '车辆';
this.form.insuredAmount = '';
this.form.invoiceNo = '';
this.form.invoiceDate = '';
this.form.remark = '';
this.initOcrTemplateOptions('车辆', true);
}
if (['edit', 'view'].includes(type)) {
this.isDetailLoading = true;
getDetail(this.form.id)
.then(res => {
const detail = res.data.data || {};
this.form = {
...res.data.data,
vehicleType: res.data.data.vehicleType || '车辆',
...detail,
vehicleType: detail.vehicleType || '车辆',
insuredAmount: this.normalizeOptionalAmount(detail.insuredAmount),
premium: this.normalizeOptionalAmount(detail.premium),
invoiceNo: this.normalizeOptionalText(detail.invoiceNo) || '',
invoiceDate: this.normalizeOptionalText(detail.invoiceDate) || '',
remark: this.normalizeOptionalText(detail.remark) || '',
};
this.vehicleOptions = [];
this.loadVehicleOptions(this.form.vehicleNo || '');
+49 -13
View File
@@ -194,7 +194,7 @@ import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
import { openImportDialog } from '@/utils/import-excel';
import { formatUpdateUserName } from '@/utils/audit';
import { getToken } from '@/utils/auth';
import { getToken, isAdministrator } from '@/utils/auth';
import { normalizeSearchRangeParams } from '@/utils/search-range';
import AddressMapPicker from '@/components/address-map-picker/main.vue';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
@@ -220,6 +220,8 @@ const exportColumns = [
{ prop: 'nextMaintenanceTime', label: '下次保养时间' },
{ prop: 'nextMaintenanceMileage', label: '下次保养里程/航程' },
{ prop: 'remark', label: '备注' },
{ prop: 'createUserName', label: '创建人' },
{ prop: 'createTime', label: '创建时间' },
];
export default {
@@ -436,6 +438,14 @@ export default {
slot: true,
formslot: true,
},
{
label: '创建人',
prop: 'createUserName',
addDisplay: false,
editDisplay: false,
display: false,
minWidth: 120,
},
{
label: '创建时间',
prop: 'createTime',
@@ -537,7 +547,7 @@ export default {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
return isAdministrator(this.userInfo);
},
permissionList() {
return {
@@ -620,11 +630,11 @@ export default {
});
},
formatMileage(value, unit) {
if (value === undefined || value === null || value === '') return '';
if (value === undefined || value === null || value === '' || Number(value) === -1) return '';
return `${Math.max(Number(value), 0)} ${unit || '公里'}`;
},
isUnsetNextMaintenanceMileage(value) {
return Number(value) === -1;
return value === undefined || value === null || value === '' || Number(value) === -1;
},
formatNextMaintenanceMileage(value, unit) {
if (this.isUnsetNextMaintenanceMileage(value)) return '';
@@ -647,7 +657,7 @@ export default {
}
},
isEmpty(value) {
return value === undefined || value === null || value === '';
return value === undefined || value === null || value === '' || Number(value) === -1;
},
toNumber(value) {
return this.isEmpty(value) ? null : Number(value);
@@ -686,12 +696,34 @@ export default {
const values = { ...row };
values.vehicleType = values.vehicleType || '车辆';
values.mileageUnit = values.vehicleType === '船舶' ? '海里' : '公里';
// 非必填里程未填写时不要提交 -1 占位值
values.mileage = this.normalizeOptionalAmount(values.mileage);
values.nextMaintenanceMileage = this.normalizeOptionalAmount(values.nextMaintenanceMileage);
values.maintainer = this.normalizeOptionalText(values.maintainer);
values.maintenanceItem = this.normalizeOptionalText(values.maintenanceItem);
values.storeName = this.normalizeOptionalText(values.storeName);
values.contactPhone = this.normalizeOptionalText(values.contactPhone);
values.address = this.normalizeOptionalText(values.address);
values.remark = this.normalizeOptionalText(values.remark);
values.attachments = this.stringifyAttachments(values.attachments);
return values;
},
normalizeOptionalAmount(value) {
if (value === undefined || value === null || value === '' || Number(value) === -1) {
return null;
}
return value;
},
normalizeOptionalText(value) {
if (value === undefined || value === null) return null;
const text = String(value).trim();
return !text || text === '-1' ? null : text;
},
validateRow(row) {
const mileage = this.toNumber(row.mileage);
const nextMaintenanceMileage = this.toNumber(row.nextMaintenanceMileage);
const mileage = this.toNumber(this.normalizeOptionalAmount(row.mileage));
const nextMaintenanceMileage = this.toNumber(
this.normalizeOptionalAmount(row.nextMaintenanceMileage)
);
if (
mileage !== null &&
nextMaintenanceMileage !== null &&
@@ -782,12 +814,16 @@ export default {
detail.attachments = this.parseAttachments(detail.attachments);
detail.vehicleType = detail.vehicleType || '车辆';
detail.mileageUnit = detail.vehicleType === '船舶' ? '海里' : '公里';
if (type === 'edit') {
if (Number(detail.mileage) === -1) detail.mileage = '';
if (Number(detail.nextMaintenanceMileage) === -1) {
detail.nextMaintenanceMileage = '';
}
}
detail.mileage = this.normalizeOptionalAmount(detail.mileage);
detail.nextMaintenanceMileage = this.normalizeOptionalAmount(
detail.nextMaintenanceMileage
);
detail.maintainer = this.normalizeOptionalText(detail.maintainer) || '';
detail.maintenanceItem = this.normalizeOptionalText(detail.maintenanceItem) || '';
detail.storeName = this.normalizeOptionalText(detail.storeName) || '';
detail.contactPhone = this.normalizeOptionalText(detail.contactPhone) || '';
detail.address = this.normalizeOptionalText(detail.address) || '';
detail.remark = this.normalizeOptionalText(detail.remark) || '';
this.form = detail;
this.updateVehicleType(detail.vehicleType);
this.vehicleOptions = [];
+12 -2
View File
@@ -167,7 +167,7 @@ import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
import { openImportDialog } from '@/utils/import-excel';
import { formatUpdateUserName } from '@/utils/audit';
import { getToken } from '@/utils/auth';
import { getToken, isAdministrator } from '@/utils/auth';
import { normalizeSearchRangeParams } from '@/utils/search-range';
import AddressMapPicker from '@/components/address-map-picker/main.vue';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
@@ -193,6 +193,8 @@ const exportColumns = [
{ prop: 'factoryTime', label: '出厂时间' },
{ prop: 'mileage', label: '里程/航程数' },
{ prop: 'remark', label: '备注' },
{ prop: 'createUserName', label: '创建人' },
{ prop: 'createTime', label: '创建时间' },
];
export default {
@@ -409,6 +411,14 @@ export default {
slot: true,
formslot: true,
},
{
label: '创建人',
prop: 'createUserName',
addDisplay: false,
editDisplay: false,
display: false,
minWidth: 120,
},
{
label: '创建时间',
prop: 'createTime',
@@ -510,7 +520,7 @@ export default {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
return isAdministrator(this.userInfo);
},
permissionList() {
return {
+23 -3
View File
@@ -209,7 +209,7 @@ import { getDeptTree } from '@/api/system/dept';
import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
import { openImportDialog } from '@/utils/import-excel';
import { getToken } from '@/utils/auth';
import { getToken, isAdministrator } from '@/utils/auth';
import { normalizeSearchRangeParams } from '@/utils/search-range';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
import { mapGetters } from 'vuex';
@@ -255,7 +255,7 @@ export default {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
return isAdministrator(this.userInfo);
},
permissionList() {
return {
@@ -345,7 +345,7 @@ export default {
return JSON.stringify(value);
},
isEmpty(value) {
return value === undefined || value === null || value === '';
return value === undefined || value === null || value === '' || Number(value) === -1;
},
toNumber(value) {
return this.isEmpty(value) ? null : Number(value);
@@ -356,6 +356,15 @@ export default {
normalizeIntegerInput(prop, value) {
this.form[prop] = String(value ?? '').replace(/\D/g, '');
},
normalizeOptionalAmount(value) {
if (this.isEmpty(value)) return null;
return value;
},
normalizeOptionalText(value) {
if (value === undefined || value === null) return null;
const text = String(value).trim();
return !text || text === '-1' ? null : text;
},
normalizeRow(row) {
const values = { ...row };
values.vehicleType = '车辆';
@@ -363,6 +372,12 @@ export default {
if (values.vehicleNo) {
values.vehicleNo = values.vehicleNo.trim().toUpperCase();
}
// 非必填里程未填写时不要提交 -1 占位值
values.previousMonthMileage = this.normalizeOptionalAmount(values.previousMonthMileage);
values.currentMonthMileage = this.normalizeOptionalAmount(values.currentMonthMileage);
values.monthlyMileage = this.normalizeOptionalAmount(values.monthlyMileage);
values.totalMileage = this.normalizeOptionalAmount(values.totalMileage);
values.remark = this.normalizeOptionalText(values.remark);
const previousMonthMileage = this.toNumber(values.previousMonthMileage);
const currentMonthMileage = this.toNumber(values.currentMonthMileage);
if (
@@ -503,6 +518,11 @@ export default {
detail.attachments = this.parseAttachments(detail.attachments);
detail.vehicleType = '车辆';
detail.mileageUnit = '公里';
detail.previousMonthMileage = this.normalizeOptionalAmount(detail.previousMonthMileage);
detail.currentMonthMileage = this.normalizeOptionalAmount(detail.currentMonthMileage);
detail.monthlyMileage = this.normalizeOptionalAmount(detail.monthlyMileage);
detail.totalMileage = this.normalizeOptionalAmount(detail.totalMileage);
detail.remark = this.normalizeOptionalText(detail.remark) || '';
this.form = detail;
this.vehicleOptions = [];
this.loadVehicleOptions(this.form.vehicleNo || '');
+31 -3
View File
@@ -171,7 +171,7 @@ import { getDeptTree } from '@/api/system/dept';
import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
import { openImportDialog } from '@/utils/import-excel';
import { getToken } from '@/utils/auth';
import { getToken, isAdministrator } from '@/utils/auth';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
import { mapGetters } from 'vuex';
import { excelOption, exportColumns, option } from '@/option/vehicle/oil-electric-record';
@@ -213,7 +213,7 @@ export default {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
return isAdministrator(this.userInfo);
},
permissionList() {
return {
@@ -337,7 +337,7 @@ export default {
return JSON.stringify(value);
},
isEmpty(value) {
return value === undefined || value === null || value === '';
return value === undefined || value === null || value === '' || Number(value) === -1;
},
normalizeDecimalInput(prop, value) {
const sanitized = String(value ?? '').replace(/[^\d.]/g, '');
@@ -346,6 +346,15 @@ export default {
? `${integerPart || '0'}.${decimalParts.join('').slice(0, 2)}`
: integerPart;
},
normalizeOptionalAmount(value) {
if (this.isEmpty(value)) return null;
return value;
},
normalizeOptionalText(value) {
if (value === undefined || value === null) return null;
const text = String(value).trim();
return !text || text === '-1' ? null : text;
},
normalizeRow(row) {
const values = { ...row };
values.vehicleType = values.vehicleType || '车辆';
@@ -359,6 +368,16 @@ export default {
if (values.feeType === '充电') {
values.oilProduct = undefined;
}
// 非必填数值未填写时不要提交 -1;交易金额必填,-1 转 null 后由校验拦截
values.quantity = this.normalizeOptionalAmount(values.quantity);
values.unitPrice = this.normalizeOptionalAmount(values.unitPrice);
values.transactionAmount = this.normalizeOptionalAmount(values.transactionAmount);
values.balance = this.normalizeOptionalAmount(values.balance);
values.cardNo = this.normalizeOptionalText(values.cardNo);
values.cardHolder = this.normalizeOptionalText(values.cardHolder);
values.station = this.normalizeOptionalText(values.station);
values.oilProduct = this.normalizeOptionalText(values.oilProduct);
values.remark = this.normalizeOptionalText(values.remark);
values.attachments = this.stringifyAttachments(values.attachments);
return values;
},
@@ -472,6 +491,15 @@ export default {
const detail = res.data.data;
detail.attachments = this.parseAttachments(detail.attachments);
detail.vehicleType = detail.vehicleType || '车辆';
detail.quantity = this.normalizeOptionalAmount(detail.quantity);
detail.unitPrice = this.normalizeOptionalAmount(detail.unitPrice);
detail.transactionAmount = this.normalizeOptionalAmount(detail.transactionAmount);
detail.balance = this.normalizeOptionalAmount(detail.balance);
detail.cardNo = this.normalizeOptionalText(detail.cardNo) || '';
detail.cardHolder = this.normalizeOptionalText(detail.cardHolder) || '';
detail.station = this.normalizeOptionalText(detail.station) || '';
detail.oilProduct = this.normalizeOptionalText(detail.oilProduct) || '';
detail.remark = this.normalizeOptionalText(detail.remark) || '';
this.form = detail;
this.updateVehicleType(detail.vehicleType);
this.vehicleOptions = [];
+2 -2
View File
@@ -141,7 +141,7 @@ import { getDictionary } from '@/api/system/dictbiz';
import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
import { openImportDialog } from '@/utils/import-excel';
import { getToken } from '@/utils/auth';
import { getToken, isAdministrator } from '@/utils/auth';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
import { mapGetters } from 'vuex';
import { excelOption, exportColumns, option } from '@/option/vehicle/other-expense-record';
@@ -184,7 +184,7 @@ export default {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
return isAdministrator(this.userInfo);
},
permissionList() {
return {
+39 -10
View File
@@ -154,7 +154,7 @@ import { getDeptTree } from '@/api/system/dept';
import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
import { openImportDialog } from '@/utils/import-excel';
import { getToken } from '@/utils/auth';
import { getToken, isAdministrator } from '@/utils/auth';
import { normalizeSearchRangeParams } from '@/utils/search-range';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
import { mapGetters } from 'vuex';
@@ -200,7 +200,7 @@ export default {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
return isAdministrator(this.userInfo);
},
permissionList() {
return {
@@ -298,19 +298,41 @@ export default {
if (values.vehicleNo) {
values.vehicleNo = values.vehicleNo.trim().toUpperCase();
}
// 非必填数量未填写时不要提交 -1;费用必填,-1 转 null 后由校验拦截
values.tireQuantity = this.normalizeOptionalAmount(values.tireQuantity);
values.replacementCost = this.normalizeOptionalAmount(values.replacementCost);
values.handler = this.normalizeOptionalText(values.handler);
values.tireBrand = this.normalizeOptionalText(values.tireBrand);
values.replacementDescription = this.normalizeOptionalText(values.replacementDescription);
values.remark = this.normalizeOptionalText(values.remark);
values.attachments = this.stringifyAttachments(values.attachments);
return values;
},
normalizeOptionalAmount(value) {
if (value === undefined || value === null || value === '' || Number(value) === -1) {
return null;
}
return value;
},
normalizeOptionalText(value) {
if (value === undefined || value === null) return null;
const text = String(value).trim();
return !text || text === '-1' ? null : text;
},
validateRow(row) {
if (Number(row.replacementCost) < 0) {
const replacementCost = this.normalizeOptionalAmount(row.replacementCost);
const tireQuantity = this.normalizeOptionalAmount(row.tireQuantity);
if (replacementCost === null) {
this.$message.warning('请输入换胎费用');
return false;
}
if (Number(replacementCost) < 0) {
this.$message.warning('换胎费用不能小于 0');
return false;
}
if (
row.tireQuantity !== undefined &&
row.tireQuantity !== null &&
row.tireQuantity !== '' &&
(!Number.isInteger(Number(row.tireQuantity)) || Number(row.tireQuantity) <= 0)
tireQuantity !== null &&
(!Number.isInteger(Number(tireQuantity)) || Number(tireQuantity) <= 0)
) {
this.$message.warning('换胎数量必须为正整数');
return false;
@@ -391,9 +413,16 @@ export default {
}
if (['edit', 'view'].includes(type)) {
getDetail(this.form.id).then(res => {
const detail = res.data.data;
detail.attachments = this.parseAttachments(detail.attachments);
this.form = detail;
const detail = res.data.data;
detail.attachments = this.parseAttachments(detail.attachments);
detail.tireQuantity = this.normalizeOptionalAmount(detail.tireQuantity);
detail.replacementCost = this.normalizeOptionalAmount(detail.replacementCost);
detail.handler = this.normalizeOptionalText(detail.handler) || '';
detail.tireBrand = this.normalizeOptionalText(detail.tireBrand) || '';
detail.replacementDescription =
this.normalizeOptionalText(detail.replacementDescription) || '';
detail.remark = this.normalizeOptionalText(detail.remark) || '';
this.form = detail;
this.vehicleOptions = [];
this.loadVehicleOptions(this.form.vehicleNo || '');
});
+14 -7
View File
@@ -129,7 +129,7 @@ import { getDeptTree } from '@/api/system/dept';
import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
import { openImportDialog } from '@/utils/import-excel';
import { getToken } from '@/utils/auth';
import { getToken, isAdministrator } from '@/utils/auth';
import { normalizeSearchRangeParams } from '@/utils/search-range';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
import { mapGetters } from 'vuex';
@@ -176,7 +176,7 @@ export default {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
return isAdministrator(this.userInfo);
},
permissionList() {
return {
@@ -289,15 +289,20 @@ export default {
normalizeRow(row) {
const values = { ...row };
values.vehicleType = values.vehicleType || '车辆';
if (values.vehicleNo) {
values.vehicleNo =
values.vehicleType === '船舶'
? values.vehicleNo.trim()
: values.vehicleNo.trim().toUpperCase();
// 非必填文本未填写时不要提交 "-1" 占位值
values.vehicleNo = this.normalizeOptionalText(values.vehicleNo);
if (values.vehicleNo && values.vehicleType !== '船舶') {
values.vehicleNo = values.vehicleNo.toUpperCase();
}
values.remark = this.normalizeOptionalText(values.remark);
values.attachments = this.stringifyAttachments(values.attachments);
return values;
},
normalizeOptionalText(value) {
if (value === undefined || value === null) return null;
const text = String(value).trim();
return !text || text === '-1' ? null : text;
},
validateRow(row) {
if (row.changeContent && row.changeContent.length > 200) {
this.$message.warning('变更内容不能超过 200 字');
@@ -389,6 +394,8 @@ export default {
const detail = res.data.data;
detail.attachments = this.parseAttachments(detail.attachments);
detail.vehicleType = detail.vehicleType || '车辆';
detail.vehicleNo = this.normalizeOptionalText(detail.vehicleNo) || '';
detail.remark = this.normalizeOptionalText(detail.remark) || '';
this.form = detail;
this.updateVehicleType(detail.vehicleType);
this.vehicleOptions = [];
+29 -9
View File
@@ -196,7 +196,7 @@ import { getDictionary } from '@/api/system/dictbiz';
import { exportBlob } from '@/api/common';
import { downloadXls } from '@/utils/util';
import { openImportDialog } from '@/utils/import-excel';
import { getToken } from '@/utils/auth';
import { getToken, isAdministrator } from '@/utils/auth';
import { normalizeSearchRangeParams } from '@/utils/search-range';
import AddressMapPicker from '@/components/address-map-picker/main.vue';
import VehicleAttachmentTable from '@/components/vehicle-attachment-table/main.vue';
@@ -247,7 +247,7 @@ export default {
...mapGetters(['permission', 'userInfo']),
isAdmin() {
const authority = this.userInfo.authority || '';
return authority.includes('admin');
return isAdministrator(this.userInfo);
},
permissionList() {
return {
@@ -448,9 +448,25 @@ export default {
} else {
values.violationItem = undefined;
}
// 非必填项未填写时不要提交 -1 占位值
values.fineAmount = this.normalizeOptionalAmount(values.fineAmount);
values.deductPoints = this.normalizeOptionalAmount(values.deductPoints);
values.penaltyUnit = this.normalizeOptionalText(values.penaltyUnit);
values.processResult = this.normalizeOptionalText(values.processResult);
values.attachments = this.stringifyAttachments(values.attachments);
return values;
},
normalizeOptionalAmount(value) {
if (value === undefined || value === null || value === '' || Number(value) === -1) {
return null;
}
return value;
},
normalizeOptionalText(value) {
if (value === undefined || value === null) return null;
const text = String(value).trim();
return !text || text === '-1' ? null : text;
},
validateRow(row) {
if (row.vehicleType === '船舶' && !row.violationItem) {
this.$message.warning('请输入事项');
@@ -467,17 +483,17 @@ export default {
this.$message.warning('日期不能超过当前时间');
return false;
}
if (Number(row.fineAmount) < 0) {
const fineAmount = this.normalizeOptionalAmount(row.fineAmount);
const deductPoints = this.normalizeOptionalAmount(row.deductPoints);
if (fineAmount !== null && Number(fineAmount) < 0) {
this.$message.warning('被罚金额不能小于 0');
return false;
}
if (
row.deductPoints !== undefined &&
row.deductPoints !== null &&
row.deductPoints !== '' &&
(!Number.isInteger(Number(row.deductPoints)) ||
Number(row.deductPoints) < 0 ||
Number(row.deductPoints) > 15)
deductPoints !== null &&
(!Number.isInteger(Number(deductPoints)) ||
Number(deductPoints) < 0 ||
Number(deductPoints) > 15)
) {
this.$message.warning('被扣分数范围为 0-15 分');
return false;
@@ -570,6 +586,10 @@ export default {
detail.attachments = this.parseAttachments(detail.attachments);
detail.vehicleType = detail.vehicleType || '车辆';
detail.processStatus = detail.processStatus || '未处理';
detail.fineAmount = this.normalizeOptionalAmount(detail.fineAmount);
detail.deductPoints = this.normalizeOptionalAmount(detail.deductPoints);
detail.penaltyUnit = this.normalizeOptionalText(detail.penaltyUnit) || '';
detail.processResult = this.normalizeOptionalText(detail.processResult) || '';
this.form = detail;
this.updateVehicleTypeDisplays(detail.vehicleType);
this.vehicleOptions = [];