add package

This commit is contained in:
Timi
2026-09-01 01:14:54 +08:00
parent 100d4c4c44
commit 2d94abfb32
39 changed files with 4425 additions and 59 deletions
+152 -3
View File
@@ -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 {