Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -42,6 +42,7 @@ export const config = {
|
|||||||
},
|
},
|
||||||
excludeVoidedProjects: true,
|
excludeVoidedProjects: true,
|
||||||
enableAttachmentTable: true,
|
enableAttachmentTable: true,
|
||||||
|
detailAttachmentDescriptionPlain: true,
|
||||||
enableTransportPlanForm: true,
|
enableTransportPlanForm: true,
|
||||||
enableShippingTemplateFreight: true,
|
enableShippingTemplateFreight: true,
|
||||||
editableRoadAddress: true,
|
editableRoadAddress: true,
|
||||||
|
|||||||
@@ -51,9 +51,9 @@ const formatGoodsQuantity = value => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getSecondCargoTypeName = item =>
|
const getSecondCargoTypeName = item =>
|
||||||
item?.cargoType ||
|
|
||||||
item?.secondCargoTypeName ||
|
item?.secondCargoTypeName ||
|
||||||
item?.secondCargoType ||
|
item?.secondCargoType ||
|
||||||
|
item?.cargoType ||
|
||||||
item?.goodsType ||
|
item?.goodsType ||
|
||||||
item?.type ||
|
item?.type ||
|
||||||
item?.cargoTypeName ||
|
item?.cargoTypeName ||
|
||||||
@@ -97,10 +97,10 @@ const formatGoodsInfo = row => {
|
|||||||
|
|
||||||
const text = Object.entries(groups)
|
const text = Object.entries(groups)
|
||||||
.map(([unit, group]) => {
|
.map(([unit, group]) => {
|
||||||
const quantity = `${formatGoodsQuantity(group.quantity)}${unit}`;
|
const quantity = formatGoodsQuantity(group.quantity);
|
||||||
return `${group.typeName}等${group.count}种货物 | ${quantity}`;
|
return `${group.typeName}等${group.count}种货物 | ${quantity}${unit ? ` ${unit}` : ''}`;
|
||||||
})
|
})
|
||||||
.join('; ');
|
.join('; ');
|
||||||
|
|
||||||
return text || row.goodsInfo || '';
|
return text || row.goodsInfo || '';
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -261,6 +261,7 @@ export const config = {
|
|||||||
importUrl: '/blade-transport/waybill-manage/import-waybill-manage',
|
importUrl: '/blade-transport/waybill-manage/import-waybill-manage',
|
||||||
defaultForm: {
|
defaultForm: {
|
||||||
carrierType: '承运商',
|
carrierType: '承运商',
|
||||||
|
transportType: 'road',
|
||||||
quantityUnit: '吨',
|
quantityUnit: '吨',
|
||||||
priceUnit: '元/吨',
|
priceUnit: '元/吨',
|
||||||
},
|
},
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -127,7 +127,7 @@
|
|||||||
<el-row :gutter="16">
|
<el-row :gutter="16">
|
||||||
<template v-if="isRoad(route)">
|
<template v-if="isRoad(route)">
|
||||||
<el-col v-if="route.carrierType === '承运商'" :span="6"><el-form-item label="承运商" required><el-select v-model="route.carrierName" filterable remote clearable placeholder="请选择" :remote-method="loadCarrierOptions" :loading="carrierLoading" @visible-change="visible => visible && loadCarrierOptions()"><el-option v-for="item in carrierOptions" :key="carrierOptionKey(item)" :label="carrierOptionLabel(item)" :value="carrierOptionLabel(item)" /></el-select></el-form-item></el-col>
|
<el-col v-if="route.carrierType === '承运商'" :span="6"><el-form-item label="承运商" required><el-select v-model="route.carrierName" filterable remote clearable placeholder="请选择" :remote-method="loadCarrierOptions" :loading="carrierLoading" @visible-change="visible => visible && loadCarrierOptions()"><el-option v-for="item in carrierOptions" :key="carrierOptionKey(item)" :label="carrierOptionLabel(item)" :value="carrierOptionLabel(item)" /></el-select></el-form-item></el-col>
|
||||||
<el-col :span="6"><el-form-item label="司机" :required="route.carrierType !== '承运商'"><el-select :ref="element => setDriverInput(route.segmentNo, element)" v-model="route.driverName" filterable remote clearable placeholder="请选择" :remote-method="loadDriverOptions" :loading="driverLoading" @visible-change="visible => visible && loadDriverOptions()" @change="value => handleDriverChange(route, value)"><el-option v-for="item in driverOptions" :key="driverOptionKey(item)" :label="driverOptionLabel(item)" :value="driverOptionLabel(item)" /></el-select></el-form-item></el-col>
|
<el-col :span="6"><el-form-item label="司机" :required="route.carrierType !== '承运商'"><el-autocomplete :ref="element => setDriverInput(route.segmentNo, element)" v-model="route.driverName" :debounce="300" :fetch-suggestions="fetchDriverSuggestions" clearable placeholder="请输入司机" :loading="driverLoading" @select="item => handleDriverSuggestionSelect(route, item)" /></el-form-item></el-col>
|
||||||
<el-col :span="6"><el-form-item :label="route.carrierType === '承运商' ? '手机号' : '司机手机号'" :required="route.carrierType !== '承运商'"><el-input v-model="route.driverPhone" placeholder="请输入" /></el-form-item></el-col>
|
<el-col :span="6"><el-form-item :label="route.carrierType === '承运商' ? '手机号' : '司机手机号'" :required="route.carrierType !== '承运商'"><el-input v-model="route.driverPhone" placeholder="请输入" /></el-form-item></el-col>
|
||||||
<el-col :span="6"><el-form-item label="车牌号" required><el-input v-model="route.vehicleNo" placeholder="请输入" /></el-form-item></el-col>
|
<el-col :span="6"><el-form-item label="车牌号" required><el-input v-model="route.vehicleNo" placeholder="请输入" /></el-form-item></el-col>
|
||||||
<el-col v-if="route.carrierType !== '承运商'" :span="6"><el-form-item label="挂车车牌号" required><el-input v-model="route.trailerVehicleNo" placeholder="请输入" /></el-form-item></el-col>
|
<el-col v-if="route.carrierType !== '承运商'" :span="6"><el-form-item label="挂车车牌号" required><el-input v-model="route.trailerVehicleNo" placeholder="请输入" /></el-form-item></el-col>
|
||||||
@@ -527,7 +527,11 @@ export default {
|
|||||||
this.carrierLoading = true;
|
this.carrierLoading = true;
|
||||||
try {
|
try {
|
||||||
this.carrierOptions = unwrapRecords(
|
this.carrierOptions = unwrapRecords(
|
||||||
await getCustomerList(1, 20, { customerName: keyword, customerType: '承运商' })
|
await getCustomerList(1, 20, {
|
||||||
|
customerName: keyword,
|
||||||
|
customerType: '承运商',
|
||||||
|
status: 1,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
this.carrierLoading = false;
|
this.carrierLoading = false;
|
||||||
@@ -541,6 +545,23 @@ export default {
|
|||||||
this.driverLoading = false;
|
this.driverLoading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
fetchDriverSuggestions(queryString, callback) {
|
||||||
|
this.loadDriverOptions(String(queryString || '').trim())
|
||||||
|
.then(() =>
|
||||||
|
callback(
|
||||||
|
this.driverOptions.map(item => ({
|
||||||
|
...item,
|
||||||
|
value: this.driverOptionLabel(item),
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.catch(() => callback([]));
|
||||||
|
},
|
||||||
|
handleDriverSuggestionSelect(route, item = {}) {
|
||||||
|
route.driverName = this.driverOptionLabel(item);
|
||||||
|
route.driverId = item.id || item.driverId || '';
|
||||||
|
route.driverPhone = item.mobile || item.driverPhone || item.phone || route.driverPhone || '';
|
||||||
|
},
|
||||||
setDriverInput(segmentNo, element) {
|
setDriverInput(segmentNo, element) {
|
||||||
if (element) this.driverInputs[segmentNo] = element;
|
if (element) this.driverInputs[segmentNo] = element;
|
||||||
else delete this.driverInputs[segmentNo];
|
else delete this.driverInputs[segmentNo];
|
||||||
|
|||||||
@@ -137,21 +137,22 @@
|
|||||||
label="货物名称"
|
label="货物名称"
|
||||||
min-width="130"
|
min-width="130"
|
||||||
><template #default="{ row }"
|
><template #default="{ row }"
|
||||||
><el-select
|
><el-autocomplete
|
||||||
v-model="row.cargoName"
|
v-model="row.cargoName"
|
||||||
filterable
|
|
||||||
allow-create
|
|
||||||
default-first-option
|
|
||||||
clearable
|
clearable
|
||||||
placeholder="请选择或输入"
|
placeholder="请输入货物名称"
|
||||||
|
:debounce="300"
|
||||||
|
:fetch-suggestions="
|
||||||
|
(queryString, callback) => fetchCargoSuggestions(queryString, callback, row.cargoTypePath)
|
||||||
|
"
|
||||||
:loading="isCargoOptionsLoading(row.cargoTypePath)"
|
:loading="isCargoOptionsLoading(row.cargoTypePath)"
|
||||||
@visible-change="visible => visible && loadCargoOptionsByType(row.cargoTypePath)"
|
@select="cargo => selectCargoSuggestion(row, cargo)"
|
||||||
@change="selectCargoName(row, $event)"
|
><template #default="{ item }">
|
||||||
><el-option
|
<div class="master-editor__cargo-autocomplete-item">
|
||||||
v-for="cargo in getCargoOptionsByType(row.cargoTypePath)"
|
<span>{{ item.cargoName || item.name || '' }}</span>
|
||||||
:key="cargo.id"
|
<small v-if="item.cargoCode">{{ item.cargoCode }}</small>
|
||||||
:label="cargo.cargoName"
|
</div>
|
||||||
:value="cargo.cargoName" /></el-select
|
</template></el-autocomplete
|
||||||
></template></el-table-column
|
></template></el-table-column
|
||||||
><el-table-column label="货物类型" min-width="130"
|
><el-table-column label="货物类型" min-width="130"
|
||||||
><template #default="{ row }"
|
><template #default="{ row }"
|
||||||
@@ -1062,6 +1063,45 @@ export default {
|
|||||||
this.cargoOptionsLoadingMap = { ...this.cargoOptionsLoadingMap, [key]: false };
|
this.cargoOptionsLoadingMap = { ...this.cargoOptionsLoadingMap, [key]: false };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async fetchCargoSuggestions(queryString, callback, path = []) {
|
||||||
|
const keyword = String(queryString || '').trim();
|
||||||
|
const cargoType = this.getCargoTypeByPath(path);
|
||||||
|
const key = this.getCargoOptionsKey(path);
|
||||||
|
if (!keyword && !cargoType) {
|
||||||
|
callback([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (key) this.cargoOptionsLoadingMap = { ...this.cargoOptionsLoadingMap, [key]: true };
|
||||||
|
try {
|
||||||
|
const response = await getCommonCargoList(1, 50, {
|
||||||
|
allDept: 0,
|
||||||
|
...(cargoType
|
||||||
|
? {
|
||||||
|
secondCargoTypeName: cargoType.label,
|
||||||
|
secondCargoTypeCode: cargoType.cargoCode || cargoType.code || '',
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
...(keyword ? { cargoName: keyword } : {}),
|
||||||
|
});
|
||||||
|
const options = this.responseRecords(response);
|
||||||
|
if (key) this.cargoOptionsMap = { ...this.cargoOptionsMap, [key]: options };
|
||||||
|
callback(
|
||||||
|
options.map(item => ({
|
||||||
|
...item,
|
||||||
|
value: item.cargoName || item.name || '',
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
window.console.log(error);
|
||||||
|
callback([]);
|
||||||
|
} finally {
|
||||||
|
if (key) this.cargoOptionsLoadingMap = { ...this.cargoOptionsLoadingMap, [key]: false };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectCargoSuggestion(goods, cargo = {}) {
|
||||||
|
goods.cargoName = cargo.cargoName || cargo.name || '';
|
||||||
|
Object.assign(goods, this.normalizeCargoRow(cargo, goods.cargoTypePath));
|
||||||
|
},
|
||||||
selectCargoName(goods, cargoName) {
|
selectCargoName(goods, cargoName) {
|
||||||
if (!cargoName) return;
|
if (!cargoName) return;
|
||||||
const cargo = this.getCargoOptionsByType(goods.cargoTypePath).find(
|
const cargo = this.getCargoOptionsByType(goods.cargoTypePath).find(
|
||||||
|
|||||||
@@ -583,6 +583,7 @@
|
|||||||
clearable
|
clearable
|
||||||
filterable
|
filterable
|
||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
|
:disabled="dialogMode === 'add'"
|
||||||
@visible-change="visible => visible && loadTransportTypeOptions()"
|
@visible-change="visible => visible && loadTransportTypeOptions()"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
@@ -825,27 +826,15 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="司机" :required="['自运', '网货平台'].includes(dialogForm.carrierType)">
|
<el-form-item label="司机" :required="['自运', '网货平台'].includes(dialogForm.carrierType)">
|
||||||
<el-select
|
<el-autocomplete
|
||||||
v-model="dialogForm.driverName"
|
v-model="dialogForm.driverName"
|
||||||
clearable
|
clearable
|
||||||
filterable
|
:debounce="300"
|
||||||
allow-create
|
:fetch-suggestions="fetchDriverSuggestions"
|
||||||
default-first-option
|
|
||||||
remote
|
|
||||||
reserve-keyword
|
|
||||||
:remote-method="loadDriverOptions"
|
|
||||||
:loading="driverLoading"
|
:loading="driverLoading"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
@change="handleDriverChange"
|
@select="item => handleDriverSuggestionSelect('dialog', item)"
|
||||||
@visible-change="visible => visible && loadDriverOptions(dialogForm.driverName)"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="item in driverOptions"
|
|
||||||
:key="item.id || item.driverName || item.name"
|
|
||||||
:label="item.driverName || item.name"
|
|
||||||
:value="item.driverName || item.name"
|
|
||||||
/>
|
/>
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="手机号" :required="['自运', '网货平台'].includes(dialogForm.carrierType)">
|
<el-form-item label="手机号" :required="['自运', '网货平台'].includes(dialogForm.carrierType)">
|
||||||
<el-input v-model="dialogForm.driverPhone" clearable placeholder="请输入" />
|
<el-input v-model="dialogForm.driverPhone" clearable placeholder="请输入" />
|
||||||
@@ -853,13 +842,13 @@
|
|||||||
<el-form-item label="车牌号" required>
|
<el-form-item label="车牌号" required>
|
||||||
<el-input v-model="dialogForm.vehicleNo" clearable placeholder="请输入" />
|
<el-input v-model="dialogForm.vehicleNo" clearable placeholder="请输入" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="挂车车牌号">
|
<el-form-item v-if="dialogForm.carrierType !== '承运商'" label="挂车车牌号">
|
||||||
<el-input v-model="dialogForm.trailerVehicleNo" clearable placeholder="请输入" />
|
<el-input v-model="dialogForm.trailerVehicleNo" clearable placeholder="请输入" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="押运人">
|
<el-form-item v-if="dialogForm.carrierType !== '承运商'" label="押运人">
|
||||||
<el-input v-model="dialogForm.escortName" clearable placeholder="请输入" />
|
<el-input v-model="dialogForm.escortName" clearable placeholder="请输入" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="押运人手机号">
|
<el-form-item v-if="dialogForm.carrierType !== '承运商'" label="押运人手机号">
|
||||||
<el-input v-model="dialogForm.escortPhone" clearable placeholder="请输入" />
|
<el-input v-model="dialogForm.escortPhone" clearable placeholder="请输入" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="里程(km)">
|
<el-form-item label="里程(km)">
|
||||||
@@ -1047,7 +1036,7 @@ const createDialogForm = () => ({
|
|||||||
batchNo: '',
|
batchNo: '',
|
||||||
currentProcessNode: '接单',
|
currentProcessNode: '接单',
|
||||||
mileage: '',
|
mileage: '',
|
||||||
estimatedStartDate: dayjs().format('YYYY-MM-DD'),
|
estimatedStartDate: '',
|
||||||
estimatedEndDate: '',
|
estimatedEndDate: '',
|
||||||
taskRemark: '',
|
taskRemark: '',
|
||||||
goodsJson: '',
|
goodsJson: '',
|
||||||
@@ -1207,6 +1196,13 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.loadTransportTypeOptions();
|
this.loadTransportTypeOptions();
|
||||||
this.loadTable();
|
this.loadTable();
|
||||||
|
const detailId = this.$route.query.detailId;
|
||||||
|
if (detailId) this.openLoadingDialog('view', { id: detailId });
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
'$route.query.detailId'(detailId) {
|
||||||
|
if (detailId) this.openLoadingDialog('view', { id: detailId });
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
buildQueryParams(form) {
|
buildQueryParams(form) {
|
||||||
@@ -1280,11 +1276,11 @@ export default {
|
|||||||
findWaybillId(row, waybillNo) {
|
findWaybillId(row, waybillNo) {
|
||||||
const rows = this.splitText(row.loadingSubNos);
|
const rows = this.splitText(row.loadingSubNos);
|
||||||
const index = rows.findIndex(item => item === waybillNo);
|
const index = rows.findIndex(item => item === waybillNo);
|
||||||
const linkedRows = this.parseJsonArray(row.waybillList || row.waybillRows || row.waybills);
|
const linkedRows = parseJsonArray(row.waybillList || row.waybillRows || row.waybills);
|
||||||
if (linkedRows[index]?.id || linkedRows[index]?.waybillId) {
|
if (linkedRows[index]?.id || linkedRows[index]?.waybillId) {
|
||||||
return linkedRows[index].id || linkedRows[index].waybillId;
|
return linkedRows[index].id || linkedRows[index].waybillId;
|
||||||
}
|
}
|
||||||
const ids = this.parseJsonArray(row.waybillIdsJson);
|
const ids = parseJsonArray(row.waybillIdsJson);
|
||||||
return ids[index] || '';
|
return ids[index] || '';
|
||||||
},
|
},
|
||||||
async openWaybillDetail(row = {}) {
|
async openWaybillDetail(row = {}) {
|
||||||
@@ -1415,6 +1411,7 @@ export default {
|
|||||||
this.dialogVisible = true;
|
this.dialogVisible = true;
|
||||||
this.dialogLoading = true;
|
this.dialogLoading = true;
|
||||||
this.resetDialogData();
|
this.resetDialogData();
|
||||||
|
if (mode === 'add') this.candidateQuery.transportType = 'road';
|
||||||
try {
|
try {
|
||||||
if (row?.id) {
|
if (row?.id) {
|
||||||
const res = await loadingApi.getDetail(row.id);
|
const res = await loadingApi.getDetail(row.id);
|
||||||
@@ -1435,6 +1432,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.dialogForm = createDialogForm();
|
this.dialogForm = createDialogForm();
|
||||||
this.candidateQuery = createCandidateSearchForm();
|
this.candidateQuery = createCandidateSearchForm();
|
||||||
|
this.candidateQuery.transportType = 'road';
|
||||||
await this.loadCandidateWaybills();
|
await this.loadCandidateWaybills();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1465,6 +1463,7 @@ export default {
|
|||||||
},
|
},
|
||||||
clearLoadingDialog() {
|
clearLoadingDialog() {
|
||||||
this.resetDialogData();
|
this.resetDialogData();
|
||||||
|
if (this.dialogMode === 'add') this.candidateQuery.transportType = 'road';
|
||||||
this.loadCandidateWaybills();
|
this.loadCandidateWaybills();
|
||||||
},
|
},
|
||||||
restoreRouteAndCargo() {
|
restoreRouteAndCargo() {
|
||||||
@@ -1719,6 +1718,7 @@ export default {
|
|||||||
...this.buildQueryParams(this.candidateQuery),
|
...this.buildQueryParams(this.candidateQuery),
|
||||||
businessStatus: 'pending',
|
businessStatus: 'pending',
|
||||||
onlyUnassignedLoading: 1,
|
onlyUnassignedLoading: 1,
|
||||||
|
...(this.dialogMode === 'add' ? { transportType: 'road' } : {}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const page = unwrapPage(res);
|
const page = unwrapPage(res);
|
||||||
@@ -1738,6 +1738,7 @@ export default {
|
|||||||
},
|
},
|
||||||
handleCandidateReset() {
|
handleCandidateReset() {
|
||||||
this.candidateQuery = createCandidateSearchForm();
|
this.candidateQuery = createCandidateSearchForm();
|
||||||
|
if (this.dialogMode === 'add') this.candidateQuery.transportType = 'road';
|
||||||
this.candidatePage.currentPage = 1;
|
this.candidatePage.currentPage = 1;
|
||||||
this.loadCandidateWaybills();
|
this.loadCandidateWaybills();
|
||||||
},
|
},
|
||||||
@@ -1805,18 +1806,6 @@ export default {
|
|||||||
if (!this.dialogForm.transportType) {
|
if (!this.dialogForm.transportType) {
|
||||||
this.dialogForm.transportType = rows.find(row => row.transportType)?.transportType || '';
|
this.dialogForm.transportType = rows.find(row => row.transportType)?.transportType || '';
|
||||||
}
|
}
|
||||||
if (!this.dialogForm.carrierName) {
|
|
||||||
this.dialogForm.carrierName = rows.find(row => row.carrierName)?.carrierName || '';
|
|
||||||
}
|
|
||||||
if (!this.dialogForm.driverName) {
|
|
||||||
this.dialogForm.driverName = rows.find(row => row.driverName)?.driverName || '';
|
|
||||||
}
|
|
||||||
if (!this.dialogForm.driverPhone) {
|
|
||||||
this.dialogForm.driverPhone = rows.find(row => row.driverPhone)?.driverPhone || '';
|
|
||||||
}
|
|
||||||
if (!this.dialogForm.vehicleNo) {
|
|
||||||
this.dialogForm.vehicleNo = rows.find(row => row.vehicleNo)?.vehicleNo || '';
|
|
||||||
}
|
|
||||||
if (rebuildRoute) {
|
if (rebuildRoute) {
|
||||||
this.routeNodes = this.buildRouteNodes(rows);
|
this.routeNodes = this.buildRouteNodes(rows);
|
||||||
}
|
}
|
||||||
@@ -2079,7 +2068,11 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleCarrierTypeChange() {
|
handleCarrierTypeChange() {
|
||||||
if (['自运', '网货平台'].includes(this.dialogForm.carrierType)) {
|
if (this.dialogForm.carrierType === '承运商') {
|
||||||
|
this.dialogForm.trailerVehicleNo = '';
|
||||||
|
this.dialogForm.escortName = '';
|
||||||
|
this.dialogForm.escortPhone = '';
|
||||||
|
} else if (['自运', '网货平台'].includes(this.dialogForm.carrierType)) {
|
||||||
this.dialogForm.carrierName = '';
|
this.dialogForm.carrierName = '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2089,6 +2082,26 @@ export default {
|
|||||||
this.dialogForm.driverPhone = driver.mobile || driver.driverPhone || driver.phone || '';
|
this.dialogForm.driverPhone = driver.mobile || driver.driverPhone || driver.phone || '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
fetchDriverSuggestions(queryString, callback) {
|
||||||
|
this.loadDriverOptions(String(queryString || '').trim())
|
||||||
|
.then(() =>
|
||||||
|
callback(
|
||||||
|
this.driverOptions.map(item => ({
|
||||||
|
...item,
|
||||||
|
value: item.driverName || item.name || '',
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.catch(() => callback([]));
|
||||||
|
},
|
||||||
|
handleDriverSuggestionSelect(target, item = {}) {
|
||||||
|
const value = item.driverName || item.name || '';
|
||||||
|
if (target === 'dialog') {
|
||||||
|
this.dialogForm.driverName = value;
|
||||||
|
const phone = item.mobile || item.driverPhone || item.phone || '';
|
||||||
|
if (phone) this.dialogForm.driverPhone = phone;
|
||||||
|
}
|
||||||
|
},
|
||||||
handleMileageInput(value) {
|
handleMileageInput(value) {
|
||||||
this.dialogForm.mileage = String(value || '').replace(/[^\d.]/g, '');
|
this.dialogForm.mileage = String(value || '').replace(/[^\d.]/g, '');
|
||||||
},
|
},
|
||||||
@@ -2147,7 +2160,11 @@ export default {
|
|||||||
this.carrierLoading = true;
|
this.carrierLoading = true;
|
||||||
try {
|
try {
|
||||||
this.carrierOptions = unwrapRecords(
|
this.carrierOptions = unwrapRecords(
|
||||||
await getCustomerList(1, 20, { customerName: keyword })
|
await getCustomerList(1, 20, {
|
||||||
|
customerName: keyword,
|
||||||
|
customerType: '承运商',
|
||||||
|
status: 1,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
this.carrierLoading = false;
|
this.carrierLoading = false;
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
v-model="form"
|
v-model="form"
|
||||||
ref="crud"
|
ref="crud"
|
||||||
:permission="permissionList"
|
:permission="permissionList"
|
||||||
|
:before-open="beforeOpen"
|
||||||
|
@row-save="rowSave"
|
||||||
|
@row-update="rowUpdate"
|
||||||
|
@row-del="rowDel"
|
||||||
@search-change="searchChange"
|
@search-change="searchChange"
|
||||||
@search-reset="searchReset"
|
@search-reset="searchReset"
|
||||||
@selection-change="selectionChange"
|
@selection-change="selectionChange"
|
||||||
@@ -21,7 +25,7 @@
|
|||||||
type="primary"
|
type="primary"
|
||||||
icon="el-icon-plus"
|
icon="el-icon-plus"
|
||||||
v-if="hasPermission('process_config_add')"
|
v-if="hasPermission('process_config_add')"
|
||||||
@click="openProcessConfig()"
|
@click="$refs.crud.rowAdd()"
|
||||||
>新建过程配置
|
>新建过程配置
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
@@ -42,6 +46,27 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template #menu-form-before="{ disabled }">
|
||||||
|
<template v-if="!dialogReadonly">
|
||||||
|
<el-button
|
||||||
|
plain
|
||||||
|
:loading="disabled"
|
||||||
|
:disabled="disabled"
|
||||||
|
@click="handleProcessConfigSave('draft')"
|
||||||
|
>
|
||||||
|
保存草稿
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:loading="disabled"
|
||||||
|
:disabled="disabled"
|
||||||
|
@click="handleProcessConfigSave('enabled')"
|
||||||
|
>
|
||||||
|
保存并启用
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #status="{ row }">
|
<template #status="{ row }">
|
||||||
<el-tag :type="statusTagType(row.status)">
|
<el-tag :type="statusTagType(row.status)">
|
||||||
{{ row.statusName || displayStatus(row.status) }}
|
{{ row.statusName || displayStatus(row.status) }}
|
||||||
@@ -54,70 +79,13 @@
|
|||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #menu="{ row, index }">
|
<template #basicInfo-form>
|
||||||
<el-link
|
<section-card title="基本信息">
|
||||||
type="primary"
|
|
||||||
v-if="hasPermission('process_config_view')"
|
|
||||||
@click="openProcessConfig(row, true)"
|
|
||||||
>
|
|
||||||
查看
|
|
||||||
</el-link>
|
|
||||||
<el-link
|
|
||||||
type="primary"
|
|
||||||
v-if="canEdit(row)"
|
|
||||||
@click="openProcessConfig(row, false)"
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</el-link>
|
|
||||||
<el-link
|
|
||||||
type="primary"
|
|
||||||
v-if="hasPermission('process_config_copy')"
|
|
||||||
@click="handleCopy(row)"
|
|
||||||
>
|
|
||||||
复制
|
|
||||||
</el-link>
|
|
||||||
<el-link
|
|
||||||
type="primary"
|
|
||||||
v-if="canEnable(row)"
|
|
||||||
@click="handleAction('enable', row)"
|
|
||||||
>
|
|
||||||
启用
|
|
||||||
</el-link>
|
|
||||||
<el-link
|
|
||||||
type="primary"
|
|
||||||
v-if="canDisable(row)"
|
|
||||||
@click="handleAction('disable', row)"
|
|
||||||
>
|
|
||||||
停用
|
|
||||||
</el-link>
|
|
||||||
<el-link
|
|
||||||
type="danger"
|
|
||||||
v-if="canDelete(row)"
|
|
||||||
@click="rowDel(row)"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-link>
|
|
||||||
</template>
|
|
||||||
</avue-crud>
|
|
||||||
|
|
||||||
<el-dialog
|
|
||||||
:title="dialogTitle"
|
|
||||||
append-to-body
|
|
||||||
v-model="processBox"
|
|
||||||
width="92%"
|
|
||||||
top="4vh"
|
|
||||||
class="process-dialog"
|
|
||||||
@closed="resetProcess"
|
|
||||||
>
|
|
||||||
<el-form
|
<el-form
|
||||||
ref="processForm"
|
|
||||||
:model="form"
|
:model="form"
|
||||||
label-position="right"
|
label-position="right"
|
||||||
label-width="calc(6em + 24px)"
|
label-width="calc(6em + 24px)"
|
||||||
:disabled="dialogReadonly"
|
|
||||||
class="process-form dialog-form-label-fixed"
|
|
||||||
>
|
>
|
||||||
<section-card title="基本信息">
|
|
||||||
<el-row :gutter="18">
|
<el-row :gutter="18">
|
||||||
<el-col :span="6">
|
<el-col :span="6">
|
||||||
<el-form-item label="配置名称" required>
|
<el-form-item label="配置名称" required>
|
||||||
@@ -181,8 +149,12 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
</el-form>
|
||||||
</section-card>
|
</section-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #nodeConfigJson-form>
|
||||||
|
<div class="process-config-page__custom-form">
|
||||||
<section-card title="现行过程节点">
|
<section-card title="现行过程节点">
|
||||||
<div class="process-config-page__timeline" v-if="enabledNodes.length">
|
<div class="process-config-page__timeline" v-if="enabledNodes.length">
|
||||||
<div class="process-config-page__timeline-line"></div>
|
<div class="process-config-page__timeline-line"></div>
|
||||||
@@ -386,20 +358,54 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</section-card>
|
</section-card>
|
||||||
</el-form>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template #footer>
|
<template #menu="{ row, index }">
|
||||||
<template v-if="!dialogReadonly">
|
<el-link
|
||||||
<el-button plain :loading="submitting" :disabled="submitting" @click="submitProcessConfig('draft')">
|
type="primary"
|
||||||
保存草稿
|
v-if="hasPermission('process_config_view')"
|
||||||
</el-button>
|
@click="$refs.crud.rowView(row, index)"
|
||||||
<el-button type="primary" :loading="submitting" :disabled="submitting" @click="submitProcessConfig('enabled')">
|
>
|
||||||
保存并启用
|
查看
|
||||||
</el-button>
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
type="primary"
|
||||||
|
v-if="canEdit(row)"
|
||||||
|
@click="$refs.crud.rowEdit(row, index)"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
type="primary"
|
||||||
|
v-if="hasPermission('process_config_copy')"
|
||||||
|
@click="handleCopy(row)"
|
||||||
|
>
|
||||||
|
复制
|
||||||
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
type="primary"
|
||||||
|
v-if="canEnable(row)"
|
||||||
|
@click="handleAction('enable', row)"
|
||||||
|
>
|
||||||
|
启用
|
||||||
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
type="primary"
|
||||||
|
v-if="canDisable(row)"
|
||||||
|
@click="handleAction('disable', row)"
|
||||||
|
>
|
||||||
|
停用
|
||||||
|
</el-link>
|
||||||
|
<el-link
|
||||||
|
type="danger"
|
||||||
|
v-if="canDelete(row)"
|
||||||
|
@click="rowDel(row)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
<el-button @click="processBox = false">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
|
</avue-crud>
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<empty-pagination
|
<empty-pagination
|
||||||
:page="page"
|
:page="page"
|
||||||
@@ -513,8 +519,6 @@ export default {
|
|||||||
},
|
},
|
||||||
selectionList: [],
|
selectionList: [],
|
||||||
submitStatus: 1,
|
submitStatus: 1,
|
||||||
processBox: false,
|
|
||||||
submitting: false,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -536,10 +540,6 @@ export default {
|
|||||||
enabledNodes() {
|
enabledNodes() {
|
||||||
return this.nodeRows.filter(item => item.enabled);
|
return this.nodeRows.filter(item => item.enabled);
|
||||||
},
|
},
|
||||||
dialogTitle() {
|
|
||||||
if (this.dialogReadonly) return '查看过程配置';
|
|
||||||
return this.form.id ? '编辑过程配置' : '新增过程配置';
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.resetNodeRows();
|
this.resetNodeRows();
|
||||||
@@ -730,6 +730,33 @@ export default {
|
|||||||
this.form.projectIds = project ? String(project.id) : '';
|
this.form.projectIds = project ? String(project.id) : '';
|
||||||
this.form.projectNames = project ? project.projectName : '';
|
this.form.projectNames = project ? project.projectName : '';
|
||||||
},
|
},
|
||||||
|
checkEnabledProjectConfig(projectId, excludeId = '') {
|
||||||
|
if (!projectId) return Promise.resolve(false);
|
||||||
|
return api
|
||||||
|
.getList(1, 100, {
|
||||||
|
projectIds: String(projectId),
|
||||||
|
status: 1,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
const data = res?.data?.data || {};
|
||||||
|
const records = Array.isArray(data) ? data : data.records || [];
|
||||||
|
return records.some(item => {
|
||||||
|
const projectIds = String(item.projectIds || '')
|
||||||
|
.split(/[,,]/)
|
||||||
|
.map(value => value.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
return (
|
||||||
|
String(item.id || '') !== String(excludeId || '') &&
|
||||||
|
Number(item.status) === 1 &&
|
||||||
|
projectIds.includes(String(projectId))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
checkEnabledProjectBeforeSubmit(row) {
|
||||||
|
if (row.id || !row.projectIds) return Promise.resolve(false);
|
||||||
|
return this.checkEnabledProjectConfig(row.projectIds);
|
||||||
|
},
|
||||||
handleFinishDaysInput(value) {
|
handleFinishDaysInput(value) {
|
||||||
this.form.defaultFinishDays = String(value || '')
|
this.form.defaultFinishDays = String(value || '')
|
||||||
.replace(/\D/g, '')
|
.replace(/\D/g, '')
|
||||||
@@ -853,6 +880,11 @@ export default {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
stopSubmitLoading(loading) {
|
||||||
|
if (typeof loading === 'function') {
|
||||||
|
loading();
|
||||||
|
}
|
||||||
|
},
|
||||||
needsDuplicateVoucherConfirm() {
|
needsDuplicateVoucherConfirm() {
|
||||||
const signNode = this.nodeRows.find(item => item.key === 'sign');
|
const signNode = this.nodeRows.find(item => item.key === 'sign');
|
||||||
const returnNode = this.nodeRows.find(item => item.key === 'return');
|
const returnNode = this.nodeRows.find(item => item.key === 'return');
|
||||||
@@ -882,74 +914,50 @@ export default {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
openProcessConfig(row, readonly = false) {
|
submitRow(row, done, loading, status = this.submitStatus) {
|
||||||
this.dialogReadonly = readonly;
|
|
||||||
this.submitStatus = 1;
|
this.submitStatus = 1;
|
||||||
if (row && row.id) {
|
|
||||||
api.getDetail(row.id).then(res => {
|
|
||||||
const data = res.data.data || {};
|
|
||||||
this.form = data;
|
|
||||||
this.selectedProjectId = data.projectIds ? String(data.projectIds) : '';
|
|
||||||
this.ensureSelectedProjectOption();
|
|
||||||
this.parseNodeConfig(data);
|
|
||||||
this.processBox = true;
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.form = {
|
|
||||||
configName: '',
|
|
||||||
projectIds: '',
|
|
||||||
projectNames: '',
|
|
||||||
defaultFinishDays: '',
|
|
||||||
remark: '',
|
|
||||||
status: 0,
|
|
||||||
};
|
|
||||||
this.selectedProjectId = '';
|
|
||||||
this.resetNodeRows();
|
|
||||||
this.processBox = true;
|
|
||||||
},
|
|
||||||
submitProcessConfig(mode) {
|
|
||||||
this.$refs.processForm.validate(valid => {
|
|
||||||
if (!valid) return;
|
|
||||||
this.submitting = true;
|
|
||||||
this.submitStatus = mode === 'draft' ? 2 : 1;
|
|
||||||
this.syncNodeFields();
|
this.syncNodeFields();
|
||||||
const submitRow = this.normalizeRow({ ...this.form, status: this.submitStatus });
|
const submitRow = this.normalizeRow({ ...row, status });
|
||||||
if (!this.validateRow(submitRow)) {
|
if (!this.validateRow(submitRow)) {
|
||||||
this.submitting = false;
|
this.stopSubmitLoading(loading);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.confirmDuplicateVoucher()
|
this.checkEnabledProjectBeforeSubmit(submitRow)
|
||||||
|
.then(exists => {
|
||||||
|
if (exists) {
|
||||||
|
this.$message.warning('该项目已有启用状态的过程配置,不能重复添加');
|
||||||
|
throw { code: 'enabled-project-config' };
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => this.confirmDuplicateVoucher())
|
||||||
.then(() => api.submit(submitRow))
|
.then(() => api.submit(submitRow))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.submitting = false;
|
|
||||||
this.processBox = false;
|
|
||||||
this.onLoad(this.page);
|
this.onLoad(this.page);
|
||||||
this.$message({ type: 'success', message: '操作成功!' });
|
this.$message({ type: 'success', message: '操作成功!' });
|
||||||
|
done();
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
this.submitting = false;
|
if (
|
||||||
if (error !== 'cancel' && error !== 'close') {
|
error !== 'cancel' &&
|
||||||
|
error !== 'close' &&
|
||||||
|
error?.code !== 'enabled-project-config'
|
||||||
|
) {
|
||||||
window.console.log(error);
|
window.console.log(error);
|
||||||
}
|
}
|
||||||
});
|
this.stopSubmitLoading(loading);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
resetProcess() {
|
rowSave(row, done, loading) {
|
||||||
this.dialogReadonly = false;
|
this.submitRow(row, done, loading);
|
||||||
this.submitting = false;
|
},
|
||||||
this.submitStatus = 1;
|
rowUpdate(row, index, done, loading) {
|
||||||
this.form = {
|
this.submitRow(row, done, loading);
|
||||||
configName: '',
|
},
|
||||||
projectIds: '',
|
handleProcessConfigSave(mode) {
|
||||||
projectNames: '',
|
this.submitStatus = mode === 'draft' ? 2 : 1;
|
||||||
defaultFinishDays: '',
|
if (this.$refs.crud && typeof this.$refs.crud.rowSave === 'function') {
|
||||||
remark: '',
|
this.$refs.crud.rowSave();
|
||||||
status: 0,
|
}
|
||||||
};
|
|
||||||
this.selectedProjectId = '';
|
|
||||||
this.resetNodeRows();
|
|
||||||
this.$refs.processForm?.clearValidate();
|
|
||||||
},
|
},
|
||||||
rowDel(row) {
|
rowDel(row) {
|
||||||
this.$confirm('确定将选择数据删除?', {
|
this.$confirm('确定将选择数据删除?', {
|
||||||
@@ -993,6 +1001,35 @@ export default {
|
|||||||
}
|
}
|
||||||
this.selectionClear();
|
this.selectionClear();
|
||||||
},
|
},
|
||||||
|
beforeOpen(done, type) {
|
||||||
|
this.dialogReadonly = type === 'view';
|
||||||
|
this.submitStatus = 1;
|
||||||
|
if (type === 'add') {
|
||||||
|
this.form = {
|
||||||
|
configName: '',
|
||||||
|
projectIds: '',
|
||||||
|
projectNames: '',
|
||||||
|
defaultFinishDays: '',
|
||||||
|
remark: '',
|
||||||
|
status: 0,
|
||||||
|
};
|
||||||
|
this.selectedProjectId = '';
|
||||||
|
this.resetNodeRows();
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (['edit', 'view'].includes(type)) {
|
||||||
|
api.getDetail(this.form.id).then(res => {
|
||||||
|
this.form = res.data.data || {};
|
||||||
|
this.selectedProjectId = this.form.projectIds ? String(this.form.projectIds) : '';
|
||||||
|
this.ensureSelectedProjectOption();
|
||||||
|
this.parseNodeConfig(this.form);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
},
|
||||||
searchReset() {
|
searchReset() {
|
||||||
this.query = {};
|
this.query = {};
|
||||||
this.applyDefaultDeptSearch();
|
this.applyDefaultDeptSearch();
|
||||||
@@ -1090,8 +1127,28 @@ export default {
|
|||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning',
|
type: 'warning',
|
||||||
})
|
})
|
||||||
.then(() => api[action](row.id))
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
if (action !== 'enable') return true;
|
||||||
|
return this.checkEnabledProjectConfig(row.projectIds, row.id)
|
||||||
|
.then(exists => {
|
||||||
|
if (exists) {
|
||||||
|
this.$message.warning('该项目已有其他启用状态的过程配置,不能重复启用');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
window.console.log(error);
|
||||||
|
this.$message.error('检查项目过程配置失败,暂不能启用');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(canProceed => {
|
||||||
|
if (!canProceed) return null;
|
||||||
|
return api[action](row.id);
|
||||||
|
})
|
||||||
|
.then(result => {
|
||||||
|
if (!result) return;
|
||||||
this.$message.success(`${actionName}成功`);
|
this.$message.success(`${actionName}成功`);
|
||||||
this.onLoad(this.page, this.query);
|
this.onLoad(this.page, this.query);
|
||||||
});
|
});
|
||||||
@@ -1240,10 +1297,5 @@ export default {
|
|||||||
:deep(.el-table__body tr:nth-child(even) > td.el-table__cell) {
|
:deep(.el-table__body tr:nth-child(even) > td.el-table__cell) {
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.process-dialog .el-dialog__body) {
|
|
||||||
max-height: 72vh;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -823,7 +823,12 @@ const loadProgress = async () => {
|
|||||||
businessType: 'voucher',
|
businessType: 'voucher',
|
||||||
});
|
});
|
||||||
const data = res.data?.data || {};
|
const data = res.data?.data || {};
|
||||||
progressRows.value = data.records || [];
|
progressRows.value = (data.records || []).slice().sort((left, right) => {
|
||||||
|
const leftTime = left.createTime ? new Date(left.createTime).getTime() : 0;
|
||||||
|
const rightTime = right.createTime ? new Date(right.createTime).getTime() : 0;
|
||||||
|
if (rightTime !== leftTime) return rightTime - leftTime;
|
||||||
|
return String(right.id || '').localeCompare(String(left.id || ''));
|
||||||
|
});
|
||||||
progressPage.total = data.total || 0;
|
progressPage.total = data.total || 0;
|
||||||
};
|
};
|
||||||
const searchProgress = () => {
|
const searchProgress = () => {
|
||||||
|
|||||||
@@ -655,7 +655,7 @@
|
|||||||
<el-image-viewer v-if="qualificationImagePreviewVisible" :url-list="qualificationImagePreviewUrls" :initial-index="qualificationImagePreviewIndex" @close="qualificationImagePreviewVisible = false" />
|
<el-image-viewer v-if="qualificationImagePreviewVisible" :url-list="qualificationImagePreviewUrls" :initial-index="qualificationImagePreviewIndex" @close="qualificationImagePreviewVisible = false" />
|
||||||
|
|
||||||
<section-card title="变更记录">
|
<section-card title="变更记录">
|
||||||
<el-table :data="changeRecordPage.records" border>
|
<el-table :data="changeRecordPage.records" border :show-overflow-tooltip="false">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="序号"
|
label="序号"
|
||||||
type="index"
|
type="index"
|
||||||
@@ -664,23 +664,25 @@
|
|||||||
:index="changeRecordIndex"
|
:index="changeRecordIndex"
|
||||||
/>
|
/>
|
||||||
<el-table-column label="变更日期" prop="changeTime" min-width="170" sortable />
|
<el-table-column label="变更日期" prop="changeTime" min-width="170" sortable />
|
||||||
<el-table-column label="变更字段" prop="changedFields" min-width="180" />
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="原数据"
|
label="变更内容"
|
||||||
prop="beforeData"
|
min-width="560"
|
||||||
min-width="240"
|
>
|
||||||
show-overflow-tooltip
|
<template #default="{ row }">
|
||||||
:formatter="formatChangeData"
|
<el-tooltip placement="top" :show-after="200">
|
||||||
/>
|
<template #content>
|
||||||
<el-table-column
|
<div class="change-record-content-tooltip">{{ formatChangeContent(row) }}</div>
|
||||||
label="修改后数据"
|
</template>
|
||||||
prop="afterData"
|
<span class="change-record-content-cell">{{ formatChangeContent(row) }}</span>
|
||||||
min-width="240"
|
</el-tooltip>
|
||||||
show-overflow-tooltip
|
</template>
|
||||||
:formatter="formatChangeData"
|
</el-table-column>
|
||||||
/>
|
|
||||||
<el-table-column label="变更内容" prop="changeContent" min-width="180" />
|
|
||||||
<el-table-column label="变更账号" prop="changeUserName" min-width="160" />
|
<el-table-column label="变更账号" prop="changeUserName" min-width="160" />
|
||||||
|
<el-table-column label="操作" width="90" fixed="right" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-link type="primary" @click="openChangeRecordDetail(row)">详情</el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<empty-pagination
|
<empty-pagination
|
||||||
:page="changeRecordPage"
|
:page="changeRecordPage"
|
||||||
@@ -690,6 +692,40 @@
|
|||||||
/>
|
/>
|
||||||
</section-card>
|
</section-card>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="changeRecordDetailVisible"
|
||||||
|
title="变更记录详情"
|
||||||
|
append-to-body
|
||||||
|
destroy-on-close
|
||||||
|
width="1100px"
|
||||||
|
top="10px"
|
||||||
|
class="change-record-detail-dialog"
|
||||||
|
>
|
||||||
|
<div v-if="changeRecordDetail" class="change-record-detail-meta">
|
||||||
|
<span>变更日期:{{ changeRecordDetail.changeTime || '-' }}</span>
|
||||||
|
<span>变更账号:{{ changeRecordDetail.changeUserName || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<el-table :data="changeRecordDetailRows" border :show-overflow-tooltip="false">
|
||||||
|
<el-table-column prop="field" label="变更字段" min-width="180" />
|
||||||
|
<el-table-column
|
||||||
|
prop="before"
|
||||||
|
label="变更前"
|
||||||
|
min-width="360"
|
||||||
|
class-name="change-record-detail-value"
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="after"
|
||||||
|
label="变更后"
|
||||||
|
min-width="500"
|
||||||
|
class-name="change-record-detail-value"
|
||||||
|
/>
|
||||||
|
</el-table>
|
||||||
|
<el-empty v-if="!changeRecordDetailRows.length" description="暂无变更内容" :image-size="60" />
|
||||||
|
<template #footer>
|
||||||
|
<el-button type="primary" @click="changeRecordDetailVisible = false">关闭</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="archiveBox = false">{{ readonly ? '关闭' : '取消' }}</el-button>
|
<el-button @click="archiveBox = false">{{ readonly ? '关闭' : '取消' }}</el-button>
|
||||||
<el-button type="primary" v-if="!readonly" @click="saveArchive">保存</el-button>
|
<el-button type="primary" v-if="!readonly" @click="saveArchive">保存</el-button>
|
||||||
@@ -1354,6 +1390,9 @@ export default {
|
|||||||
total: 0,
|
total: 0,
|
||||||
records: [],
|
records: [],
|
||||||
},
|
},
|
||||||
|
changeRecordDetailVisible: false,
|
||||||
|
changeRecordDetail: null,
|
||||||
|
changeRecordDetailRows: [],
|
||||||
contactIndex: -1,
|
contactIndex: -1,
|
||||||
contactForm: this.emptyContact(),
|
contactForm: this.emptyContact(),
|
||||||
contactMapBox: false,
|
contactMapBox: false,
|
||||||
@@ -1882,17 +1921,286 @@ export default {
|
|||||||
changeRecordIndex(index) {
|
changeRecordIndex(index) {
|
||||||
return (this.changeRecordPage.currentPage - 1) * this.changeRecordPage.pageSize + index + 1;
|
return (this.changeRecordPage.currentPage - 1) * this.changeRecordPage.pageSize + index + 1;
|
||||||
},
|
},
|
||||||
formatChangeData(row, column, value) {
|
parseChangeData(value) {
|
||||||
if (!value || value === '-1') return '';
|
if (!value || value === '-1') return {};
|
||||||
|
if (typeof value === 'object') return this.replaceNegativeOneWithBlank(value);
|
||||||
|
const text = String(value).trim();
|
||||||
|
if (!text || text === '-1') return {};
|
||||||
try {
|
try {
|
||||||
const data = this.replaceNegativeOneWithBlank(JSON.parse(value));
|
const parsed = JSON.parse(text);
|
||||||
return Object.entries(data)
|
return parsed && typeof parsed === 'object'
|
||||||
.map(([field, fieldValue]) => `${field}:${fieldValue ?? ''}`)
|
? this.replaceNegativeOneWithBlank(parsed)
|
||||||
.join(';');
|
: { 变更内容: parsed };
|
||||||
|
} catch (error) {
|
||||||
|
return text.split(/[;;]/).reduce((data, item) => {
|
||||||
|
const match = item.trim().match(/^([^::]+)\s*[::]\s*(.*)$/);
|
||||||
|
if (match) data[match[1].trim()] = match[2].trim();
|
||||||
|
return data;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getChangeFieldLabel(field) {
|
||||||
|
const fieldLabelMap = {
|
||||||
|
accessType: '准入标准',
|
||||||
|
approvalStatus: '审核状态',
|
||||||
|
currentNode: '当前节点',
|
||||||
|
currentProcessor: '当前处理人',
|
||||||
|
customerCode: '客商编号',
|
||||||
|
shortName: '客商简称',
|
||||||
|
fullName: '客商名称',
|
||||||
|
customerType: '客户类型',
|
||||||
|
customerNature: '客户性质',
|
||||||
|
unifiedCreditCode: '统一信用代码',
|
||||||
|
deptName: '所属组织',
|
||||||
|
approvedTime: '审核通过时间',
|
||||||
|
status: '状态',
|
||||||
|
businessTermType: '营业期限类型',
|
||||||
|
registeredRegionName: '注册地址',
|
||||||
|
registeredDetailAddress: '详细地址',
|
||||||
|
invoiceType: '发票类型',
|
||||||
|
qualificationAttachments: '客商材料',
|
||||||
|
customerAttachments: '客商材料',
|
||||||
|
客商材料: '客商材料',
|
||||||
|
};
|
||||||
|
if (fieldLabelMap[field]) return fieldLabelMap[field];
|
||||||
|
const column = (this.option.column || []).find(item => item.prop === field);
|
||||||
|
return column?.label || field;
|
||||||
|
},
|
||||||
|
normalizeChangeFieldValue(value) {
|
||||||
|
if (typeof value !== 'string') return value;
|
||||||
|
const text = value.trim();
|
||||||
|
if (!text || (!text.startsWith('[') && !text.startsWith('{') && text !== 'null')) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return JSON.parse(text);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
isEmptyChangeValue(value) {
|
||||||
|
const normalized = this.normalizeChangeFieldValue(value);
|
||||||
|
if (normalized === undefined || normalized === null || normalized === '' || normalized === '-1') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (Array.isArray(normalized)) return normalized.length === 0;
|
||||||
|
if (typeof normalized === 'object') return Object.keys(normalized).length === 0;
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
formatScoreChangeValue(value) {
|
||||||
|
const scores = Array.isArray(value) ? value : [value];
|
||||||
|
const categoryNames = { basic: '基础得分项', plus: '加分项目', minus: '减分项目' };
|
||||||
|
return scores
|
||||||
|
.filter(item => item && typeof item === 'object')
|
||||||
|
.map((score, index) => {
|
||||||
|
const summary = [
|
||||||
|
score.scoreDate && `评分日期:${score.scoreDate}`,
|
||||||
|
score.selfScore !== undefined && score.selfScore !== ''
|
||||||
|
? `自评得分:${this.formatScoreValue(score.selfScore)}`
|
||||||
|
: '',
|
||||||
|
score.reviewScore !== undefined && score.reviewScore !== ''
|
||||||
|
? `复评得分:${this.formatScoreValue(score.reviewScore)}`
|
||||||
|
: '',
|
||||||
|
score.finalScore !== undefined && score.finalScore !== ''
|
||||||
|
? `最终得分:${this.formatScoreValue(score.finalScore)}`
|
||||||
|
: '',
|
||||||
|
score.creditLevel ? `信用等级:${score.creditLevel}` : '',
|
||||||
|
].filter(Boolean);
|
||||||
|
const scoreDetails = this.normalizeChangeFieldValue(score.details);
|
||||||
|
const details = (Array.isArray(scoreDetails) ? scoreDetails : [])
|
||||||
|
.map(detail => {
|
||||||
|
const category =
|
||||||
|
detail.categoryName ||
|
||||||
|
categoryNames[this.getScoreDetailCategoryCode(detail)] ||
|
||||||
|
'';
|
||||||
|
const itemName = detail.itemName || '评分项目';
|
||||||
|
const option = detail.selectedOption || detail.scoreDescription || '';
|
||||||
|
const points = [
|
||||||
|
detail.selfScore !== undefined && detail.selfScore !== ''
|
||||||
|
? `自评${this.formatScoreValue(detail.selfScore)}分`
|
||||||
|
: '',
|
||||||
|
detail.reviewScore !== undefined && detail.reviewScore !== ''
|
||||||
|
? `复评${this.formatScoreValue(detail.reviewScore)}分`
|
||||||
|
: '',
|
||||||
|
].filter(Boolean);
|
||||||
|
if (
|
||||||
|
!points.length &&
|
||||||
|
detail.score !== undefined &&
|
||||||
|
detail.score !== '' &&
|
||||||
|
detail.score !== -1 &&
|
||||||
|
detail.score !== '-1'
|
||||||
|
) {
|
||||||
|
points.push(`得分${this.formatScoreValue(detail.score)}分`);
|
||||||
|
}
|
||||||
|
const label = `${category ? `${category}-` : ''}${itemName}`;
|
||||||
|
return `${label}${option ? `:${option}` : ''}${points.length ? `(${points.join(',')})` : ''}`;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
if (details.length) summary.push(`评分明细:${details.join(';')}`);
|
||||||
|
return summary.length ? summary.join(';') : `第${index + 1}条评分`;
|
||||||
|
})
|
||||||
|
.join(';');
|
||||||
|
},
|
||||||
|
normalizeScoreChangeEntries(value) {
|
||||||
|
const normalized = this.normalizeChangeFieldValue(value);
|
||||||
|
if (Array.isArray(normalized)) return normalized;
|
||||||
|
return normalized && typeof normalized === 'object' ? [normalized] : [];
|
||||||
|
},
|
||||||
|
formatScoreChangeDiff(beforeValue, afterValue) {
|
||||||
|
const beforeEntries = this.normalizeScoreChangeEntries(beforeValue);
|
||||||
|
const afterEntries = this.normalizeScoreChangeEntries(afterValue);
|
||||||
|
const size = Math.max(beforeEntries.length, afterEntries.length);
|
||||||
|
const fields = [
|
||||||
|
['scoreDate', '评分日期'],
|
||||||
|
['selfScore', '自评得分'],
|
||||||
|
['reviewScore', '复评得分'],
|
||||||
|
['finalScore', '最终得分'],
|
||||||
|
['creditLevel', '信用等级'],
|
||||||
|
['details', '评分明细'],
|
||||||
|
];
|
||||||
|
const formatValue = (field, value) => {
|
||||||
|
if (field === 'details') {
|
||||||
|
return this.formatScoreChangeValue([{ details: value }]).replace(/^第\d+条评分$/, '空');
|
||||||
|
}
|
||||||
|
if (value === undefined || value === null || value === '' || value === '-1') return '空';
|
||||||
|
return field.endsWith('Score') ? `${this.formatScoreValue(value)}分` : String(value);
|
||||||
|
};
|
||||||
|
const before = [];
|
||||||
|
const after = [];
|
||||||
|
for (let index = 0; index < size; index += 1) {
|
||||||
|
const left = beforeEntries[index] || {};
|
||||||
|
const right = afterEntries[index] || {};
|
||||||
|
fields.forEach(([field, label]) => {
|
||||||
|
const leftValue = left[field];
|
||||||
|
const rightValue = right[field];
|
||||||
|
if (JSON.stringify(leftValue) === JSON.stringify(rightValue)) return;
|
||||||
|
const prefix = size > 1 ? `第${index + 1}条-${label}` : label;
|
||||||
|
before.push(`${prefix}:${formatValue(field, leftValue)}`);
|
||||||
|
after.push(`${prefix}:${formatValue(field, rightValue)}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return { before: before.join(';') || '空', after: after.join(';') || '空' };
|
||||||
|
},
|
||||||
|
formatAttachmentChangeValue(value) {
|
||||||
|
const attachments = Array.isArray(value)
|
||||||
|
? value
|
||||||
|
: value && typeof value === 'object'
|
||||||
|
? [value]
|
||||||
|
: this.parseAttachments(value);
|
||||||
|
const names = attachments
|
||||||
|
.map(item => {
|
||||||
|
const fileName =
|
||||||
|
typeof item === 'string'
|
||||||
|
? item
|
||||||
|
: item?.originalName || item?.name || item?.fileName || item?.url || item?.link || '';
|
||||||
|
const name = String(fileName).trim().split('?')[0].split('/').pop();
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(name);
|
||||||
|
} catch (error) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
return names.length ? names.join('、') : '空';
|
||||||
|
},
|
||||||
|
formatChangeFieldValue(field, value) {
|
||||||
|
value = this.normalizeChangeFieldValue(value);
|
||||||
|
if (value === undefined || value === null || value === '' || value === '-1') return '空';
|
||||||
|
const normalizedField = {
|
||||||
|
准入类型: 'accessType',
|
||||||
|
准入标准: 'accessType',
|
||||||
|
审批状态: 'approvalStatus',
|
||||||
|
审核状态: 'approvalStatus',
|
||||||
|
状态: 'status',
|
||||||
|
客户类型: 'customerType',
|
||||||
|
客商材料: 'qualificationAttachments',
|
||||||
|
}[field] || field;
|
||||||
|
if (['qualificationAttachments', 'customerAttachments'].includes(normalizedField)) {
|
||||||
|
return this.formatAttachmentChangeValue(value);
|
||||||
|
}
|
||||||
|
if (normalizedField === '评分信息' || normalizedField === 'scores') {
|
||||||
|
return this.formatScoreChangeValue(value) || '空';
|
||||||
|
}
|
||||||
|
const valueMap = {
|
||||||
|
accessType: { temporary: '临时', formal: '正式' },
|
||||||
|
approvalStatus: {
|
||||||
|
draft: '草稿',
|
||||||
|
reviewing: '审核中',
|
||||||
|
approved: '审核通过',
|
||||||
|
rejected: '审核不通过',
|
||||||
|
},
|
||||||
|
status: { 1: '启用', 2: '停用', 0: '停用' },
|
||||||
|
};
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.map(item => this.formatChangeFieldValue(normalizedField, item)).join('、');
|
||||||
|
}
|
||||||
|
if (valueMap[normalizedField]?.[value] !== undefined) {
|
||||||
|
return valueMap[normalizedField][value];
|
||||||
|
}
|
||||||
|
if (normalizedField === 'customerType') {
|
||||||
|
return String(value)
|
||||||
|
.split(/[,,]/)
|
||||||
|
.map(item => ({ customer: '客户', carrier: '承运商' }[item.trim()] || item.trim()))
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('、');
|
||||||
|
}
|
||||||
|
if (typeof value === 'object') return JSON.stringify(value);
|
||||||
|
return String(value);
|
||||||
|
},
|
||||||
|
buildChangeRecordDetailRows(row = {}) {
|
||||||
|
const beforeData = this.parseChangeData(row.beforeData);
|
||||||
|
const afterData = this.parseChangeData(row.afterData);
|
||||||
|
const fields = [...new Set([...Object.keys(beforeData), ...Object.keys(afterData)])];
|
||||||
|
const rows = fields
|
||||||
|
.filter(
|
||||||
|
field =>
|
||||||
|
field !== '变更内容' &&
|
||||||
|
!(this.isEmptyChangeValue(beforeData[field]) && this.isEmptyChangeValue(afterData[field]))
|
||||||
|
)
|
||||||
|
.map(field => ({
|
||||||
|
field: this.getChangeFieldLabel(field),
|
||||||
|
...(this.isScoreChangeField(field)
|
||||||
|
? this.formatScoreChangeDiff(beforeData[field], afterData[field])
|
||||||
|
: {
|
||||||
|
before: this.formatChangeFieldValue(field, beforeData[field]),
|
||||||
|
after: this.formatChangeFieldValue(field, afterData[field]),
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
const content = String(row.changeContent || '').trim();
|
||||||
|
if (content) {
|
||||||
|
rows.unshift({ field: '变更内容', before: '空', after: content });
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
},
|
||||||
|
isScoreChangeField(field) {
|
||||||
|
return ['评分信息', 'scores'].includes(field);
|
||||||
|
},
|
||||||
|
openChangeRecordDetail(row) {
|
||||||
|
this.changeRecordDetail = row;
|
||||||
|
this.changeRecordDetailRows = this.buildChangeRecordDetailRows(row);
|
||||||
|
this.changeRecordDetailVisible = true;
|
||||||
|
},
|
||||||
|
formatChangeContent(row) {
|
||||||
|
const beforeData = this.parseChangeData(row.beforeData);
|
||||||
|
const afterData = this.parseChangeData(row.afterData);
|
||||||
|
const fields = [...new Set([...Object.keys(beforeData), ...Object.keys(afterData)])];
|
||||||
|
const details = fields
|
||||||
|
.filter(
|
||||||
|
field =>
|
||||||
|
field !== '变更内容' &&
|
||||||
|
!(this.isEmptyChangeValue(beforeData[field]) && this.isEmptyChangeValue(afterData[field]))
|
||||||
|
)
|
||||||
|
.map(field => {
|
||||||
|
const scoreDiff = this.isScoreChangeField(field)
|
||||||
|
? this.formatScoreChangeDiff(beforeData[field], afterData[field])
|
||||||
|
: null;
|
||||||
|
const before = scoreDiff?.before || this.formatChangeFieldValue(field, beforeData[field]);
|
||||||
|
const after = scoreDiff?.after || this.formatChangeFieldValue(field, afterData[field]);
|
||||||
|
return `${this.getChangeFieldLabel(field)}:变更前:${before} → 变更后:${after}`;
|
||||||
|
});
|
||||||
|
const content = String(row.changeContent || '').trim();
|
||||||
|
return [content, ...details].filter(Boolean).join(';');
|
||||||
|
},
|
||||||
emptyContact() {
|
emptyContact() {
|
||||||
return {
|
return {
|
||||||
contactName: '',
|
contactName: '',
|
||||||
@@ -3007,6 +3315,9 @@ export default {
|
|||||||
},
|
},
|
||||||
resetArchive() {
|
resetArchive() {
|
||||||
this.readonly = false;
|
this.readonly = false;
|
||||||
|
this.changeRecordDetailVisible = false;
|
||||||
|
this.changeRecordDetail = null;
|
||||||
|
this.changeRecordDetailRows = [];
|
||||||
this.archiveForm = this.emptyArchive();
|
this.archiveForm = this.emptyArchive();
|
||||||
this.originalAccessType = '';
|
this.originalAccessType = '';
|
||||||
this.qualificationFiles = [];
|
this.qualificationFiles = [];
|
||||||
@@ -4108,6 +4419,42 @@ export default {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.change-record-content-cell {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-record-content-tooltip {
|
||||||
|
max-width: 900px;
|
||||||
|
max-height: 320px;
|
||||||
|
overflow: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.change-record-detail-dialog .el-dialog__body) {
|
||||||
|
max-height: 65vh;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.change-record-detail-dialog .change-record-detail-value .cell) {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
overflow: visible;
|
||||||
|
text-overflow: clip;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-record-detail-meta {
|
||||||
|
display: flex;
|
||||||
|
gap: 32px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.archive-dialog .el-dialog__body) {
|
:deep(.archive-dialog .el-dialog__body) {
|
||||||
max-height: 72vh;
|
max-height: 72vh;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|||||||
Reference in New Issue
Block a user