From 7e6e0f9f8c153bc24fe0d30da7581e0a3ced74ff Mon Sep 17 00:00:00 2001 From: Timi Date: Sat, 5 Sep 2026 21:03:19 +0800 Subject: [PATCH] edit remark and photo in SalesOrderDetail.vue --- src/api/SalesOrderAPI.ts | 8 +- src/pages/salesOrder/SalesOrderDetail.vue | 174 ++++++++++++++++++---- src/types/SalesOrder.ts | 6 + src/utils/permission.ts | 2 + 4 files changed, 162 insertions(+), 28 deletions(-) diff --git a/src/api/SalesOrderAPI.ts b/src/api/SalesOrderAPI.ts index c7c264c..35f3b4b 100644 --- a/src/api/SalesOrderAPI.ts +++ b/src/api/SalesOrderAPI.ts @@ -12,7 +12,8 @@ import type { SalesOrder, SalesOrderCancelRequest, SalesOrderPage, - SalesOrderPageResult + SalesOrderPageResult, + SalesOrderUpdateRequest } from "@/types/SalesOrder"; const ORDER_URI = "/gao/sales/order"; @@ -27,6 +28,9 @@ async function detail(id: string): Promise { async function createDirectProduct(request: DirectProductOrderCreateRequest): Promise { return await axios.post(`${ORDER_URI}/direct-product/create`, withCurrentStoreId(request)); } +async function update(request: SalesOrderUpdateRequest): Promise { + return await axios.post(`${ORDER_URI}/update`, request); +} async function cancel(request: SalesOrderCancelRequest): Promise { return await axios.post(`${ORDER_URI}/cancel`, request); } @@ -61,4 +65,4 @@ async function fundRecordRevoke(request: OrderPaymentRevokeRequest): Promise - - - -
- -
-
+ + + 查看来源记录 + + + + + + +
+ +
+
+ +
+ 编辑 + +
+
+
import { PassTimeLabel, Text, Time } from "timi-web"; -import { useUserStore } from "timi-web-api"; +import { type Attachment, useUserStore } from "timi-web-api"; import SalesOrderAPI from "@/api/SalesOrderAPI"; import CustomerProfileCard from "@/components/CustomerProfileCard.vue"; import PhotoScroller from "@/components/PhotoScroller.vue"; @@ -144,6 +179,7 @@ import { } from "@/utils/salesOrder"; import { Message } from "@/utils/message"; import { permissionCodes } from "@/utils/permission"; +import { uploadPhotoList } from "@/utils/photo"; defineOptions({ name: "SalesOrderDetail" }); const route = useRoute(); @@ -152,7 +188,12 @@ const navBarStore = useNavBarStore(); const userStore = useUserStore(); const order = ref(); const loading = ref(false); +const isEditing = ref(false); +const isSaving = ref(false); +const editRemark = ref(""); +const photoItems = ref([]); const canReadFund = computed(() => userStore.hasPermissions(permissionCodes.fundRecord.read)); +const canUpdate = computed(() => userStore.hasPermissions(permissionCodes.salesOrder.update)); const canCreateFulfillment = computed(() => userStore.hasPermissions(permissionCodes.orderFulfillment.create)); const canOperateStock = computed(() => userStore.hasPermissions(permissionCodes.productStock.create)); const canReceive = computed(() => !!order.value && order.value.status !== "CANCELLED" && order.value.paymentStatus !== "PAID" && userStore.hasPermissions(permissionCodes.fundReceive.create)); @@ -160,16 +201,29 @@ const canRefund = computed(() => !!order.value && order.value.status === "CANCEL const canConfirmNoRefund = computed(() => !!order.value && order.value.status === "CANCELLED" && order.value.refundStatus === "PENDING" && userStore.hasPermissions(permissionCodes.fund.refund)); const canCancel = computed(() => !!order.value && order.value.status === "ACTIVE" && userStore.hasPermissions(permissionCodes.salesOrder.cancel)); const canFulfillByPayment = computed(() => order.value?.paymentStatus === "PAID" || (order.value?.paymentStatus === "PARTIALLY_PAID" && canOperateStock.value)); -const photoItems = computed(() => (order.value?.attachmentList || []) - .filter(item => !!item.id) - .map(item => ({ - attachment: item, - attachType: item.attachType || "PHOTO", - previewUrl: getAttachmentReadURL(item.id || ""), - url: getAttachmentReadURL(item.id || "", 90) - }))); const photoURLList = computed(() => photoItems.value.map(item => item.url)); +function toPhotoItems(attachmentList?: readonly Partial[]): PhotoItem[] { + return (attachmentList || []) + .filter(item => !!item.id) + .map(item => ({ + attachment: item, + attachType: item.attachType || "PHOTO", + previewUrl: getAttachmentReadURL(item.id || ""), + url: getAttachmentReadURL(item.id || "", 90) + })); +} + +function buildAttachmentList(): Partial[] { + return photoItems.value.flatMap((item): Partial[] => { + const attachmentId = item.attachment?.id; + if (attachmentId) { + return [{ id: attachmentId, attachType: item.attachType || item.attachment?.attachType || "PHOTO" }]; + } + return item.tempFileId ? [{ tempFileId: item.tempFileId, attachType: item.attachType || "PHOTO" }] : []; + }); +} + function getPaidAtText(paidAt?: number | null): string { return paidAt ? Time.toDateTime(paidAt) : "待后端回填"; } @@ -232,10 +286,18 @@ function getItemFulfillmentStatus(item: SalesOrderItem): { text: string; theme: async function loadData(): Promise { const id = String(route.params.id || ""); - if (!id) return; + isEditing.value = false; + navBarStore.clearRight(); + if (!id) { + order.value = undefined; + photoItems.value = []; + return; + } loading.value = true; try { order.value = await SalesOrderAPI.detail(id); + editRemark.value = order.value.remark || ""; + photoItems.value = toPhotoItems(order.value.attachmentList); } catch (error) { Message.error(getErrorMessage(error, "订单加载失败")); } finally { @@ -243,6 +305,42 @@ async function loadData(): Promise { } } +function toEdit(): void { + if (!order.value || !canUpdate.value) return; + editRemark.value = order.value.remark || ""; + photoItems.value = toPhotoItems(order.value.attachmentList); + isEditing.value = true; +} + +function cancelEdit(): void { + if (isSaving.value) return; + editRemark.value = order.value?.remark || ""; + photoItems.value = toPhotoItems(order.value?.attachmentList); + isEditing.value = false; +} + +async function saveEdit(): Promise { + const orderId = order.value?.id; + if (!orderId || !canUpdate.value || !isEditing.value || isSaving.value) return; + isSaving.value = true; + try { + const result = await SalesOrderAPI.update({ + id: orderId, + attachmentList: buildAttachmentList(), + remark: editRemark.value.trim() || null + }); + order.value = result; + editRemark.value = result.remark || ""; + photoItems.value = toPhotoItems(result.attachmentList); + isEditing.value = false; + Message.success("订单信息已保存"); + } catch (error) { + Message.error(getErrorMessage(error, "订单信息保存失败")); + } finally { + isSaving.value = false; + } +} + async function toEventRecord(): Promise { if (order.value?.sourceEventRecord?.id) await router.push(`/management/registration-event-records/${order.value.sourceEventRecord.id}`); } @@ -302,6 +400,7 @@ function getCurrentRouteDepth(): number { } onMounted(loadData); +watch(() => route.params.id, loadData); onUnmounted(() => navBarStore.clearRight()); @@ -365,9 +464,32 @@ onUnmounted(() => navBarStore.clearRight()); } .info { + margin: 0 1rem; + padding: .5rem 0; + overflow: hidden; + background: var(--td-bg-color-container, #fff); + box-shadow: var(--td-shadow-1); + border-radius: 1.25rem; + + .form-title { + padding: .75rem 1rem .5rem; + font-size: .875rem; + font-weight: 700; + } .photo-wrap { - padding: .75rem; + flex: 1; + } + + .edit-actions { + gap: .5rem; + flex: 1; + display: flex; + justify-content: flex-end; + } + + .remark-editor { + width: 100%; } } diff --git a/src/types/SalesOrder.ts b/src/types/SalesOrder.ts index 8889051..0207fad 100644 --- a/src/types/SalesOrder.ts +++ b/src/types/SalesOrder.ts @@ -93,6 +93,12 @@ export type DirectProductOrderCreateRequest = { remark?: string | null; }; +export type SalesOrderUpdateRequest = { + id: string; + attachmentList?: Partial[]; + remark?: string | null; +}; + export type OrderPaymentReceiveRequest = { salesOrderId: string; requestNo: string; diff --git a/src/utils/permission.ts b/src/utils/permission.ts index d225732..f03c8e3 100644 --- a/src/utils/permission.ts +++ b/src/utils/permission.ts @@ -75,6 +75,7 @@ export const permissionCodes = { salesOrder: { create: "GAO:SALES_ORDER:CREATE", read: "GAO:SALES_ORDER:READ", + update: "GAO:SALES_ORDER:UPDATE", cancel: "GAO:SALES_ORDER:CANCEL", priceOverride: "GAO:SALES_ORDER:PRICE_OVERRIDE" }, @@ -208,6 +209,7 @@ export const gaoStoreScopePermissionCodes = [ permissionCodes.customerPackage.priceOverride, permissionCodes.salesOrder.create, permissionCodes.salesOrder.read, + permissionCodes.salesOrder.update, permissionCodes.salesOrder.cancel, permissionCodes.salesOrder.priceOverride, permissionCodes.orderFulfillment.create,