From eb8f4778a5c94e393c8ef69e4abb52e8a1d0f398 Mon Sep 17 00:00:00 2001 From: Timi Date: Sat, 5 Sep 2026 12:12:26 +0800 Subject: [PATCH] fix OrderFulfillmentCreate.vue style --- .../fulfillment/OrderFulfillmentCreate.vue | 263 +++++++++++++++--- src/types/Package.ts | 1 + src/types/SalesOrder.ts | 1 + src/utils/product.ts | 2 +- 4 files changed, 225 insertions(+), 42 deletions(-) diff --git a/src/pages/fulfillment/OrderFulfillmentCreate.vue b/src/pages/fulfillment/OrderFulfillmentCreate.vue index 9d1bc40..d3d7cc7 100644 --- a/src/pages/fulfillment/OrderFulfillmentCreate.vue +++ b/src/pages/fulfillment/OrderFulfillmentCreate.vue @@ -5,27 +5,78 @@ - - - - - - - + + +
+
+ +
+
+
+
+ + +
+
+ + + + 全部履约 +
+
+
+ +
- - + + + + + + + + + - @@ -65,6 +116,7 @@ import { getErrorMessage } from "@/utils/error"; import { Message } from "@/utils/message"; import { permissionCodes } from "@/utils/permission"; import { uploadPhotoList } from "@/utils/photo"; +import { getProductPhotoURL } from "@/utils/product"; type SourceItem = SalesOrderItem | CustomerPackageItem; defineOptions({ name: "OrderFulfillmentCreate" }); @@ -87,6 +139,7 @@ const orderLoading = ref(false); const orderOptions = ref([]); const isPackage = computed(() => !!customerPackage.value); const sourceItems = computed(() => customerPackage.value?.itemList || order.value?.itemList || []); + async function loadData(): Promise { const salesOrderId = String(route.query.salesOrderId || ""); const customerPackageId = String(route.query.customerPackageId || ""); @@ -94,34 +147,71 @@ async function loadData(): Promise { try { order.value = await SalesOrderAPI.detail(salesOrderId); if (customerPackageId) customerPackage.value = await PackageAPI.customerPackageDetail(customerPackageId); - } catch (error) { Message.error(getErrorMessage(error, "履约来源加载失败")); } + } catch (error) { + Message.error(getErrorMessage(error, "履约来源加载失败")); + } } -function getItemTitle(item: SourceItem): string { return `${item.productNameSnapshot || "未命名产品"} · ${item.unitSnapshot || "未设置单位"}`; } -function getItemDescription(item: SourceItem): string { + +function getAvailableQuantity(item: SourceItem): string { if (isPackage.value) { - return `本次可履约 ${getPackageQuantityText((item as CustomerPackageItem).availableQuantity)}`; + return getPackageQuantityText((item as CustomerPackageItem).availableQuantity); } const orderItem = item as SalesOrderItem; const availableQuantity = Math.max(0, Number(orderItem.quantity || 0) - Number(orderItem.reservedQuantity || 0) - Number(orderItem.fulfilledQuantity || 0)); - return `本次可履约 ${getPackageQuantityText(availableQuantity.toFixed(3))}`; + return getPackageQuantityText(availableQuantity.toFixed(3)); } -function confirmTime(value: string | number): void { deliveredAtText.value = String(value); pickerVisible.value = false; } + +function getItemDescription(item: SourceItem): string { + return ` · 剩余 ${getAvailableQuantity(item)}`; +} + +function fillAllQuantity(item: SourceItem): void { + if (item.id) { + quantityMap[item.id] = getAvailableQuantity(item); + } +} + +function confirmTime(value: string | number): void { + deliveredAtText.value = String(value); + pickerVisible.value = false; +} + function resolveTimestamp(value: string): number | undefined { if (!value) return undefined; const timestamp = new Date(value.replace(" ", "T")).getTime(); return Number.isFinite(timestamp) ? timestamp : undefined; } + function buildAttachments(): Partial[] { - return photoItems.value.flatMap(item => item.tempFileId ? [{ tempFileId: item.tempFileId, attachType: "PHOTO" }] : []); + return photoItems.value.flatMap(item => item.tempFileId ? [{ + tempFileId: item.tempFileId, + attachType: "PHOTO" + }] : []); } + function validate(): boolean { - if (!order.value?.id) { Message.warning("请先从订单详情进入履约创建"); return false; } - if (!buildAttachments().length) { Message.warning("履约必须上传至少一张照片"); return false; } + if (!order.value?.id) { + Message.warning("请先从订单详情进入履约创建"); + return false; + } + if (!buildAttachments().length) { + Message.warning("履约必须上传至少一张照片"); + return false; + } const selected = sourceItems.value.filter(item => !!item.id && quantityMap[item.id]); - if (!selected.length) { Message.warning("请填写至少一项履约数量"); return false; } - for (const item of selected) { if (!normalizePackageQuantity(quantityMap[item.id || ""])) { Message.warning("履约数量必须是正数,最多三位小数"); return false; } } + if (!selected.length) { + Message.warning("请填写至少一项履约数量"); + return false; + } + for (const item of selected) { + if (!normalizePackageQuantity(quantityMap[item.id || ""])) { + Message.warning("履约数量必须是正数,最多三位小数"); + return false; + } + } return true; } + async function save(): Promise { if (isSaving.value || !validate()) return; if (!requestNo.value) requestNo.value = createPackageRequestNo(); @@ -141,12 +231,21 @@ async function save(): Promise { attachmentList: buildAttachments(), remark: remark.value.trim() || null }); - Message.success("履约单已提交"); await router.replace(`/management/order-fulfillments/${result.id}`); - } catch (error) { Message.error(getErrorMessage(error, "履约单创建失败,重试会复用同一请求号")); } finally { isSaving.value = false; } + Message.success("履约单已提交"); + await router.replace(`/management/order-fulfillments/${result.id}`); + } catch (error) { + Message.error(getErrorMessage(error, "履约单创建失败,重试会复用同一请求号")); + } finally { + isSaving.value = false; + } } + async function openOrderPicker(): Promise { if (order.value?.id) { - await router.push(`/management/sales-orders/${order.value.id}`); + await router.push({ + path: `/management/sales-orders/${order.value.id}`, + query: { transitionDepth: String(getCurrentRouteDepth() + 1) } + }); return; } orderPickerVisible.value = true; @@ -155,9 +254,13 @@ async function openOrderPicker(): Promise { try { const result = await SalesOrderAPI.list({ index: 0, size: 100, orderType: "DIRECT_PRODUCT", status: "ACTIVE" }); orderOptions.value = result.list || []; - } catch (error) { Message.error(getErrorMessage(error, "可履约订单加载失败")); } - finally { orderLoading.value = false; } + } catch (error) { + Message.error(getErrorMessage(error, "可履约订单加载失败")); + } finally { + orderLoading.value = false; + } } + async function selectOrder(item: SalesOrder): Promise { if (!item.id) return; order.value = item; @@ -165,16 +268,94 @@ async function selectOrder(item: SalesOrder): Promise { for (const key of Object.keys(quantityMap)) delete quantityMap[key]; await router.replace({ query: { salesOrderId: item.id } }); } -onMounted(async () => { navBarStore.setRightText("提交履约", "var(--td-brand-color)", save); await loadData(); }); + +function getCurrentRouteDepth(): number { + const transitionDepth = Array.isArray(route.query.transitionDepth) + ? route.query.transitionDepth[0] + : route.query.transitionDepth; + const depth = Number(transitionDepth); + if (transitionDepth && Number.isInteger(depth) && 0 <= depth && depth <= 20) { + return depth; + } + return Number(route.meta.depth ?? 0); +} + +onMounted(async () => { + navBarStore.setRightText("提交", "var(--td-brand-color)", save); + await loadData(); +}); onUnmounted(() => navBarStore.clearRight()); diff --git a/src/types/Package.ts b/src/types/Package.ts index 6d966af..6cd7580 100644 --- a/src/types/Package.ts +++ b/src/types/Package.ts @@ -36,6 +36,7 @@ export type CustomerPackageItem = { customerPackageId?: string; productId?: string; productUnitId?: string; + attachmentList?: Attachment[]; productNameSnapshot?: string; unitSnapshot?: string; baseQuantityPerUnitSnapshot?: number | string; diff --git a/src/types/SalesOrder.ts b/src/types/SalesOrder.ts index 29c97b5..d32ffce 100644 --- a/src/types/SalesOrder.ts +++ b/src/types/SalesOrder.ts @@ -14,6 +14,7 @@ export type SalesOrderItem = { salesOrderId?: string; productId?: string; productUnitId?: string; + attachmentList?: Attachment[]; productNameSnapshot?: string; unitSnapshot?: string; baseQuantityPerUnitSnapshot?: number | string; diff --git a/src/utils/product.ts b/src/utils/product.ts index 508fe5f..2b7cc34 100644 --- a/src/utils/product.ts +++ b/src/utils/product.ts @@ -78,7 +78,7 @@ export function getStockOperationText(type?: StockOperationType): string { } as Record)[type || ""] || "未知操作"; } -export function getProductPhotoURL(item: Product): string { +export function getProductPhotoURL(item: Pick): string { const attachment = item.attachmentList?.find(item => item.attachType === "PHOTO" && !!item.id); return attachment?.id ? getAttachmentReadURL(attachment.id, 128) : ""; }