add event option

This commit is contained in:
Timi
2026-08-30 23:36:59 +08:00
parent 82e2359857
commit f65c88920a
17 changed files with 1620 additions and 398 deletions
+46 -6
View File
@@ -44,6 +44,7 @@
:customer="selectedCustomer"
v-entrance-transition
/>
<div v-if="isEditMode" class="edit-point-tip red">涉及积分变动的数据不能修改,已产生积分流水的登记记录不会因修改客户、事件或时间而重算;如需修改这些事实,请删除后重新创建,删除会自动回流积分。</div>
<event-selector
v-model="selectedEventIdList"
class="event-selector"
@@ -51,6 +52,13 @@
:disabled="isEditMode"
required-mark
/>
<event-option-selector
class="option-selector"
v-model="selectedOptionSelectionMap"
:event-list="eventList"
:selected-event-id-list="selectedEventIdList"
:disabled="isSaving"
/>
<div v-if="!isEditMode && selectedNumberEventList.length" class="event-value-list">
<t-form-item
v-for="item in selectedNumberEventList"
@@ -68,7 +76,6 @@
/>
</t-form-item>
</div>
<div v-if="isEditMode" class="edit-point-tip">已产生积分的登记记录不会因修改客户、事件或时间而重算;如需修改这些事实,请删除后重新创建。</div>
<t-form-item v-if="showSourceValueInput" label="积分动作">
<span class="picker-text" v-text="getPointActionText(sourceValueAction)" />
</t-form-item>
@@ -202,15 +209,17 @@ import CodeScanner from "@/components/CodeScanner.vue";
import CustomerList from "@/components/CustomerList.vue";
import CustomerProfileCard from "@/components/CustomerProfileCard.vue";
import CustomerSelector from "@/components/CustomerSelector.vue";
import EventOptionSelector from "@/components/EventOptionSelector.vue";
import PhotoScroller from "@/components/PhotoScroller.vue";
import EventSelector from "@/components/EventSelector.vue";
import { useNavBarStore } from "@/store/navBarStore";
import { useSettingStore } from "@/store/settingStore";
import type { Customer } from "@/types/Customer";
import type { EventItem } from "@/types/EventItem";
import type { EventItem, EventOptionSelectionMap } from "@/types/EventItem";
import type { EventRecord } from "@/types/EventRecord";
import type { PointAction } from "@/types/Point";
import { getAttachmentReadURL } from "@/utils/attachment";
import { buildEventOptionSelectionMap, resolveSelectedOptionGroupList, validateEventOptionSelection } from "@/utils/eventOption";
import { getEventRecordPhotoURL, hasPhotoAttachment } from "@/utils/eventRecord";
import { getEventNumberValuePlaceholder, resolveEventNumberValue } from "@/utils/eventValue";
import { permissionCodes } from "@/utils/permission";
@@ -240,7 +249,7 @@ type EditablePhoto = {
const routeId = computed(() => String(route.params.id || ""));
const isEditMode = computed(() => !!routeId.value);
const pageAccess = computed(() => isEditMode.value ? permissionCodes.eventRecord.update : permissionCodes.eventRecord.create);
const saveButtonText = computed(() => isEditMode.value ? "保存记录" : "提交");
const saveButtonText = computed(() => isEditMode.value ? "保存" : "提交");
// ---------- 客户 ----------
@@ -406,6 +415,7 @@ function handleScanStatusChange(value: string): void {
const eventList = ref<EventItem[]>([]);
const selectedEventIdList = ref<string[]>([]);
const selectedOptionSelectionMap = reactive<EventOptionSelectionMap>({});
const numberValueTextMap = reactive<Record<string, string>>({});
const selectedNumberEventList = computed(() => selectedEventIdList.value
.map(eventId => eventList.value.find(item => item.id === eventId))
@@ -432,6 +442,11 @@ function appendRecordEvent(record: EventRecord): void {
eventList.value = [record.event, ...eventList.value];
}
function resolveRecordOptionGroupList(eventId: string) {
const event = eventList.value.find(item => item.id === eventId);
return event ? resolveSelectedOptionGroupList(event, selectedOptionSelectionMap) : [];
}
function getNumberValuePlaceholder(event: EventItem): string {
return getEventNumberValuePlaceholder(event);
}
@@ -629,6 +644,7 @@ async function submitRecord(): Promise<void> {
await EventRecordAPI.createBatch(selectedEventIdList.value.map(eventId => ({
eventId,
customerIdList,
selectedOptionGroupList: resolveRecordOptionGroupList(eventId),
remark: note.value || "客户现场登记",
registeredAt: buildRegisteredAt(),
pointValue: showSourceValueInput.value ? resolveSourceValue() : undefined,
@@ -646,6 +662,9 @@ async function submitRecord(): Promise<void> {
for (const eventId of Object.keys(numberValueTextMap)) {
delete numberValueTextMap[eventId];
}
for (const eventId of Object.keys(selectedOptionSelectionMap)) {
delete selectedOptionSelectionMap[eventId];
}
await loadRecentRecordList();
await settingStore.playRegisterSuccessSound();
Message.success("登记已提交");
@@ -664,6 +683,17 @@ function validateForm(): boolean {
Message.warning("请至少选择一个登记事件");
return false;
}
for (const eventId of selectedEventIdList.value) {
const event = eventList.value.find(item => item.id === eventId);
if (!event) {
continue;
}
const optionError = validateEventOptionSelection(event, selectedOptionSelectionMap);
if (optionError) {
Message.warning(optionError);
return false;
}
}
if (!validateRequiredPhotoList()) {
return false;
}
@@ -698,9 +728,10 @@ async function updateRecord(): Promise<void> {
}
await EventRecordAPI.update({
...editingRecord.value,
id: editingRecord.value.id,
customerId: editingRecord.value.customerId,
eventId: editingRecord.value.eventId,
selectedOptionGroupList: resolveRecordOptionGroupList(editingRecord.value.eventId),
remark: note.value,
registeredAt: editingRecord.value.registeredAt,
attachmentList: buildPhotoAttachmentList()
@@ -749,6 +780,12 @@ async function loadRecordForEdit(): Promise<void> {
customerCode.value = record.customer?.code || record.customerId || "";
selectedCustomerIdList.value = record.customer?.id ? [record.customer.id] : [];
selectedEventIdList.value = record.eventId ? [record.eventId] : [];
for (const eventId of Object.keys(selectedOptionSelectionMap)) {
delete selectedOptionSelectionMap[eventId];
}
if (record.eventId) {
Object.assign(selectedOptionSelectionMap, buildEventOptionSelectionMap(record.eventId, record.selectedOptionGroupList));
}
registeredAtText.value = record.registeredAt ? Time.formatDateTime(new Date(record.registeredAt)) : "";
note.value = record.remark || "";
pointValueText.value = "";
@@ -793,7 +830,7 @@ watch(customerCode, async (value) => {
.registration-record-edit {
gap: 1rem;
display: flex;
padding: calc(var(--app-nav-height) + 1rem) 0 4rem 0;
padding: calc(var(--app-nav-height) + 1rem) 0 1rem 0;
flex-direction: column;
.form,
@@ -843,6 +880,10 @@ watch(customerCode, async (value) => {
}
}
.option-selector {
margin: 1rem 0 1rem 1rem;
}
.time-actions {
gap: .5rem;
display: flex;
@@ -851,7 +892,6 @@ watch(customerCode, async (value) => {
}
.edit-point-tip {
color: #9a3412;
padding: 0 1rem 1rem;
font-size: .8125rem;
margin-top: .25rem;