import type { ExportMemberPointMallRecordQuery, MemberPointMallProductDto, MemberPointMallRecordListQuery, SaveMemberPointMallProductPayload, SaveMemberPointMallRulePayload, } from '#/api/member/points-mall'; import type { PointMallProductEditorForm, PointMallRecordFilterForm, PointMallRuleForm, } from '#/views/member/points-mall/types'; import { createDefaultPointMallProductEditorForm, createDefaultPointMallRuleForm, } from '#/views/member/points-mall/types'; /** 数量格式化。 */ export function formatInteger(value: null | number | undefined) { const num = Number(value ?? 0); if (!Number.isFinite(num)) { return '0'; } return Math.round(num).toLocaleString('zh-CN'); } /** 积分格式化。 */ export function formatPoints(value: null | number | undefined) { return formatInteger(value); } /** 金额格式化。 */ export function formatCurrency(value: null | number | undefined) { const amount = Number(value ?? 0); if (Number.isNaN(amount)) { return '¥0.00'; } return `¥${amount.toFixed(2)}`; } /** 判断字符串是否为空。 */ export function isBlank(value: string | undefined) { return !value || value.trim().length === 0; } /** 规则 DTO -> 表单。 */ export function mapRuleToForm( rule: SaveMemberPointMallRulePayload, ): PointMallRuleForm { return { isConsumeRewardEnabled: Boolean(rule.isConsumeRewardEnabled), consumeAmountPerStep: Number(rule.consumeAmountPerStep), consumeRewardPointsPerStep: Number(rule.consumeRewardPointsPerStep), isReviewRewardEnabled: Boolean(rule.isReviewRewardEnabled), reviewRewardPoints: Number(rule.reviewRewardPoints), isRegisterRewardEnabled: Boolean(rule.isRegisterRewardEnabled), registerRewardPoints: Number(rule.registerRewardPoints), isSigninRewardEnabled: Boolean(rule.isSigninRewardEnabled), signinRewardPoints: Number(rule.signinRewardPoints), expiryMode: rule.expiryMode, }; } /** 规则表单 -> 保存 DTO。 */ export function mapRuleFormToSavePayload( form: PointMallRuleForm, storeId: string, ): SaveMemberPointMallRulePayload { return { storeId, isConsumeRewardEnabled: form.isConsumeRewardEnabled, consumeAmountPerStep: Number(form.consumeAmountPerStep ?? 0), consumeRewardPointsPerStep: Number(form.consumeRewardPointsPerStep ?? 0), isReviewRewardEnabled: form.isReviewRewardEnabled, reviewRewardPoints: Number(form.reviewRewardPoints ?? 0), isRegisterRewardEnabled: form.isRegisterRewardEnabled, registerRewardPoints: Number(form.registerRewardPoints ?? 0), isSigninRewardEnabled: form.isSigninRewardEnabled, signinRewardPoints: Number(form.signinRewardPoints ?? 0), expiryMode: form.expiryMode, }; } /** 重置规则表单。 */ export function resetRuleForm(form: PointMallRuleForm) { const defaults = createDefaultPointMallRuleForm(); form.isConsumeRewardEnabled = defaults.isConsumeRewardEnabled; form.consumeAmountPerStep = defaults.consumeAmountPerStep; form.consumeRewardPointsPerStep = defaults.consumeRewardPointsPerStep; form.isReviewRewardEnabled = defaults.isReviewRewardEnabled; form.reviewRewardPoints = defaults.reviewRewardPoints; form.isRegisterRewardEnabled = defaults.isRegisterRewardEnabled; form.registerRewardPoints = defaults.registerRewardPoints; form.isSigninRewardEnabled = defaults.isSigninRewardEnabled; form.signinRewardPoints = defaults.signinRewardPoints; form.expiryMode = defaults.expiryMode; } /** 商品 DTO -> 编辑表单。 */ export function mapProductToEditorForm( source: MemberPointMallProductDto, ): PointMallProductEditorForm { return { pointMallProductId: source.pointMallProductId, redeemType: source.redeemType, productId: source.productId ?? '', productName: '', couponTemplateId: source.couponTemplateId ?? '', couponTemplateName: '', physicalName: source.physicalName ?? '', pickupMethod: source.pickupMethod ?? 'store_pickup', name: source.name, imageUrl: source.imageUrl ?? '', exchangeType: source.exchangeType, requiredPoints: source.requiredPoints, cashAmount: source.exchangeType === 'mixed' ? source.cashAmount : null, stockTotal: source.stockTotal, perMemberLimit: source.perMemberLimit ?? null, description: source.description ?? '', notifyChannels: [...source.notifyChannels], status: source.status, }; } /** 商品表单 -> 保存 DTO。 */ export function mapProductEditorFormToSavePayload( form: PointMallProductEditorForm, storeId: string, ): SaveMemberPointMallProductPayload { return { storeId, pointMallProductId: form.pointMallProductId || undefined, name: form.name.trim(), imageUrl: form.imageUrl.trim() || undefined, redeemType: form.redeemType, productId: form.productId || undefined, couponTemplateId: form.couponTemplateId || undefined, physicalName: form.physicalName.trim() || undefined, pickupMethod: form.redeemType === 'physical' ? form.pickupMethod : undefined, description: form.description.trim() || undefined, exchangeType: form.exchangeType, requiredPoints: Number(form.requiredPoints ?? 0), cashAmount: Number(form.cashAmount ?? 0), stockTotal: Number(form.stockTotal ?? 0), perMemberLimit: form.perMemberLimit && form.perMemberLimit > 0 ? Number(form.perMemberLimit) : undefined, notifyChannels: [...form.notifyChannels], status: form.status, }; } /** 重置商品编辑表单。 */ export function resetProductEditorForm(form: PointMallProductEditorForm) { const defaults = createDefaultPointMallProductEditorForm(); form.pointMallProductId = defaults.pointMallProductId; form.redeemType = defaults.redeemType; form.productId = defaults.productId; form.productName = defaults.productName; form.couponTemplateId = defaults.couponTemplateId; form.couponTemplateName = defaults.couponTemplateName; form.physicalName = defaults.physicalName; form.pickupMethod = defaults.pickupMethod; form.name = defaults.name; form.imageUrl = defaults.imageUrl; form.exchangeType = defaults.exchangeType; form.requiredPoints = defaults.requiredPoints; form.cashAmount = defaults.cashAmount; form.stockTotal = defaults.stockTotal; form.perMemberLimit = defaults.perMemberLimit; form.description = defaults.description; form.notifyChannels = [...defaults.notifyChannels]; form.status = defaults.status; } /** 记录筛选项映射。 */ export function mapRecordFilterToQuery( filterForm: PointMallRecordFilterForm, ): Pick< ExportMemberPointMallRecordQuery & MemberPointMallRecordListQuery, 'endDate' | 'keyword' | 'redeemType' | 'startDate' | 'status' > { const query: Pick< ExportMemberPointMallRecordQuery & MemberPointMallRecordListQuery, 'endDate' | 'keyword' | 'redeemType' | 'startDate' | 'status' > = {}; if (filterForm.redeemType) { query.redeemType = filterForm.redeemType; } if (filterForm.status) { query.status = filterForm.status; } const keyword = filterForm.keyword.trim(); if (keyword) { query.keyword = keyword; } if (filterForm.dateRange && filterForm.dateRange.length === 2) { query.startDate = filterForm.dateRange[0].format('YYYY-MM-DD'); query.endDate = filterForm.dateRange[1].format('YYYY-MM-DD'); } return query; } /** base64 下载。 */ export function downloadBase64File( fileName: string, fileContentBase64: string, ) { const binary = atob(fileContentBase64); const length = binary.length; const bytes = new Uint8Array(length); for (let index = 0; index < length; index++) { bytes[index] = binary.codePointAt(index) ?? 0; } const blob = new Blob([bytes], { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(blob); const anchor = document.createElement('a'); anchor.href = url; anchor.download = fileName; document.body.append(anchor); anchor.click(); anchor.remove(); URL.revokeObjectURL(url); }