add product

This commit is contained in:
Timi
2026-09-03 14:27:40 +08:00
parent 7f669621fb
commit 333999fe21
63 changed files with 5662 additions and 2210 deletions
+10 -1
View File
@@ -274,6 +274,9 @@ const customerDisplayText = computed(() => {
async function handleCustomerChange(value: string | string[]): Promise<void> {
selectedCustomerIdList.value = Array.isArray(value) ? value : [value];
if (isPackagePurchaseSelected.value) {
resetPackageRequestNo();
}
await loadSelectedCustomerList();
await loadRecentRecordList();
}
@@ -601,6 +604,7 @@ const {
handleEventSelectionChange,
applySelectedPackageEvent,
resetPackagePurchaseForm,
resetPackageRequestNo,
submitPackagePurchase,
validatePackagePurchaseForm
} = usePackagePurchaseRecord({
@@ -635,8 +639,9 @@ async function submitRecord(): Promise<void> {
return;
}
const packageResult = isPackagePurchaseSelected.value ? await submitPackagePurchase() : undefined;
const success = isPackagePurchaseSelected.value
? await submitPackagePurchase()
? !!packageResult?.id
: await submitEventRecords();
if (!success) {
return;
@@ -645,6 +650,10 @@ async function submitRecord(): Promise<void> {
await loadRecentRecordList();
await settingStore.playRegisterSuccessSound();
Message.success(isPackagePurchaseSelected.value ? "套餐购买已提交" : "登记已提交");
if (packageResult?.id) {
await router.replace(`/management/customer-packages/${packageResult.id}`);
return;
}
await router.replace("/customers");
} finally {
isSaving.value = false;
+1 -1
View File
@@ -718,7 +718,7 @@ onUnmounted(() => {
flex-direction: column;
.search-input {
--td-input-vertical-padding: 8px;
--td-input-vertical-padding: 8px 14px;
border: 1px solid rgba(148, 163, 184, .28);
z-index: 1;
@@ -29,9 +29,9 @@
</div>
<t-form-item
v-for="(item, index) in itemList"
:key="`${index}-${item.productName}-${item.unit}`"
:key="`${index}-${item.productId}-${item.productUnitId}`"
class="item"
:label="item.productName"
:label="getItemLabel(item)"
>
<t-input
:model-value="item.totalQuantity"
@@ -43,7 +43,7 @@
@update:model-value="updateItemQuantity(index, $event)"
>
<template #suffix-icon>
<span class="unit" v-text="item.unit" />
<span class="unit" v-text="getItemUnit(item)" />
</template>
</t-input>
</t-form-item>
@@ -68,6 +68,15 @@ const props = defineProps<Props>();
const saleAmount = defineModel<string>("saleAmount", { required: true });
const itemList = defineModel<PackagePurchaseItem[]>("itemList", { required: true });
function getItemLabel(item: PackagePurchaseItem): string {
const configItem = props.packageConfig?.itemList?.find(config => config.productId === item.productId && config.productUnitId === item.productUnitId);
return configItem ? `${configItem.productNameSnapshot || "未命名产品"} · ${configItem.unitSnapshot || "未设置单位"}` : "产品权益";
}
function getItemUnit(item: PackagePurchaseItem): string {
return props.packageConfig?.itemList?.find(config => config.productId === item.productId && config.productUnitId === item.productUnitId)?.unitSnapshot || "";
}
function updateItemQuantity(index: number, value: string | number): void {
itemList.value = itemList.value.map((item, itemIndex) => itemIndex === index ? {
...item,
@@ -2,7 +2,7 @@ import { computed, ref, type Ref } from "vue";
import PackageAPI from "@/api/PackageAPI";
import type { Customer } from "@/types/Customer";
import type { EventItem } from "@/types/EventItem";
import type { PackagePurchaseItem } from "@/types/Package";
import type { GaoCustomerPackage, PackagePurchaseItem } from "@/types/Package";
import { isPackagePurchaseEvent } from "@/utils/event";
import { createPackageRequestNo, formatPackageAmountInput, normalizePackageQuantity, parsePackageAmount } from "@/utils/package";
import { permissionCodes } from "@/utils/permission";
@@ -37,6 +37,7 @@ export function usePackagePurchaseRecord(options: UsePackagePurchaseRecordOption
const canPriceOverride = computed(() => options.hasPermission(permissionCodes.customerPackage.priceOverride));
const saleAmountText = ref("");
const packagePurchaseItemList = ref<PackagePurchaseItem[]>([]);
const packageRequestNo = ref("");
function handleEventSelectionChange(eventIdList: string[]): void {
const packageEvent = eventIdList
@@ -65,8 +66,8 @@ export function usePackagePurchaseRecord(options: UsePackagePurchaseRecordOption
saleAmountText.value = formatPackageAmountInput(config.standardAmount);
packagePurchaseItemList.value = (config.itemList || []).map((item, index) => ({
productName: item.productName,
unit: item.unit,
productId: item.productId,
productUnitId: item.productUnitId,
totalQuantity: normalizePackageQuantity(item.defaultQuantity) || "",
sort: item.sort ?? index,
remark: item.remark ?? null
@@ -76,25 +77,33 @@ export function usePackagePurchaseRecord(options: UsePackagePurchaseRecordOption
function resetPackagePurchaseForm(): void {
saleAmountText.value = "";
packagePurchaseItemList.value = [];
packageRequestNo.value = "";
}
async function submitPackagePurchase(): Promise<boolean> {
function resetPackageRequestNo(): void {
packageRequestNo.value = "";
}
async function submitPackagePurchase(): Promise<GaoCustomerPackage | undefined> {
const eventId = selectedPackageEvent.value?.id;
const customerId = options.selectedCustomerList.value[0]?.id;
const saleAmount = parsePackageAmount(saleAmountText.value);
if (!eventId || !customerId || saleAmount === undefined) {
return false;
return undefined;
}
await PackageAPI.purchase({
requestNo: createPackageRequestNo(),
if (!packageRequestNo.value) {
packageRequestNo.value = createPackageRequestNo();
}
const result = await PackageAPI.purchase({
requestNo: packageRequestNo.value,
eventId,
customerId,
saleAmount,
purchasedAt: options.buildRegisteredAt(),
itemList: packagePurchaseItemList.value.map((item, index) => ({
productName: item.productName.trim(),
unit: item.unit.trim(),
productId: item.productId,
productUnitId: item.productUnitId,
totalQuantity: normalizePackageQuantity(item.totalQuantity)!,
sort: item.sort ?? index,
remark: item.remark ?? null
@@ -105,7 +114,7 @@ export function usePackagePurchaseRecord(options: UsePackagePurchaseRecordOption
})),
remark: options.note.value.trim() || null
});
return true;
return result;
}
function validatePackagePurchaseForm(): boolean {
@@ -135,8 +144,8 @@ export function usePackagePurchaseRecord(options: UsePackagePurchaseRecordOption
}
for (const item of packagePurchaseItemList.value) {
const quantity = normalizePackageQuantity(item.totalQuantity);
if (!item.productName.trim() || !item.unit.trim() || !quantity || quantity === "0") {
Message.warning("产品名称、单位和正数量不能为空");
if (!item.productId || !item.productUnitId || !quantity || quantity === "0") {
Message.warning("产品单位和正数量不能为空");
return false;
}
}
@@ -159,6 +168,7 @@ export function usePackagePurchaseRecord(options: UsePackagePurchaseRecordOption
handleEventSelectionChange,
applySelectedPackageEvent,
resetPackagePurchaseForm,
resetPackageRequestNo,
submitPackagePurchase,
validatePackagePurchaseForm
};