diff --git a/src/api/business/process-config.js b/src/api/business/process-config.js
index 21a3bc4..a43262c 100644
--- a/src/api/business/process-config.js
+++ b/src/api/business/process-config.js
@@ -1,9 +1,24 @@
+import request from '@/axios';
import { createCrudApi } from './common';
const api = createCrudApi('/blade-transport/process-config');
export const getList = api.getList;
-export const getDetail = api.getDetail;
+export const getDetail = (id, waybillId) =>
+ request({
+ url: '/blade-transport/process-config/detail',
+ method: 'get',
+ params: {
+ id,
+ ...(waybillId === undefined ? {} : { waybillId }),
+ },
+ });
+export const getVoucherImages = waybillId =>
+ request({
+ url: '/blade-transport/process-config/voucher-images',
+ method: 'get',
+ params: { waybillId },
+ });
export const submit = api.submit;
export const remove = api.remove;
export const copy = api.copy;
diff --git a/src/api/business/voucher-manage.js b/src/api/business/voucher-manage.js
index b3c34c3..3f3ccd5 100644
--- a/src/api/business/voucher-manage.js
+++ b/src/api/business/voucher-manage.js
@@ -30,7 +30,7 @@ export const getPartUploadUrl = (id, partNumber) =>
export const updateFileTask = data =>
request({ url: `${fileTaskBaseUrl}/updateFileTask`, method: 'post', data });
export const pauseFileTask = ids =>
- request({ url: `${fileTaskBaseUrl}/updatePaused`, method: 'post', params: { ids: ids.join(',') } });
+ request({ url: `${fileTaskBaseUrl}/updatePaused`, method: 'post', data: { ids } });
export const resumeFileTask = id =>
request({ url: `${fileTaskBaseUrl}/resume`, method: 'post', params: { id } });
export const getFileTaskList = (current, size, params) =>
diff --git a/src/views/business/components/business-crud-page.vue b/src/views/business/components/business-crud-page.vue
index 7a5928c..3f514d6 100644
--- a/src/views/business/components/business-crud-page.vue
+++ b/src/views/business/components/business-crud-page.vue
@@ -797,6 +797,7 @@
placeholder="请输入"
clearable
maxlength="10"
+ inputmode="numeric"
:disabled="dialogReadonly"
@input="handleTaskMileageInput"
/>
@@ -927,6 +928,7 @@
placeholder="请输入"
clearable
maxlength="10"
+ inputmode="numeric"
:disabled="dialogReadonly"
@input="handleTaskMileageInput"
/>
@@ -1347,6 +1349,7 @@
placeholder="请输入里程"
clearable
maxlength="10"
+ inputmode="numeric"
:disabled="dialogReadonly"
@input="handleTaskMileageInput"
/>
@@ -2334,6 +2337,7 @@
v-model="detailBox"
:title="`${config.title}详情`"
append-to-body
+ top="10px"
width="96%"
class="business-crud-page__detail-dialog"
>
@@ -2386,6 +2390,7 @@
v-for="file in attachmentRows"
:key="file.url || file.name || file.originalName"
type="primary"
+ @click="previewAttachment(file)"
>
{{ file.originalName || file.name || '-' }}
@@ -2502,15 +2507,44 @@
-
- {{ node.name || node.nodeName || node.label }}
+
+
+ {{ node.name || node.nodeName || node.label }}
+
+
+
+
-
-
+
+
+
+
@@ -2565,7 +2599,11 @@
- {{ row.originalName || row.name || '-' }}
+
+
+ {{ attachmentName(row) }}
+
+
@@ -2587,6 +2625,38 @@
+
+
+
+
+
+
{
const rowActions = (config.actions || []).filter(action => action !== 'batchComplete');
return 3 + rowActions.length + (config.operations || []).length;
@@ -4859,7 +4943,7 @@ const defaultDispatchRow = () => ({
const taskCarrierTypes = ['承运商', '自运', '网货平台'];
export default {
- components: { WaybillImportDialog, Rank },
+ components: { WaybillImportDialog, Rank, ElImageViewer, OpenFileViewer },
name: 'BusinessCrudPage',
props: {
api: {
@@ -4904,6 +4988,11 @@ export default {
detailLoading: false,
detailRow: {},
waybillDetailProcessNodes: [],
+ waybillProcessDetailTab: 'punch',
+ waybillHasRelatedVoucher: false,
+ waybillVoucherImages: [],
+ waybillVoucherImagesLoading: false,
+ waybillVoucherImagesLoaded: false,
waybillRouteChangeBox: false,
waybillRouteChangeTab: 'change',
waybillRouteChangeRows: [],
@@ -4989,6 +5078,19 @@ export default {
selectedAttachmentRows: [],
contractFileRows: [],
selectedContractFileRows: [],
+ attachmentImagePreviewVisible: false,
+ attachmentImagePreviewUrls: [],
+ attachmentImagePreviewIndex: 0,
+ attachmentDocumentPreviewVisible: false,
+ attachmentPreviewFile: {},
+ attachmentViewerPlugins,
+ attachmentViewerToolbar: {
+ download: true,
+ fullscreen: true,
+ print: true,
+ rotate: true,
+ zoom: true,
+ },
transportCargoRows: [],
taskFreightCurrency: 'CNY',
shippingTemplateFreight: defaultShippingTemplateFreight(),
@@ -5643,6 +5745,9 @@ export default {
}
},
},
+ waybillProcessDetailTab(tab) {
+ if (tab === 'batchSupplement') this.loadWaybillVoucherImages();
+ },
'form.transportType'(value, oldValue) {
if (!this.shippingInfoFormEnabled || this.suppressTransportTypeClear) return;
if (String(value ?? '') === String(oldValue ?? '')) return;
@@ -5990,6 +6095,10 @@ export default {
this.detailBox = true;
this.detailLoading = true;
this.detailRow = { ...row };
+ this.waybillProcessDetailTab = 'punch';
+ this.waybillHasRelatedVoucher = false;
+ this.waybillVoucherImages = [];
+ this.waybillVoucherImagesLoaded = false;
const request =
typeof this.api.getDetail === 'function'
? this.api.getDetail(row.id)
@@ -6039,8 +6148,9 @@ export default {
return projectIds.includes(String(projectId));
});
if (!config?.id) return [];
- return getProcessConfigDetail(config.id).then(detailRes => {
+ return getProcessConfigDetail(config.id, this.detailRow.id).then(detailRes => {
const detail = detailRes?.data?.data || detailRes?.data || config;
+ this.waybillHasRelatedVoucher = Boolean(detail.hasRelatedVoucher);
return this.getProcessConfigEnabledNodes(detail);
});
})
@@ -6052,6 +6162,25 @@ export default {
})
.catch(() => []);
},
+ loadWaybillVoucherImages() {
+ if (!this.detailRow.id || this.waybillVoucherImagesLoading || this.waybillVoucherImagesLoaded) {
+ return;
+ }
+ this.waybillVoucherImagesLoading = true;
+ getProcessConfigVoucherImages(this.detailRow.id)
+ .then(res => {
+ this.waybillVoucherImages = extractRecords(res);
+ this.waybillVoucherImagesLoaded = true;
+ })
+ .finally(() => {
+ this.waybillVoucherImagesLoading = false;
+ });
+ },
+ previewWaybillVoucherImage(index) {
+ this.attachmentImagePreviewUrls = this.waybillVoucherImages.map(image => image.url).filter(Boolean);
+ this.attachmentImagePreviewIndex = index;
+ this.attachmentImagePreviewVisible = this.attachmentImagePreviewUrls.length > 0;
+ },
buildWaybillRouteNodes(rows = []) {
const nodes = [];
rows.forEach((row, rowIndex) => {
@@ -7021,6 +7150,7 @@ export default {
['quantity', 'mileage', 'unitPrice'].forEach(prop => {
if (Number(this.form[prop]) === -1) this.form[prop] = '';
});
+ this.form.mileage = this.normalizeMileageValue(this.form.mileage);
this.$nextTick(() => {
this.suppressTransportTypeClear = false;
});
@@ -7291,6 +7421,7 @@ export default {
});
},
applyShippingPlan(plan = {}) {
+ this.suppressTransportTypeClear = true;
this.form.planId = plan.id || '';
this.form.planName = plan.planName || '';
[
@@ -7301,6 +7432,7 @@ export default {
'transportMode',
'transportTypeName',
'transportModeName',
+ 'departureAddressId',
'departureName',
'departureAddress',
'departureContact',
@@ -7308,6 +7440,7 @@ export default {
'departureProvince',
'departureCity',
'departureDistrict',
+ 'arrivalAddressId',
'arrivalName',
'arrivalAddress',
'arrivalContact',
@@ -7337,11 +7470,24 @@ export default {
].forEach(prop => {
if (firstGoods[prop] !== undefined) this.form[prop] = firstGoods[prop];
});
+ this.form.cargoTypePath = this.normalizeBillingCargoTypePath(firstGoods.cargoTypePath);
+ this.initTaskInfoForm();
+ this.ensureBillingCargoTypeOptions().then(() => {
+ if (this.form.cargoTypePath.length) return;
+ const cargoType = this.findBillingCargoTypeByCodeOrName({
+ cargoTypeCode: this.form.cargoTypeCode,
+ cargoType: this.form.cargoType,
+ });
+ if (cargoType?.path) this.form.cargoTypePath = cargoType.path;
+ });
if (this.isTaskFullMode) {
this.initTaskFullCargoRows(goodsRows);
}
if (this.contractSelectEnabled) this.syncCurrentContractOption();
this.$refs.crud?.validateField?.('planName');
+ this.$nextTick(() => {
+ this.suppressTransportTypeClear = false;
+ });
},
clearShippingPlan() {
this.form.planId = '';
@@ -7417,7 +7563,7 @@ export default {
this.form.quantityUnit =
this.form.quantityUnit || taskInfo.quantityUnit || goodsInfo.quantityUnit || '吨';
const mileage = this.form.mileage || taskInfo.mileage || goodsInfo.mileage || '';
- this.form.mileage = Number(mileage) === -1 ? '' : mileage;
+ this.form.mileage = this.normalizeMileageValue(mileage);
this.form.unitPrice = this.form.unitPrice || taskInfo.unitPrice || goodsInfo.unitPrice || '';
['quantity', 'mileage', 'unitPrice'].forEach(prop => {
if (Number(this.form[prop]) === -1) this.form[prop] = '';
@@ -7702,6 +7848,11 @@ export default {
.replace(/\D/g, '')
.slice(0, 10);
},
+ normalizeMileageValue(value) {
+ if (value === undefined || value === null || value === '' || Number(value) === -1) return '';
+ const mileage = Number(value);
+ return Number.isFinite(mileage) && mileage >= 0 ? String(Math.trunc(mileage)) : '';
+ },
handleDispatchMileageInput(value) {
this.dispatchItemForm.mileage = String(value || '')
.replace(/\D/g, '')
@@ -9015,13 +9166,54 @@ export default {
if (number < 1024 * 1024) return `${(number / 1024).toFixed(1)}KB`;
return `${(number / 1024 / 1024).toFixed(1)}MB`;
},
+ attachmentUrl(row = {}) {
+ return row.url || row.link || row.fileUrl || row.downloadUrl || row.domain || '';
+ },
+ attachmentName(row = {}) {
+ return row.originalName || row.name || row.fileName || '附件';
+ },
+ attachmentExtension(row = {}) {
+ const source = String(this.attachmentName(row) || this.attachmentUrl(row)).split('?')[0];
+ const index = source.lastIndexOf('.');
+ return index > -1 ? source.slice(index + 1).toLowerCase() : '';
+ },
+ isAttachmentImage(row) {
+ return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(this.attachmentExtension(row));
+ },
+ previewAttachment(row) {
+ const url = this.attachmentUrl(row);
+ if (!url) {
+ this.$message.warning('附件地址为空,无法预览');
+ return;
+ }
+ if (this.isAttachmentImage(row)) {
+ this.attachmentImagePreviewUrls = this.attachmentRows
+ .filter(item => this.isAttachmentImage(item) && this.attachmentUrl(item))
+ .map(item => this.attachmentUrl(item));
+ this.attachmentImagePreviewIndex = Math.max(this.attachmentImagePreviewUrls.indexOf(url), 0);
+ this.attachmentImagePreviewVisible = true;
+ return;
+ }
+ this.attachmentPreviewFile = {
+ name: this.attachmentName(row),
+ url,
+ mimeType: row.mimeType || row.contentType || '',
+ };
+ this.attachmentDocumentPreviewVisible = true;
+ },
+ handleAttachmentPreviewUnsupported() {
+ this.$message.warning('当前文件暂不支持在线预览');
+ },
+ handleAttachmentPreviewError() {
+ this.$message.error('附件预览失败');
+ },
downloadAttachment(row) {
- const url = row.url || row.link;
+ const url = this.attachmentUrl(row);
if (!url) {
this.$message.warning('附件地址为空');
return;
}
- downloadFileByUrl(url, row.originalName || row.name || '附件');
+ downloadFileByUrl(url, this.attachmentName(row));
},
handleBatchDownload() {
const rows = this.selectedAttachmentRows.length
@@ -12413,6 +12605,32 @@ export default {
gap: 8px;
}
+ &__waybill-process-actions {
+ display: flex;
+ gap: 8px;
+ }
+
+ &__voucher-image-grid {
+ display: grid;
+ grid-template-columns: repeat(5, minmax(0, 1fr));
+ gap: 8px;
+ min-height: 120px;
+ }
+
+ &__voucher-image-item {
+ aspect-ratio: 1;
+ overflow: hidden;
+ cursor: pointer;
+ border: 1px solid #eff1f7;
+ border-radius: 4px;
+
+ .el-image {
+ display: block;
+ width: 100%;
+ height: 100%;
+ }
+ }
+
&__route-change-dialog {
:deep(.el-dialog__body) {
padding-top: 12px;
diff --git a/src/views/business/components/master-order-detail.vue b/src/views/business/components/master-order-detail.vue
index 8dea2d6..b526abb 100644
--- a/src/views/business/components/master-order-detail.vue
+++ b/src/views/business/components/master-order-detail.vue
@@ -2,14 +2,19 @@
多联总单详情 | {{ master.masterNo }}
{{ statusName(master.businessStatus) }}返回
- - 客户
- {{ master.customerName || '-' }}
- 合同编号
- {{ master.contractNo || '-' }}
- 项目
- {{ master.projectName || '-' }}
- 货物名称
- {{ goodsNames }}
- 货物类型
- {{ goodsTypes }}
- 运输周期
- {{ dateRange }}
+ - 客户
- {{ master.customerName || '-' }}
- 合同编号
- {{ master.contractNo || '-' }}
- 项目
- {{ master.projectName || '-' }}
- 附件
- {{ attachmentName(file) }}-
{{ index ? '经' : '起' }}{{ route.departureName || '-' }}{{ route.departureAddress || '-' }}{{ index ? `已到达 ${quantity(segments[index - 1].arrivedQuantity)}/${quantity(master.totalQuantity)}` : `已调度 ${quantity(route.dispatchedQuantity)}/${quantity(master.totalQuantity)}` }}已调度 {{ quantity(route.dispatchedQuantity) }}/{{ quantity(master.totalQuantity) }}
终{{ route.arrivalName || '-' }}{{ route.arrivalAddress || '-' }}已到达 {{ quantity(route.arrivedQuantity) }}/{{ quantity(master.totalQuantity) }}
+
+
+
+
+
分段执行明细
- {{ segmentNumber(route, index) }}{{ route.departureName || '-' }} → {{ route.arrivalName || '-' }}
+ {{ segmentNumber(route, index) }}{{ segmentTransportType(route) || '-' }}{{ route.departureName || '-' }} → {{ route.arrivalName || '-' }}已调度 {{ quantity(route.dispatchedQuantity) }} / {{ quantity(plannedQuantity(route)) }} {{ plannedUnit(route) }}
总量{{ quantity(master.totalQuantity) }}吨
已调度{{ quantity(route.dispatchedQuantity) }}吨
剩余{{ quantity(remainingQuantity(route)) }}吨
{{ row.waybillNo }}{{ driverVehicle(row) }}{{ quantity(row.quantity) }}{{ waybillStatusName(row.businessStatus) }}
@@ -29,11 +34,19 @@