From e1a3df43a9e2d079d87f7855db7772fc671e9c8c Mon Sep 17 00:00:00 2001 From: Timi Date: Sun, 30 Aug 2026 01:25:10 +0800 Subject: [PATCH] fix require photo event --- src/components/PhotoScroller.vue | 16 +++- src/pages/event/EventRecordEdit.vue | 51 ++++++++---- src/pages/home/QuickEntryPage.vue | 27 ++++++- src/pages/home/QuickSignInPage.vue | 118 +++++++++++++++++++++++++--- src/utils/eventRecord.ts | 9 +++ 5 files changed, 189 insertions(+), 32 deletions(-) diff --git a/src/components/PhotoScroller.vue b/src/components/PhotoScroller.vue index d4e9d9d..967a16c 100644 --- a/src/components/PhotoScroller.vue +++ b/src/components/PhotoScroller.vue @@ -1,6 +1,10 @@ @@ -113,16 +132,19 @@ import EventAPI from "@/api/EventAPI"; import EventRecordAPI from "@/api/EventRecordAPI"; import CodeScanner from "@/components/CodeScanner.vue"; import CustomerProfileCard from "@/components/CustomerProfileCard.vue"; +import PhotoScroller from "@/components/PhotoScroller.vue"; import { useSettingStore } from "@/store/settingStore"; import type { Customer } from "@/types/Customer"; import type { EventItem } from "@/types/EventItem"; import type { EventRecord } from "@/types/EventRecord"; +import type { PhotoItem } from "@/types/Photo"; import type { PointAction } from "@/types/Point"; import { getEventNumberValuePlaceholder, resolveEventNumberValue } from "@/utils/eventValue"; import { permissionCodes } from "@/utils/permission"; -import { getEventRecordPhotoURL } from "@/utils/eventRecord"; +import { getEventRecordPhotoURL, hasPhotoAttachment } from "@/utils/eventRecord"; +import { compressUploadImageList } from "@/utils/imageCompression"; import { Storage, Time } from "timi-web"; -import { useUserStore } from "timi-web-api"; +import { AttachmentAPI, useUserStore, type Attachment } from "timi-web-api"; import { loadSourceValueConfig, type SourceValueConfig } from "@/utils/point"; import EventSelector from "@/components/EventSelector.vue"; import { Space } from "timi-tdesign-mobile"; @@ -146,6 +168,7 @@ const selectedEventList = computed(() => selectedEventIdList.value .map(eventId => eventList.value.find(item => normalizeEventId(item) === eventId)) .filter((item): item is EventItem => !!item)); const selectedNumberEventList = computed(() => selectedEventList.value.filter(item => item.valueType === "NUMBER")); +const selectedRequiredPhotoEventList = computed(() => selectedEventList.value.filter(item => item.requirePhoto === true)); let sourceValueRequestNo = 0; function normalizeEventId(item?: EventItem): string { @@ -347,6 +370,68 @@ function validateNumberValueList(): boolean { return true; } +// ---------- 照片 ---------- + +const recordPhotoInputRef = ref(); +const recordPhotoItemList = ref([]); +const recordPhotoUploading = ref(false); +const showRecordPhotoInput = computed(() => selectedRequiredPhotoEventList.value.length > 0 || recordPhotoItemList.value.length > 0); +const recordPhotoList = computed({ + get() { + return recordPhotoItemList.value.map(item => item.url); + }, + set(value) { + const urlSet = new Set(value); + recordPhotoItemList.value = recordPhotoItemList.value.filter(item => urlSet.has(item.url)); + } +}); + +function capturePhoto(): void { + recordPhotoInputRef.value?.click(); +} + +async function handleRecordPhotoSelected(event: Event): Promise { + const input = event.target as HTMLInputElement; + const fileList = Array.from(input.files || []); + if (!fileList.length) { + return; + } + recordPhotoUploading.value = true; + try { + const compressedFileList = await compressUploadImageList(fileList); + const tempFileList = await AttachmentAPI.upload(compressedFileList); + for (const item of tempFileList) { + if (!item.id) { + continue; + } + recordPhotoItemList.value.push({ + tempFileId: item.id, + attachType: "PHOTO", + url: AttachmentAPI.getTempReadURL(item.id) + }); + } + } finally { + recordPhotoUploading.value = false; + input.value = ""; + } +} + +function validateRequiredPhotoList(): boolean { + const event = selectedRequiredPhotoEventList.value[0]; + if (event && !hasPhotoAttachment(recordPhotoItemList.value)) { + Message.warning(`请上传${event.name || "当前事件"}照片`); + return false; + } + return true; +} + +function buildPhotoAttachmentList(): Array> { + return recordPhotoItemList.value.map(item => ({ + tempFileId: item.tempFileId, + attachType: item.attachType || "PHOTO" + })); +} + async function doSubmit(): Promise { const customer = matchedCustomer.value; if (!customer) { @@ -356,6 +441,13 @@ async function doSubmit(): Promise { Message.warning("请至少选择一个登记事件"); return; } + if (recordPhotoUploading.value) { + Message.warning("照片正在上传,请稍候"); + return; + } + if (!validateRequiredPhotoList()) { + return; + } if (!validateNumberValueList()) { return; } @@ -367,19 +459,19 @@ async function doSubmit(): Promise { } submitting.value = true; try { - for (const eventId of selectedEventIdList.value) { - await EventRecordAPI.create({ - eventId, - customerId: customer.id, - pointValue: showSourceValueInput.value ? pointValue : undefined, - numberValue: resolveNumberValue(eventId), - remark: "客户快速扫码登记" - }); - } + await EventRecordAPI.createBatch(selectedEventIdList.value.map(eventId => ({ + eventId, + customerId: customer.id, + pointValue: showSourceValueInput.value ? pointValue : undefined, + numberValue: resolveNumberValue(eventId), + remark: "客户快速扫码登记", + attachmentList: buildPhotoAttachmentList() + }))); scanStatus.value = `登记成功:${customer.name || customer.code || scanCode.value}。`; scanCode.value = ""; matchedCustomer.value = undefined; pointValueText.value = ""; + recordPhotoItemList.value = []; for (const eventId of Object.keys(numberValueTextMap)) { delete numberValueTextMap[eventId]; } @@ -421,6 +513,10 @@ async function doSubmit(): Promise { } } + .file-input { + display: none; + } + .empty-block, .event-load-error { color: #52607a; diff --git a/src/utils/eventRecord.ts b/src/utils/eventRecord.ts index c21e879..a48dfd4 100644 --- a/src/utils/eventRecord.ts +++ b/src/utils/eventRecord.ts @@ -1,6 +1,15 @@ import type { EventRecord } from "@/types/EventRecord"; import { getAttachmentReadURL } from "@/utils/attachment"; +type EventRecordAttachment = { + attachType?: string; +}; + +/** 判断登记记录附件列表中是否包含照片。未指定类型时按历史默认值 PHOTO 处理。 */ +export function hasPhotoAttachment(attachmentList?: readonly EventRecordAttachment[]): boolean { + return !!attachmentList?.some(item => !item.attachType || item.attachType === "PHOTO"); +} + export function getEventRecordPhotoURL(record?: EventRecord, thumbnailSize?: number): string { const attachment = record?.attachmentList?.find(item => item.attachType === "PHOTO") || record?.attachmentList?.[0]; if (!attachment?.id) {