Merge remote-tracking branch 'origin/master'

This commit is contained in:
2026-08-14 16:01:17 +08:00
10 changed files with 1611 additions and 517 deletions
File diff suppressed because it is too large Load Diff
@@ -127,7 +127,7 @@
<el-row :gutter="16">
<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 :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="车牌号" 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>
@@ -527,7 +527,11 @@ export default {
this.carrierLoading = true;
try {
this.carrierOptions = unwrapRecords(
await getCustomerList(1, 20, { customerName: keyword, customerType: '承运商' })
await getCustomerList(1, 20, {
customerName: keyword,
customerType: '承运商',
status: 1,
})
);
} finally {
this.carrierLoading = false;
@@ -541,6 +545,23 @@ export default {
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) {
if (element) this.driverInputs[segmentNo] = element;
else delete this.driverInputs[segmentNo];
@@ -137,21 +137,22 @@
label="货物名称"
min-width="130"
><template #default="{ row }"
><el-select
><el-autocomplete
v-model="row.cargoName"
filterable
allow-create
default-first-option
clearable
placeholder="请选择或输入"
placeholder="请输入货物名称"
:debounce="300"
:fetch-suggestions="
(queryString, callback) => fetchCargoSuggestions(queryString, callback, row.cargoTypePath)
"
:loading="isCargoOptionsLoading(row.cargoTypePath)"
@visible-change="visible => visible && loadCargoOptionsByType(row.cargoTypePath)"
@change="selectCargoName(row, $event)"
><el-option
v-for="cargo in getCargoOptionsByType(row.cargoTypePath)"
:key="cargo.id"
:label="cargo.cargoName"
:value="cargo.cargoName" /></el-select
@select="cargo => selectCargoSuggestion(row, cargo)"
><template #default="{ item }">
<div class="master-editor__cargo-autocomplete-item">
<span>{{ item.cargoName || item.name || '' }}</span>
<small v-if="item.cargoCode">{{ item.cargoCode }}</small>
</div>
</template></el-autocomplete
></template></el-table-column
><el-table-column label="货物类型" min-width="130"
><template #default="{ row }"
@@ -1062,6 +1063,45 @@ export default {
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) {
if (!cargoName) return;
const cargo = this.getCargoOptionsByType(goods.cargoTypePath).find(
+54 -37
View File
@@ -583,6 +583,7 @@
clearable
filterable
placeholder="请选择"
:disabled="dialogMode === 'add'"
@visible-change="visible => visible && loadTransportTypeOptions()"
>
<el-option
@@ -825,27 +826,15 @@
</el-select>
</el-form-item>
<el-form-item label="司机" :required="['自运', '网货平台'].includes(dialogForm.carrierType)">
<el-select
<el-autocomplete
v-model="dialogForm.driverName"
clearable
filterable
allow-create
default-first-option
remote
reserve-keyword
:remote-method="loadDriverOptions"
:debounce="300"
:fetch-suggestions="fetchDriverSuggestions"
:loading="driverLoading"
placeholder="请输入"
@change="handleDriverChange"
@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>
@select="item => handleDriverSuggestionSelect('dialog', item)"
/>
</el-form-item>
<el-form-item label="手机号" :required="['自运', '网货平台'].includes(dialogForm.carrierType)">
<el-input v-model="dialogForm.driverPhone" clearable placeholder="请输入" />
@@ -853,13 +842,13 @@
<el-form-item label="车牌号" required>
<el-input v-model="dialogForm.vehicleNo" clearable placeholder="请输入" />
</el-form-item>
<el-form-item label="挂车车牌号">
<el-form-item v-if="dialogForm.carrierType !== '承运商'" label="挂车车牌号">
<el-input v-model="dialogForm.trailerVehicleNo" clearable placeholder="请输入" />
</el-form-item>
<el-form-item label="押运人">
<el-form-item v-if="dialogForm.carrierType !== '承运商'" label="押运人">
<el-input v-model="dialogForm.escortName" clearable placeholder="请输入" />
</el-form-item>
<el-form-item label="押运人手机号">
<el-form-item v-if="dialogForm.carrierType !== '承运商'" label="押运人手机号">
<el-input v-model="dialogForm.escortPhone" clearable placeholder="请输入" />
</el-form-item>
<el-form-item label="里程(km)">
@@ -1047,7 +1036,7 @@ const createDialogForm = () => ({
batchNo: '',
currentProcessNode: '接单',
mileage: '',
estimatedStartDate: dayjs().format('YYYY-MM-DD'),
estimatedStartDate: '',
estimatedEndDate: '',
taskRemark: '',
goodsJson: '',
@@ -1207,6 +1196,13 @@ export default {
mounted() {
this.loadTransportTypeOptions();
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: {
buildQueryParams(form) {
@@ -1280,11 +1276,11 @@ export default {
findWaybillId(row, waybillNo) {
const rows = this.splitText(row.loadingSubNos);
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) {
return linkedRows[index].id || linkedRows[index].waybillId;
}
const ids = this.parseJsonArray(row.waybillIdsJson);
const ids = parseJsonArray(row.waybillIdsJson);
return ids[index] || '';
},
async openWaybillDetail(row = {}) {
@@ -1415,6 +1411,7 @@ export default {
this.dialogVisible = true;
this.dialogLoading = true;
this.resetDialogData();
if (mode === 'add') this.candidateQuery.transportType = 'road';
try {
if (row?.id) {
const res = await loadingApi.getDetail(row.id);
@@ -1435,6 +1432,7 @@ export default {
} else {
this.dialogForm = createDialogForm();
this.candidateQuery = createCandidateSearchForm();
this.candidateQuery.transportType = 'road';
await this.loadCandidateWaybills();
}
} finally {
@@ -1465,6 +1463,7 @@ export default {
},
clearLoadingDialog() {
this.resetDialogData();
if (this.dialogMode === 'add') this.candidateQuery.transportType = 'road';
this.loadCandidateWaybills();
},
restoreRouteAndCargo() {
@@ -1719,6 +1718,7 @@ export default {
...this.buildQueryParams(this.candidateQuery),
businessStatus: 'pending',
onlyUnassignedLoading: 1,
...(this.dialogMode === 'add' ? { transportType: 'road' } : {}),
}
);
const page = unwrapPage(res);
@@ -1738,6 +1738,7 @@ export default {
},
handleCandidateReset() {
this.candidateQuery = createCandidateSearchForm();
if (this.dialogMode === 'add') this.candidateQuery.transportType = 'road';
this.candidatePage.currentPage = 1;
this.loadCandidateWaybills();
},
@@ -1805,18 +1806,6 @@ export default {
if (!this.dialogForm.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) {
this.routeNodes = this.buildRouteNodes(rows);
}
@@ -2079,7 +2068,11 @@ export default {
});
},
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 = '';
}
},
@@ -2089,6 +2082,26 @@ export default {
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) {
this.dialogForm.mileage = String(value || '').replace(/[^\d.]/g, '');
},
@@ -2147,7 +2160,11 @@ export default {
this.carrierLoading = true;
try {
this.carrierOptions = unwrapRecords(
await getCustomerList(1, 20, { customerName: keyword })
await getCustomerList(1, 20, {
customerName: keyword,
customerType: '承运商',
status: 1,
})
);
} finally {
this.carrierLoading = false;
+280 -228
View File
@@ -8,6 +8,10 @@
v-model="form"
ref="crud"
:permission="permissionList"
:before-open="beforeOpen"
@row-save="rowSave"
@row-update="rowUpdate"
@row-del="rowDel"
@search-change="searchChange"
@search-reset="searchReset"
@selection-change="selectionChange"
@@ -21,7 +25,7 @@
type="primary"
icon="el-icon-plus"
v-if="hasPermission('process_config_add')"
@click="openProcessConfig()"
@click="$refs.crud.rowAdd()"
>新建过程配置
</el-button>
<el-button
@@ -42,6 +46,27 @@
</el-button>
</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 }">
<el-tag :type="statusTagType(row.status)">
{{ row.statusName || displayStatus(row.status) }}
@@ -54,154 +79,101 @@
</span>
</template>
<template #menu="{ row, index }">
<el-link
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
ref="processForm"
:model="form"
label-position="right"
label-width="calc(6em + 24px)"
:disabled="dialogReadonly"
class="process-form dialog-form-label-fixed"
>
<template #basicInfo-form>
<section-card title="基本信息">
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="配置名称" required>
<el-input
v-model="form.configName"
:disabled="dialogReadonly"
maxlength="100"
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="项目" required>
<el-select
v-model="selectedProjectId"
clearable
filterable
remote
:remote-method="loadProjectOptions"
:loading="projectLoading"
:disabled="dialogReadonly"
placeholder="请选择"
style="width: 100%"
@change="handleProjectChange"
>
<el-option
v-for="item in projectOptions"
:key="item.id"
:label="formatProjectLabel(item)"
:value="String(item.id)"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="运输完成时限">
<div class="process-config-page__finish-days">
<span>默认</span>
<el-form
:model="form"
label-position="right"
label-width="calc(6em + 24px)"
>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="配置名称" required>
<el-input
v-model="form.defaultFinishDays"
v-model="form.configName"
:disabled="dialogReadonly"
maxlength="3"
maxlength="100"
placeholder="请输入"
@input="handleFinishDaysInput"
/>
<span>天后完成运输</span>
</div>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注">
<el-input
v-model="form.remark"
type="textarea"
:rows="4"
:disabled="dialogReadonly"
maxlength="500"
show-word-limit
placeholder="请输入"
/>
</el-form-item>
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="项目" required>
<el-select
v-model="selectedProjectId"
clearable
filterable
remote
:remote-method="loadProjectOptions"
:loading="projectLoading"
:disabled="dialogReadonly"
placeholder="请选择"
style="width: 100%"
@change="handleProjectChange"
>
<el-option
v-for="item in projectOptions"
:key="item.id"
:label="formatProjectLabel(item)"
:value="String(item.id)"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="运输完成时限">
<div class="process-config-page__finish-days">
<span>默认</span>
<el-input
v-model="form.defaultFinishDays"
:disabled="dialogReadonly"
maxlength="3"
placeholder="请输入"
@input="handleFinishDaysInput"
/>
<span>天后完成运输</span>
</div>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="备注">
<el-input
v-model="form.remark"
type="textarea"
:rows="4"
:disabled="dialogReadonly"
maxlength="500"
show-word-limit
placeholder="请输入"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
</section-card>
</template>
<section-card title="现行过程节点">
<div class="process-config-page__timeline" v-if="enabledNodes.length">
<div class="process-config-page__timeline-line"></div>
<div
v-for="node in enabledNodes"
:key="node.key"
class="process-config-page__timeline-item"
>
<span class="process-config-page__timeline-dot"></span>
<div class="process-config-page__timeline-name">{{ node.name }}</div>
<div class="process-config-page__timeline-desc">
<div v-for="text in timelineDescriptions(node)" :key="text">{{ text }}</div>
<template #nodeConfigJson-form>
<div class="process-config-page__custom-form">
<section-card title="现行过程节点">
<div class="process-config-page__timeline" v-if="enabledNodes.length">
<div class="process-config-page__timeline-line"></div>
<div
v-for="node in enabledNodes"
:key="node.key"
class="process-config-page__timeline-item"
>
<span class="process-config-page__timeline-dot"></span>
<div class="process-config-page__timeline-name">{{ node.name }}</div>
<div class="process-config-page__timeline-desc">
<div v-for="text in timelineDescriptions(node)" :key="text">{{ text }}</div>
</div>
</div>
</div>
</div>
<el-empty v-else description="请在节点设置中勾选过程节点" :image-size="72" />
</section-card>
<el-empty v-else description="请在节点设置中勾选过程节点" :image-size="72" />
</section-card>
<section-card title="节点设置">
<section-card title="节点设置">
<el-table
class="process-config-page__node-table"
border
@@ -385,21 +357,55 @@
</template>
</el-table-column>
</el-table>
</section-card>
</el-form>
<template #footer>
<template v-if="!dialogReadonly">
<el-button plain :loading="submitting" :disabled="submitting" @click="submitProcessConfig('draft')">
保存草稿
</el-button>
<el-button type="primary" :loading="submitting" :disabled="submitting" @click="submitProcessConfig('enabled')">
保存并启用
</el-button>
</template>
<el-button @click="processBox = false">{{ dialogReadonly ? '关闭' : '取消' }}</el-button>
</section-card>
</div>
</template>
</el-dialog>
<template #menu="{ row, index }">
<el-link
type="primary"
v-if="hasPermission('process_config_view')"
@click="$refs.crud.rowView(row, index)"
>
查看
</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>
</avue-crud>
<empty-pagination
:page="page"
@@ -513,8 +519,6 @@ export default {
},
selectionList: [],
submitStatus: 1,
processBox: false,
submitting: false,
};
},
computed: {
@@ -536,10 +540,6 @@ export default {
enabledNodes() {
return this.nodeRows.filter(item => item.enabled);
},
dialogTitle() {
if (this.dialogReadonly) return '查看过程配置';
return this.form.id ? '编辑过程配置' : '新增过程配置';
},
},
created() {
this.resetNodeRows();
@@ -730,6 +730,33 @@ export default {
this.form.projectIds = project ? String(project.id) : '';
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) {
this.form.defaultFinishDays = String(value || '')
.replace(/\D/g, '')
@@ -853,6 +880,11 @@ export default {
}
return true;
},
stopSubmitLoading(loading) {
if (typeof loading === 'function') {
loading();
}
},
needsDuplicateVoucherConfirm() {
const signNode = this.nodeRows.find(item => item.key === 'sign');
const returnNode = this.nodeRows.find(item => item.key === 'return');
@@ -882,74 +914,50 @@ export default {
}
);
},
openProcessConfig(row, readonly = false) {
this.dialogReadonly = readonly;
submitRow(row, done, loading, status = this.submitStatus) {
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;
});
this.syncNodeFields();
const submitRow = this.normalizeRow({ ...row, status });
if (!this.validateRow(submitRow)) {
this.stopSubmitLoading(loading);
return;
}
this.form = {
configName: '',
projectIds: '',
projectNames: '',
defaultFinishDays: '',
remark: '',
status: 0,
};
this.selectedProjectId = '';
this.resetNodeRows();
this.processBox = true;
this.checkEnabledProjectBeforeSubmit(submitRow)
.then(exists => {
if (exists) {
this.$message.warning('该项目已有启用状态的过程配置,不能重复添加');
throw { code: 'enabled-project-config' };
}
})
.then(() => this.confirmDuplicateVoucher())
.then(() => api.submit(submitRow))
.then(() => {
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
done();
})
.catch(error => {
if (
error !== 'cancel' &&
error !== 'close' &&
error?.code !== 'enabled-project-config'
) {
window.console.log(error);
}
this.stopSubmitLoading(loading);
});
},
submitProcessConfig(mode) {
this.$refs.processForm.validate(valid => {
if (!valid) return;
this.submitting = true;
this.submitStatus = mode === 'draft' ? 2 : 1;
this.syncNodeFields();
const submitRow = this.normalizeRow({ ...this.form, status: this.submitStatus });
if (!this.validateRow(submitRow)) {
this.submitting = false;
return;
}
this.confirmDuplicateVoucher()
.then(() => api.submit(submitRow))
.then(() => {
this.submitting = false;
this.processBox = false;
this.onLoad(this.page);
this.$message({ type: 'success', message: '操作成功!' });
})
.catch(error => {
this.submitting = false;
if (error !== 'cancel' && error !== 'close') {
window.console.log(error);
}
});
});
rowSave(row, done, loading) {
this.submitRow(row, done, loading);
},
resetProcess() {
this.dialogReadonly = false;
this.submitting = false;
this.submitStatus = 1;
this.form = {
configName: '',
projectIds: '',
projectNames: '',
defaultFinishDays: '',
remark: '',
status: 0,
};
this.selectedProjectId = '';
this.resetNodeRows();
this.$refs.processForm?.clearValidate();
rowUpdate(row, index, done, loading) {
this.submitRow(row, done, loading);
},
handleProcessConfigSave(mode) {
this.submitStatus = mode === 'draft' ? 2 : 1;
if (this.$refs.crud && typeof this.$refs.crud.rowSave === 'function') {
this.$refs.crud.rowSave();
}
},
rowDel(row) {
this.$confirm('确定将选择数据删除?', {
@@ -993,6 +1001,35 @@ export default {
}
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() {
this.query = {};
this.applyDefaultDeptSearch();
@@ -1090,8 +1127,28 @@ export default {
cancelButtonText: '取消',
type: 'warning',
})
.then(() => api[action](row.id))
.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.onLoad(this.page, this.query);
});
@@ -1240,10 +1297,5 @@ export default {
:deep(.el-table__body tr:nth-child(even) > td.el-table__cell) {
background: #fafafa;
}
:deep(.process-dialog .el-dialog__body) {
max-height: 72vh;
overflow: auto;
}
}
</style>
+6 -1
View File
@@ -823,7 +823,12 @@ const loadProgress = async () => {
businessType: 'voucher',
});
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;
};
const searchProgress = () => {