235 lines
7.8 KiB
TypeScript
235 lines
7.8 KiB
TypeScript
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);
|
|
}
|