Files
TakeoutSaaS.TenantUI/apps/web-antd/src/views/member/points-mall/composables/points-mall-page/drawer-actions.ts

357 lines
9.5 KiB
TypeScript

import type { Ref } from 'vue';
import type {
PointMallProductCardViewModel,
PointMallProductEditorForm,
} from '#/views/member/points-mall/types';
import { message } from 'ant-design-vue';
import { getMemberCouponPickerApi } from '#/api/member';
import {
getMemberPointMallProductDetailApi,
saveMemberPointMallProductApi,
} from '#/api/member/points-mall';
import {
mapProductEditorFormToSavePayload,
mapProductToEditorForm,
resetProductEditorForm,
} from './helpers';
interface CouponOption {
label: string;
value: string;
}
interface CreateDrawerActionsOptions {
canManage: Ref<boolean>;
couponOptions: Ref<CouponOption[]>;
drawerMode: Ref<'create' | 'edit'>;
form: PointMallProductEditorForm;
isDrawerLoading: Ref<boolean>;
isDrawerOpen: Ref<boolean>;
isDrawerSubmitting: Ref<boolean>;
loadProductList: () => Promise<void>;
openPicker: (currentProductId?: string) => Promise<void>;
selectedStoreId: Ref<string>;
}
export function createDrawerActions(options: CreateDrawerActionsOptions) {
async function loadCouponOptions(keyword?: string) {
if (!options.selectedStoreId.value) {
options.couponOptions.value = [];
return;
}
try {
const rows = await getMemberCouponPickerApi({
storeId: options.selectedStoreId.value,
keyword: keyword?.trim() || undefined,
});
options.couponOptions.value = rows.map((item) => ({
label: item.displayText || item.name,
value: item.couponTemplateId,
}));
} catch (error) {
console.error(error);
options.couponOptions.value = [];
message.error('加载优惠券失败');
}
}
async function openCreateDrawer() {
if (!options.selectedStoreId.value) {
message.warning('请先选择门店');
return;
}
options.drawerMode.value = 'create';
resetProductEditorForm(options.form);
options.isDrawerLoading.value = false;
options.isDrawerOpen.value = true;
await loadCouponOptions();
}
async function openEditDrawer(item: PointMallProductCardViewModel) {
if (!options.selectedStoreId.value) {
message.warning('请先选择门店');
return;
}
options.drawerMode.value = 'edit';
options.isDrawerLoading.value = true;
options.isDrawerOpen.value = true;
try {
const detail = await getMemberPointMallProductDetailApi({
storeId: options.selectedStoreId.value,
pointMallProductId: item.pointMallProductId,
});
const mapped = mapProductToEditorForm(detail);
options.form.pointMallProductId = mapped.pointMallProductId;
options.form.redeemType = mapped.redeemType;
options.form.productId = mapped.productId;
options.form.productName = mapped.productName;
options.form.couponTemplateId = mapped.couponTemplateId;
options.form.couponTemplateName = mapped.couponTemplateName;
options.form.physicalName = mapped.physicalName;
options.form.pickupMethod = mapped.pickupMethod;
options.form.name = mapped.name;
options.form.imageUrl = mapped.imageUrl;
options.form.exchangeType = mapped.exchangeType;
options.form.requiredPoints = mapped.requiredPoints;
options.form.cashAmount = mapped.cashAmount;
options.form.stockTotal = mapped.stockTotal;
options.form.perMemberLimit = mapped.perMemberLimit;
options.form.description = mapped.description;
options.form.notifyChannels = [...mapped.notifyChannels];
options.form.status = mapped.status;
if (detail.redeemType === 'coupon') {
await loadCouponOptions();
const selected = options.couponOptions.value.find(
(row) => row.value === detail.couponTemplateId,
);
options.form.couponTemplateName = selected?.label ?? '';
}
} catch (error) {
console.error(error);
message.error('加载兑换商品详情失败');
options.isDrawerOpen.value = false;
} finally {
options.isDrawerLoading.value = false;
}
}
function setDrawerOpen(value: boolean) {
options.isDrawerOpen.value = value;
if (!value) {
options.isDrawerLoading.value = false;
options.isDrawerSubmitting.value = false;
}
}
function setFormName(value: string) {
options.form.name = value;
}
function setFormImageUrl(value: string) {
options.form.imageUrl = value;
}
function setFormRedeemType(value: PointMallProductEditorForm['redeemType']) {
options.form.redeemType = value;
if (value !== 'product') {
options.form.productId = '';
options.form.productName = '';
}
if (value !== 'coupon') {
options.form.couponTemplateId = '';
options.form.couponTemplateName = '';
}
if (value !== 'physical') {
options.form.physicalName = '';
options.form.pickupMethod = 'store_pickup';
}
if (value === 'physical') {
options.form.notifyChannels = ['in_app', 'sms'];
return;
}
if (!options.form.notifyChannels.includes('in_app')) {
options.form.notifyChannels = ['in_app'];
}
}
function setFormProduct(value: { id: string; name: string }) {
options.form.productId = value.id;
options.form.productName = value.name;
}
function setFormCouponTemplateId(value: string) {
options.form.couponTemplateId = value;
const selected = options.couponOptions.value.find(
(item) => item.value === value,
);
options.form.couponTemplateName = selected?.label ?? '';
}
function setFormPhysicalName(value: string) {
options.form.physicalName = value;
}
function setFormPickupMethod(
value: PointMallProductEditorForm['pickupMethod'],
) {
options.form.pickupMethod = value;
}
function setFormDescription(value: string) {
options.form.description = value;
}
function setFormExchangeType(
value: PointMallProductEditorForm['exchangeType'],
) {
options.form.exchangeType = value;
if (value === 'points') {
options.form.cashAmount = null;
}
}
function setFormRequiredPoints(value: null | number) {
options.form.requiredPoints = value;
}
function setFormCashAmount(value: null | number) {
options.form.cashAmount = value;
}
function setFormStockTotal(value: null | number) {
options.form.stockTotal = value;
}
function setFormPerMemberLimit(value: null | number) {
options.form.perMemberLimit = value;
}
function setFormStatus(value: PointMallProductEditorForm['status']) {
options.form.status = value;
}
function toggleNotifyChannel(value: 'in_app' | 'sms') {
if (options.form.notifyChannels.includes(value)) {
if (options.form.notifyChannels.length === 1) {
return;
}
options.form.notifyChannels = options.form.notifyChannels.filter(
(item) => item !== value,
);
return;
}
options.form.notifyChannels = [...options.form.notifyChannels, value];
}
async function openProductPicker() {
await options.openPicker(options.form.productId || undefined);
}
function validateForm() {
if (!options.form.name.trim()) {
message.warning('请填写展示名称');
return false;
}
if (!options.form.requiredPoints || options.form.requiredPoints <= 0) {
message.warning('所需积分必须大于 0');
return false;
}
if (options.form.stockTotal === null || options.form.stockTotal < 0) {
message.warning('库存数量不能小于 0');
return false;
}
if (options.form.redeemType === 'product' && !options.form.productId) {
message.warning('请选择关联商品');
return false;
}
if (
options.form.redeemType === 'coupon' &&
!options.form.couponTemplateId.trim()
) {
message.warning('请选择关联优惠券');
return false;
}
if (
options.form.redeemType === 'physical' &&
!options.form.physicalName.trim()
) {
message.warning('请填写实物名称');
return false;
}
if (
options.form.exchangeType === 'mixed' &&
(!options.form.cashAmount || options.form.cashAmount <= 0)
) {
message.warning('积分+现金模式下,现金部分必须大于 0');
return false;
}
if (options.form.notifyChannels.length === 0) {
message.warning('请至少选择一种通知方式');
return false;
}
return true;
}
async function submitDrawer() {
if (!options.canManage.value) {
return;
}
if (!options.selectedStoreId.value) {
message.warning('请先选择门店');
return;
}
if (!validateForm()) {
return;
}
options.isDrawerSubmitting.value = true;
try {
await saveMemberPointMallProductApi(
mapProductEditorFormToSavePayload(
options.form,
options.selectedStoreId.value,
),
);
message.success(
options.drawerMode.value === 'create' ? '添加成功' : '保存成功',
);
options.isDrawerOpen.value = false;
await options.loadProductList();
} catch (error) {
console.error(error);
message.error('保存失败');
} finally {
options.isDrawerSubmitting.value = false;
}
}
return {
loadCouponOptions,
openCreateDrawer,
openEditDrawer,
openProductPicker,
setDrawerOpen,
setFormCashAmount,
setFormCouponTemplateId,
setFormDescription,
setFormExchangeType,
setFormImageUrl,
setFormName,
setFormPerMemberLimit,
setFormPhysicalName,
setFormPickupMethod,
setFormProduct,
setFormRedeemType,
setFormRequiredPoints,
setFormStatus,
setFormStockTotal,
submitDrawer,
toggleNotifyChannel,
};
}