add package
This commit is contained in:
@@ -15,6 +15,15 @@
|
||||
</div>
|
||||
<t-cell-group v-entrance-transition="70" v-if="event" title="基本信息" theme="card">
|
||||
<t-cell title="所属门店" :note="storeName" />
|
||||
<t-cell title="业务类型" :note="getEventBizTypeText(event.bizType)" />
|
||||
<t-cell v-if="isPackagePurchaseEvent(event)" title="绑定套餐">
|
||||
<template #note>
|
||||
<div class="package-note">
|
||||
<strong v-text="event.packageConfig?.name || '未返回套餐配置'" />
|
||||
<span v-if="event.packageConfig" v-text="`${formatPackageAmount(event.packageConfig.standardAmount)}|${getPackageConfigStatusText(event.packageConfig.status)}`" />
|
||||
</div>
|
||||
</template>
|
||||
</t-cell>
|
||||
<t-cell title="事件颜色">
|
||||
<template #note>
|
||||
<div class="color-note">
|
||||
@@ -39,13 +48,18 @@
|
||||
</template>
|
||||
</t-cell>
|
||||
<t-cell
|
||||
v-if="canReadEventRecord"
|
||||
v-if="canReadEventRecord && event.bizType !== 'PACKAGE_PURCHASE'"
|
||||
title="登记记录"
|
||||
note="查看该事件登记记录"
|
||||
arrow
|
||||
:hover="true"
|
||||
@click="toEventRecordList"
|
||||
/>
|
||||
<t-cell
|
||||
v-else-if="event.bizType === 'PACKAGE_PURCHASE'"
|
||||
title="购买记录"
|
||||
note="请在客户套餐详情中查看"
|
||||
/>
|
||||
</t-cell-group>
|
||||
<t-cell-group v-entrance-transition="140" v-if="event" title="登记配置" theme="card">
|
||||
<t-cell title="数值类型" :note="getValueTypeText(event.valueType)" />
|
||||
@@ -116,6 +130,8 @@ import EventRemoveDialog from "@/pages/event/component/EventRemoveDialog.vue";
|
||||
import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { EventItem, EventLimitType, EventOptionSelectionMap, EventStatus, EventValueType, EventValueUnit } from "@/types/EventItem";
|
||||
import { getEventBizTypeText, isPackagePurchaseEvent } from "@/utils/event";
|
||||
import { formatPackageAmount, getPackageConfigStatusText } from "@/utils/package";
|
||||
import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
@@ -309,7 +325,7 @@ async function removeEvent(eventItem: EventItem): Promise<void> {
|
||||
|
||||
async function toEventRecordList(): Promise<void> {
|
||||
const eventId = event.value?.id || String(route.params.id || "");
|
||||
if (!eventId || !canReadEventRecord.value) {
|
||||
if (!eventId || !canReadEventRecord.value || isPackagePurchaseEvent(event.value)) {
|
||||
return;
|
||||
}
|
||||
await router.push({
|
||||
@@ -417,7 +433,8 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.color-note,
|
||||
.tag-list {
|
||||
.tag-list,
|
||||
.package-note {
|
||||
gap: .4rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -425,6 +442,16 @@ onUnmounted(() => {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.package-note {
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
|
||||
span {
|
||||
color: var(--td-text-color-secondary);
|
||||
font-size: .8125rem;
|
||||
}
|
||||
}
|
||||
|
||||
.color-dot {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
|
||||
@@ -25,6 +25,15 @@
|
||||
<t-form-item v-if="isGlobalManager" label="门店" :arrow="isCreateMode" @click="openStorePicker">
|
||||
<div class="picker-text" v-text="selectedStoreName" />
|
||||
</t-form-item>
|
||||
<t-form-item label="业务类型" :arrow="isCreateMode" @click="isCreateMode && openBizTypePicker()">
|
||||
<div class="picker-text" v-text="getBizTypeText(formModel.bizType)" />
|
||||
</t-form-item>
|
||||
<div v-if="formModel.bizType === 'PACKAGE_PURCHASE'" class="biz-type-tip">
|
||||
套餐购买事件必须绑定一个套餐配置。实际购买时只能使用此处绑定的套餐,不能临时更换。
|
||||
</div>
|
||||
<t-form-item v-if="formModel.bizType === 'PACKAGE_PURCHASE'" label="绑定套餐" required-mark arrow @click="openPackageConfigPicker">
|
||||
<div class="picker-text" v-text="selectedPackageConfig?.name || '请选择套餐配置'" />
|
||||
</t-form-item>
|
||||
<t-form-item label="说明">
|
||||
<t-textarea
|
||||
class="description"
|
||||
@@ -80,7 +89,7 @@
|
||||
<t-switch v-model="formModel.requirePhoto" />
|
||||
</t-form-item>
|
||||
<t-form-item label="需要核销" content-align="right">
|
||||
<t-switch v-model="formModel.requireVerification" />
|
||||
<t-switch v-model="formModel.requireVerification" :disabled="formModel.bizType === 'PACKAGE_PURCHASE'" />
|
||||
</t-form-item>
|
||||
<t-form-item label="状态" arrow @click="openStatusPicker">
|
||||
<div class="picker-text" v-text="getStatusText(formModel.status)" />
|
||||
@@ -157,6 +166,24 @@
|
||||
@confirm="confirmStorePicker"
|
||||
/>
|
||||
</t-popup>
|
||||
<t-popup v-model="bizTypePickerVisible" placement="bottom">
|
||||
<t-picker
|
||||
v-model="bizTypePickerDraft"
|
||||
:columns="bizTypePickerColumns"
|
||||
title="业务类型"
|
||||
@cancel="bizTypePickerVisible = false"
|
||||
@confirm="confirmBizTypePicker"
|
||||
/>
|
||||
</t-popup>
|
||||
<t-popup v-model="packageConfigPickerVisible" placement="bottom">
|
||||
<t-picker
|
||||
v-model="packageConfigPickerDraft"
|
||||
:columns="packageConfigPickerColumns"
|
||||
title="绑定套餐"
|
||||
@cancel="packageConfigPickerVisible = false"
|
||||
@confirm="confirmPackageConfigPicker"
|
||||
/>
|
||||
</t-popup>
|
||||
<t-popup v-model="statusPickerVisible" placement="bottom">
|
||||
<t-picker
|
||||
v-model="statusPickerDraft"
|
||||
@@ -219,12 +246,16 @@ import { Time } from "timi-web";
|
||||
import { type ModuleCode, type Role, RoleAPI, useUserStore } from "timi-web-api";
|
||||
import type { ColorObject } from "tdesign-mobile-vue";
|
||||
import EventAPI from "@/api/EventAPI";
|
||||
import PackageAPI from "@/api/PackageAPI";
|
||||
import EventOptionEditor from "@/pages/event/component/EventOptionEditor.vue";
|
||||
import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { EventItem, EventLimitType, EventOptionGroup, EventStatus, EventValueType, EventValueUnit } from "@/types/EventItem";
|
||||
import type { EventBizType, EventItem, EventLimitType, EventOptionGroup, EventStatus, EventValueType, EventValueUnit } from "@/types/EventItem";
|
||||
import type { GaoPackageConfig } from "@/types/Package";
|
||||
import { getErrorMessage } from "@/utils/error";
|
||||
import { normalizeEventOptionGroupList, prepareEventOptionGroupList, validateEventOptionGroupList } from "@/utils/eventOption";
|
||||
import { getEventBizTypeText } from "@/utils/event";
|
||||
import { getPackageConfigStatusText } from "@/utils/package";
|
||||
import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
@@ -263,6 +294,8 @@ const optionGroupList = ref<EventOptionGroup[]>([]);
|
||||
const formModel = reactive({
|
||||
id: "",
|
||||
storeId: "",
|
||||
bizType: "GENERAL" as EventBizType,
|
||||
packageConfigId: "",
|
||||
name: "",
|
||||
description: "",
|
||||
color: "#0f766e",
|
||||
@@ -279,6 +312,93 @@ const formModel = reactive({
|
||||
roleCodeList: [] as string[]
|
||||
});
|
||||
|
||||
// ---------- 业务类型 ----------
|
||||
|
||||
const bizTypeOptions: Array<{ label: string; value: EventBizType }> = [
|
||||
{ label: "普通登记", value: "GENERAL" },
|
||||
{ label: "套餐购买", value: "PACKAGE_PURCHASE" }
|
||||
];
|
||||
const bizTypePickerVisible = ref(false);
|
||||
const bizTypePickerDraft = ref<PickerValue[]>([formModel.bizType]);
|
||||
const bizTypePickerColumns = [bizTypeOptions];
|
||||
|
||||
function getBizTypeText(value: EventBizType): string {
|
||||
return getEventBizTypeText(value);
|
||||
}
|
||||
|
||||
function openBizTypePicker(): void {
|
||||
if (!isCreateMode.value) {
|
||||
return;
|
||||
}
|
||||
bizTypePickerDraft.value = [formModel.bizType];
|
||||
bizTypePickerVisible.value = true;
|
||||
}
|
||||
|
||||
async function confirmBizTypePicker(value: Array<string | number>): Promise<void> {
|
||||
const bizType = value[0];
|
||||
if (bizType !== "GENERAL" && bizType !== "PACKAGE_PURCHASE") {
|
||||
return;
|
||||
}
|
||||
formModel.bizType = bizType;
|
||||
if (bizType === "PACKAGE_PURCHASE") {
|
||||
formModel.requireVerification = false;
|
||||
await loadPackageConfigList();
|
||||
} else {
|
||||
formModel.packageConfigId = "";
|
||||
}
|
||||
bizTypePickerVisible.value = false;
|
||||
}
|
||||
|
||||
// ---------- 套餐绑定 ----------
|
||||
|
||||
const packageConfigList = ref<GaoPackageConfig[]>([]);
|
||||
const packageConfigPickerVisible = ref(false);
|
||||
const packageConfigPickerDraft = ref<PickerValue[]>([]);
|
||||
const packageConfigPickerColumns = computed(() => [[...packageConfigList.value].map(item => ({
|
||||
label: `${item.name || "未命名套餐"}(${getPackageConfigStatusText(item.status)})`,
|
||||
value: item.id || ""
|
||||
}))]);
|
||||
const selectedPackageConfig = computed(() => packageConfigList.value.find(item => item.id === formModel.packageConfigId));
|
||||
|
||||
async function loadPackageConfigList(): Promise<void> {
|
||||
if (formModel.bizType !== "PACKAGE_PURCHASE" || !formModel.storeId) {
|
||||
packageConfigList.value = [];
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const result = await PackageAPI.configList({
|
||||
storeId: formModel.storeId,
|
||||
index: 0,
|
||||
size: 1000
|
||||
});
|
||||
packageConfigList.value = result.list || [];
|
||||
} catch (error) {
|
||||
packageConfigList.value = [];
|
||||
Message.error(getErrorMessage(error, "套餐配置加载失败"));
|
||||
}
|
||||
}
|
||||
|
||||
function openPackageConfigPicker(): void {
|
||||
if (formModel.bizType !== "PACKAGE_PURCHASE") {
|
||||
return;
|
||||
}
|
||||
if (!packageConfigList.value.length) {
|
||||
Message.warning("当前门店没有可绑定的套餐配置");
|
||||
return;
|
||||
}
|
||||
packageConfigPickerDraft.value = [formModel.packageConfigId];
|
||||
packageConfigPickerVisible.value = true;
|
||||
}
|
||||
|
||||
function confirmPackageConfigPicker(value: Array<string | number>): void {
|
||||
const packageConfigId = String(value[0] || "");
|
||||
if (!packageConfigList.value.some(item => item.id === packageConfigId)) {
|
||||
return;
|
||||
}
|
||||
formModel.packageConfigId = packageConfigId;
|
||||
packageConfigPickerVisible.value = false;
|
||||
}
|
||||
|
||||
// ---------- 门店选择 ----------
|
||||
|
||||
const storePickerVisible = ref(false);
|
||||
@@ -309,13 +429,16 @@ function openStorePicker(): void {
|
||||
storePickerVisible.value = true;
|
||||
}
|
||||
|
||||
function confirmStorePicker(): void {
|
||||
async function confirmStorePicker(): Promise<void> {
|
||||
const storeId = String(storePickerDraft.value[0] || "");
|
||||
if (!storeId) {
|
||||
return;
|
||||
}
|
||||
|
||||
formModel.storeId = storeId;
|
||||
if (formModel.bizType === "PACKAGE_PURCHASE") {
|
||||
await loadPackageConfigList();
|
||||
}
|
||||
storePickerVisible.value = false;
|
||||
}
|
||||
|
||||
@@ -573,6 +696,10 @@ function validateForm(): boolean {
|
||||
Message.warning("请至少选择一个可登记角色");
|
||||
return false;
|
||||
}
|
||||
if (formModel.bizType === "PACKAGE_PURCHASE" && (!formModel.packageConfigId || !selectedPackageConfig.value)) {
|
||||
Message.warning("请选择绑定套餐配置");
|
||||
return false;
|
||||
}
|
||||
const optionError = validateEventOptionGroupList(optionGroupList.value);
|
||||
if (optionError) {
|
||||
Message.warning(optionError);
|
||||
@@ -582,6 +709,10 @@ function validateForm(): boolean {
|
||||
Message.warning("请输入有效的约束数值");
|
||||
return false;
|
||||
}
|
||||
if (formModel.bizType === "PACKAGE_PURCHASE" && formModel.requireVerification) {
|
||||
Message.warning("套餐购买事件不能要求核销");
|
||||
return false;
|
||||
}
|
||||
const beginAt = resolveTimestamp(formModel.beginAtText);
|
||||
const endAt = resolveTimestamp(formModel.endAtText);
|
||||
if (beginAt !== undefined && endAt !== undefined && endAt < beginAt) {
|
||||
@@ -636,6 +767,8 @@ async function saveEventItem(): Promise<void> {
|
||||
const payload: EventItem = {
|
||||
id: formModel.id || undefined,
|
||||
storeId: formModel.storeId,
|
||||
bizType: formModel.bizType,
|
||||
packageConfigId: formModel.bizType === "PACKAGE_PURCHASE" ? formModel.packageConfigId : undefined,
|
||||
name: formModel.name.trim(),
|
||||
description: formModel.description,
|
||||
color: formModel.color,
|
||||
@@ -698,6 +831,8 @@ async function fillForm(): Promise<void> {
|
||||
Object.assign(formModel, {
|
||||
id: current.id || "",
|
||||
storeId: current.storeId || formModel.storeId,
|
||||
bizType: current.bizType || "GENERAL",
|
||||
packageConfigId: current.packageConfigId || "",
|
||||
name: current.name || "",
|
||||
description: current.description || "",
|
||||
color: current.color || "#2f88ff",
|
||||
@@ -712,6 +847,12 @@ async function fillForm(): Promise<void> {
|
||||
endAtText: current.endAt ? Time.toDateTime(current.endAt) : "",
|
||||
roleCodeList: [...(current.roleCodeList || [])]
|
||||
});
|
||||
if (formModel.bizType === "PACKAGE_PURCHASE") {
|
||||
await loadPackageConfigList();
|
||||
if (current.packageConfig && !packageConfigList.value.some(item => item.id === current.packageConfig?.id)) {
|
||||
packageConfigList.value.unshift(current.packageConfig);
|
||||
}
|
||||
}
|
||||
optionGroupList.value = normalizeEventOptionGroupList(current.optionGroupList);
|
||||
formModel.limitValueText = resolveLimitValueText(current.limitValue);
|
||||
colorDraft.value = formModel.color;
|
||||
@@ -762,6 +903,14 @@ onUnmounted(() => navBarStore.clearRight());
|
||||
font-size: .875rem;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.biz-type-tip {
|
||||
padding: .75rem 1rem;
|
||||
color: var(--td-text-color-secondary);
|
||||
font-size: .8125rem;
|
||||
line-height: 1.5;
|
||||
background: var(--td-bg-color-secondarycontainer);
|
||||
}
|
||||
}
|
||||
|
||||
.picker-text {
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
/>
|
||||
<t-tag v-if="item.requirePhoto" theme="warning" variant="outline">需拍照</t-tag>
|
||||
<t-tag v-if="item.requireVerification" theme="warning" variant="outline">需核销</t-tag>
|
||||
<t-tag v-if="item.bizType === 'PACKAGE_PURCHASE'" theme="primary" variant="outline">套餐购买</t-tag>
|
||||
</div>
|
||||
</template>
|
||||
<template #description>
|
||||
@@ -50,7 +51,8 @@
|
||||
<span v-if="!(item.roleCodeList || []).length" class="muted" v-text="'未配置'" />
|
||||
</div>
|
||||
<div class="line" v-text="getLimitText(item)" />
|
||||
<div class="line" v-text="getValueDefinitionText(item)" />
|
||||
<div class="line" v-text="`${getEventBizTypeText(item.bizType)}|${getValueDefinitionText(item)}`" />
|
||||
<div v-if="item.bizType === 'PACKAGE_PURCHASE'" class="line" v-text="`绑定套餐:${item.packageConfig?.name || '未配置'}`" />
|
||||
</div>
|
||||
</template>
|
||||
</t-cell>
|
||||
@@ -89,6 +91,7 @@ import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
||||
import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { EventItem, EventLimitType, EventStatus } from "@/types/EventItem";
|
||||
import type { NavBarAction } from "@/types/NavBarAction";
|
||||
import { getEventBizTypeText } from "@/utils/event";
|
||||
import { getEventValueUnitText } from "@/utils/eventValue";
|
||||
import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
:hover="canReadEvent && !!(record.event?.id || record.eventId)"
|
||||
@click="toEventDetail"
|
||||
/>
|
||||
<t-cell title="业务类型" :note="getEventBizTypeText(record.event?.bizType)" />
|
||||
<t-cell v-if="record.numberValue !== undefined && record.numberValue !== null" title="事件数值" :note="getNumberValueText(record)" />
|
||||
<t-cell v-if="record.pointValue !== undefined && record.pointValue !== null" title="提交积分值" :note="getPointValueText(record.pointValue, pointLedgerChangeType)" />
|
||||
<t-cell v-if="record.earnedPointValue !== undefined && record.earnedPointValue !== null" title="本次积分变化" :note="getPointValueText(record.earnedPointValue, pointLedgerChangeType)" />
|
||||
@@ -111,6 +112,7 @@ import type { PhotoItem } from "@/types/Photo";
|
||||
import type { PointLedgerChangeType } from "@/types/Point";
|
||||
import { getAttachmentReadURL } from "@/utils/attachment";
|
||||
import { buildEventOptionSelectionMap } from "@/utils/eventOption";
|
||||
import { getEventBizTypeText, isPackagePurchaseEvent } from "@/utils/event";
|
||||
import { getErrorMessage } from "@/utils/error";
|
||||
import { formatEventNumberValue } from "@/utils/eventValue";
|
||||
import { Message } from "@/utils/message";
|
||||
@@ -133,9 +135,10 @@ const isVerifying = ref(false);
|
||||
const isDeleting = ref(false);
|
||||
const verifyDialogVisible = ref(false);
|
||||
|
||||
const canUpdateRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.update));
|
||||
const canVerifyRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.verify));
|
||||
const canDeleteRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.delete));
|
||||
const isPackagePurchaseRecord = computed(() => isPackagePurchaseEvent(record.value?.event));
|
||||
const canUpdateRecord = computed(() => !!record.value && !isPackagePurchaseRecord.value && userStore.hasPermissions(permissionCodes.eventRecord.update));
|
||||
const canVerifyRecord = computed(() => !!record.value && !isPackagePurchaseRecord.value && userStore.hasPermissions(permissionCodes.eventRecord.verify));
|
||||
const canDeleteRecord = computed(() => !!record.value && !isPackagePurchaseRecord.value && userStore.hasPermissions(permissionCodes.eventRecord.delete));
|
||||
const canReadEvent = computed(() => userStore.hasPermissions(permissionCodes.event.read));
|
||||
const recordPhotoItemList = computed(() => (record.value?.attachmentList || [])
|
||||
.filter(item => !!item.id)
|
||||
@@ -149,7 +152,7 @@ const recordPhotoList = computed(() => recordPhotoItemList.value.map(item => ite
|
||||
|
||||
async function submitPhotoItems(photoItems: PhotoItem[]): Promise<void> {
|
||||
const currentRecord = record.value;
|
||||
if (!currentRecord?.id) {
|
||||
if (!currentRecord?.id || isPackagePurchaseRecord.value) {
|
||||
throw new Error("登记记录不存在");
|
||||
}
|
||||
|
||||
@@ -260,7 +263,7 @@ async function confirmVerifyRecord(): Promise<void> {
|
||||
|
||||
async function submitVerifyRecord(record: EventRecord, attachmentList: Attachment[]): Promise<void> {
|
||||
const id = record.id;
|
||||
if (!id) {
|
||||
if (!id || isPackagePurchaseRecord.value) {
|
||||
throw new Error("登记事件记录不存在");
|
||||
}
|
||||
|
||||
@@ -313,7 +316,7 @@ function confirmRemoveRecord(): void {
|
||||
}
|
||||
|
||||
async function removeRecord(id: string): Promise<void> {
|
||||
if (isDeleting.value) {
|
||||
if (isDeleting.value || isPackagePurchaseRecord.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -338,7 +341,7 @@ async function removeRecord(id: string): Promise<void> {
|
||||
|
||||
async function toEdit(): Promise<void> {
|
||||
const recordId = String(route.params.id || "");
|
||||
if (!recordId) {
|
||||
if (!recordId || !canUpdateRecord.value) {
|
||||
return;
|
||||
}
|
||||
await router.push(`/management/registration-event-records/${recordId}/edit`);
|
||||
@@ -380,12 +383,14 @@ watch(() => route.params.id, async () => {
|
||||
await loadRecord();
|
||||
}, { immediate: true });
|
||||
|
||||
onMounted(() => {
|
||||
function updateNavBarAction(): void {
|
||||
navBarStore.clearRight();
|
||||
if (canUpdateRecord.value) {
|
||||
navBarStore.setRightText("编辑", "var(--td-brand-color)", toEdit);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
watch(canUpdateRecord, updateNavBarAction, { immediate: true });
|
||||
|
||||
onUnmounted(() => {
|
||||
navBarStore.clearRight();
|
||||
|
||||
@@ -46,11 +46,12 @@
|
||||
/>
|
||||
<div v-if="isEditMode" class="edit-point-tip red">涉及积分变动的数据不能修改,已产生积分流水的登记记录不会因修改客户、事件或时间而重算;如需修改这些事实,请删除后重新创建,删除会自动回流积分。</div>
|
||||
<event-selector
|
||||
v-model="selectedEventIdList"
|
||||
:model-value="selectedEventIdList"
|
||||
class="event-selector"
|
||||
:event-list="eventList"
|
||||
:disabled="isEditMode"
|
||||
required-mark
|
||||
@update:model-value="handleEventSelectionChange"
|
||||
/>
|
||||
<event-option-selector
|
||||
class="option-selector"
|
||||
@@ -219,6 +220,7 @@ 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 { isPackagePurchaseEvent } from "@/utils/event";
|
||||
import { buildEventOptionSelectionMap, resolveSelectedOptionGroupList, validateEventOptionSelection } from "@/utils/eventOption";
|
||||
import { getEventRecordPhotoURL, hasPhotoAttachment } from "@/utils/eventRecord";
|
||||
import { getEventNumberValuePlaceholder, resolveEventNumberValue } from "@/utils/eventValue";
|
||||
@@ -434,6 +436,27 @@ const eventNameMap = computed(() => {
|
||||
return map;
|
||||
});
|
||||
|
||||
async function handleEventSelectionChange(eventIdList: string[]): Promise<void> {
|
||||
const packageEvent = eventIdList
|
||||
.map(eventId => eventList.value.find(item => item.id === eventId))
|
||||
.find(item => isPackagePurchaseEvent(item));
|
||||
if (!isEditMode.value && packageEvent?.id) {
|
||||
if (!userStore.hasPermissions(permissionCodes.customerPackage.create)) {
|
||||
Message.warning("当前账号没有套餐购买权限");
|
||||
return;
|
||||
}
|
||||
await router.push({
|
||||
name: "CustomerPackagePurchase",
|
||||
query: {
|
||||
eventId: packageEvent.id,
|
||||
...(selectedCustomer.value?.id ? { customerId: selectedCustomer.value.id } : {})
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
selectedEventIdList.value = eventIdList;
|
||||
}
|
||||
|
||||
function appendRecordEvent(record: EventRecord): void {
|
||||
if (!record.event?.id || eventList.value.some(item => item.id === record.event?.id)) {
|
||||
return;
|
||||
@@ -683,6 +706,10 @@ function validateForm(): boolean {
|
||||
Message.warning("请至少选择一个登记事件");
|
||||
return false;
|
||||
}
|
||||
if (selectedEventIdList.value.some(eventId => isPackagePurchaseEvent(eventList.value.find(item => item.id === eventId)))) {
|
||||
Message.warning("套餐购买事件必须通过客户套餐购买页面登记");
|
||||
return false;
|
||||
}
|
||||
for (const eventId of selectedEventIdList.value) {
|
||||
const event = eventList.value.find(item => item.id === eventId);
|
||||
if (!event) {
|
||||
@@ -769,11 +796,17 @@ async function loadPageData(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
selectedEventIdList.value = eventList.value.slice(0, 1).map(item => item.id).filter((item): item is string => Boolean(item));
|
||||
const defaultEvent = eventList.value.find(item => !isPackagePurchaseEvent(item));
|
||||
selectedEventIdList.value = defaultEvent?.id ? [defaultEvent.id] : [];
|
||||
}
|
||||
|
||||
async function loadRecordForEdit(): Promise<void> {
|
||||
const record = await EventRecordAPI.detail(routeId.value);
|
||||
if (isPackagePurchaseEvent(record.event)) {
|
||||
Message.warning("套餐购买记录不能通过普通登记页编辑,请在客户套餐详情中查看");
|
||||
await router.replace({ name: "EventRecordDetail", params: { id: routeId.value } });
|
||||
return;
|
||||
}
|
||||
editingRecord.value = record;
|
||||
appendRecordEvent(record);
|
||||
selectedCustomerList.value = record.customer ? [record.customer] : [];
|
||||
|
||||
@@ -128,6 +128,7 @@ import { useNavBarStore } from "@/store/navBarStore";
|
||||
import type { EventItem, EventStatus } from "@/types/EventItem";
|
||||
import type { EventOptionStat, EventRecordPage, EventRecordPageResult, VerificationStatus } from "@/types/EventRecord";
|
||||
import type { NavBarAction } from "@/types/NavBarAction";
|
||||
import { isPackagePurchaseEvent } from "@/utils/event";
|
||||
import { formatEventNumberValue } from "@/utils/eventValue";
|
||||
import { permissionCodes } from "@/utils/permission";
|
||||
import { Message } from "@/utils/message.ts";
|
||||
@@ -367,6 +368,10 @@ async function loadRecords(): Promise<void> {
|
||||
if (isLoading.value || isNoMore.value) {
|
||||
return;
|
||||
}
|
||||
if (!eventList.value.length) {
|
||||
isNoMore.value = true;
|
||||
return;
|
||||
}
|
||||
const token = loadToken + 1;
|
||||
loadToken = token;
|
||||
isLoading.value = true;
|
||||
@@ -398,7 +403,7 @@ async function loadActions(): Promise<void> {
|
||||
const result = canCreateEvent.value
|
||||
? await EventAPI.listHistory()
|
||||
: await EventAPI.list({ index: 0, size: 1000 });
|
||||
eventList.value = result.list || [];
|
||||
eventList.value = (result.list || []).filter(item => !isPackagePurchaseEvent(item));
|
||||
const eventIdList = eventList.value.map(item => item.id).filter((item): item is string => Boolean(item));
|
||||
const eventIdSet = new Set(eventIdList);
|
||||
const selectedEventIdList = parseEventIdList(route.query.eventIds).filter(eventId => eventIdSet.has(eventId));
|
||||
@@ -529,13 +534,15 @@ async function toCreate(): Promise<void> {
|
||||
|
||||
function buildRangeReq(): Omit<EventRecordPage, "index" | "size"> {
|
||||
const [beginAt, endAt] = buildUnixRange();
|
||||
const selectedAllEvent = !actionFilter.value.length || actionFilter.value.length === eventList.value.length;
|
||||
const eventIdList = actionFilter.value.length
|
||||
? actionFilter.value
|
||||
: eventList.value.map(item => item.id).filter((item): item is string => Boolean(item));
|
||||
return {
|
||||
beginAt,
|
||||
endAt,
|
||||
customerId: customerId.value || undefined,
|
||||
keyword: keyword.value.trim() || undefined,
|
||||
eventIdList: actionFilter.value.length === 1 ? actionFilter.value : selectedAllEvent ? undefined : actionFilter.value,
|
||||
eventIdList,
|
||||
optionIdList: optionFilter.value.length ? optionFilter.value : undefined,
|
||||
verificationStatus: verificationFilter.value === "ALL" ? undefined : verificationFilter.value
|
||||
};
|
||||
|
||||
@@ -74,6 +74,7 @@ import EventRecordAPI from "@/api/EventRecordAPI";
|
||||
import EventRecordVerifyDialog from "@/components/EventRecordVerifyDialog.vue";
|
||||
import type { EventRecord, EventRecordListItemView } from "@/types/EventRecord";
|
||||
import { getCustomerPhotoURL } from "@/utils/customer";
|
||||
import { isPackagePurchaseEvent } from "@/utils/event";
|
||||
import { getErrorMessage } from "@/utils/error";
|
||||
import { permissionCodes } from "@/utils/permission";
|
||||
import { Message } from "@/utils/message";
|
||||
@@ -100,9 +101,10 @@ const route = useRoute();
|
||||
const router = useRouter();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const canUpdateRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.update));
|
||||
const canVerifyRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.verify));
|
||||
const canDeleteRecord = computed(() => userStore.hasPermissions(permissionCodes.eventRecord.delete));
|
||||
const isPackagePurchaseRecord = computed(() => isPackagePurchaseEvent(props.record.event));
|
||||
const canUpdateRecord = computed(() => !isPackagePurchaseRecord.value && userStore.hasPermissions(permissionCodes.eventRecord.update));
|
||||
const canVerifyRecord = computed(() => !isPackagePurchaseRecord.value && userStore.hasPermissions(permissionCodes.eventRecord.verify));
|
||||
const canDeleteRecord = computed(() => !isPackagePurchaseRecord.value && userStore.hasPermissions(permissionCodes.eventRecord.delete));
|
||||
const isDeleting = ref(false);
|
||||
const isVerifying = ref(false);
|
||||
const isLoadingVerifyRecord = ref(false);
|
||||
|
||||
Reference in New Issue
Block a user