162 lines
4.7 KiB
TypeScript
162 lines
4.7 KiB
TypeScript
import { useMemo, useState } from 'react';
|
||
import { View, Text } from '@tarojs/components';
|
||
import '../../styles/redeem.css';
|
||
import Taro, { useRouter } from '@tarojs/taro';
|
||
import PageShell from '../../components/PageShell';
|
||
import SubPageHeader from '../../components/SubPageHeader';
|
||
import { request, toast } from '../../lib/api';
|
||
import { formatShanghaiDateTime } from '../../lib/datetime';
|
||
import { formatMoney, toMoneyNumber } from '../../lib/money';
|
||
import BenefitFigure from '../../components/BenefitFigure';
|
||
|
||
const LAST_REDEEM_RESULT_KEY = 'lastRedeemResult';
|
||
|
||
type RedeemRecord = {
|
||
id: string;
|
||
redeemNo: string;
|
||
amount: number;
|
||
storeId: string;
|
||
storeName: string;
|
||
createdAt: string;
|
||
};
|
||
|
||
/** 与权益「历史记录」一致:2026-08-03 13:53:03(Asia/Shanghai) */
|
||
function formatChinaDateTime(input?: string | null) {
|
||
return formatShanghaiDateTime(input ?? new Date());
|
||
}
|
||
|
||
function StarRating({
|
||
label,
|
||
value,
|
||
onChange,
|
||
}: {
|
||
label: string;
|
||
value: number;
|
||
onChange: (score: number) => void;
|
||
}) {
|
||
return (
|
||
<View className="redeem-rating-row">
|
||
<Text className="redeem-rating-label">{label}</Text>
|
||
<View className="redeem-star-row">
|
||
{[1, 2, 3, 4, 5].map((score) => (
|
||
<Text
|
||
key={score}
|
||
className={`redeem-star-btn${score <= value ? ' redeem-star-btn--active' : ''}`}
|
||
onClick={() => onChange(score)}
|
||
>
|
||
★
|
||
</Text>
|
||
))}
|
||
</View>
|
||
</View>
|
||
);
|
||
}
|
||
|
||
export default function RedeemSuccessPage() {
|
||
const router = useRouter();
|
||
const [serviceScore, setServiceScore] = useState(5);
|
||
const [envScore, setEnvScore] = useState(5);
|
||
const [loading, setLoading] = useState(false);
|
||
|
||
const record = useMemo<RedeemRecord | null>(() => {
|
||
try {
|
||
const cached = Taro.getStorageSync(LAST_REDEEM_RESULT_KEY);
|
||
return cached ? (JSON.parse(String(cached)) as RedeemRecord) : null;
|
||
} catch {
|
||
return null;
|
||
}
|
||
}, []);
|
||
|
||
const amount = toMoneyNumber(record?.amount ?? router.params.amount);
|
||
const storeName = record?.storeName || '门店';
|
||
const redeemNo = record?.redeemNo || '—';
|
||
const redeemedAt = formatChinaDateTime(record?.createdAt);
|
||
|
||
function clearCache() {
|
||
try {
|
||
Taro.removeStorageSync(LAST_REDEEM_RESULT_KEY);
|
||
} catch {
|
||
/* ignore */
|
||
}
|
||
}
|
||
|
||
function goBenefit() {
|
||
clearCache();
|
||
Taro.switchTab({ url: '/pages/benefit/index' });
|
||
}
|
||
|
||
function goHome() {
|
||
clearCache();
|
||
Taro.switchTab({ url: '/pages/home/index' });
|
||
}
|
||
|
||
async function submitRatingAndFinish() {
|
||
setLoading(true);
|
||
try {
|
||
if (record?.id) {
|
||
await request('/redeem/ratings', {
|
||
method: 'POST',
|
||
data: {
|
||
redeemRecordId: record.id,
|
||
serviceScore,
|
||
envScore,
|
||
},
|
||
});
|
||
toast('评价已提交', 'success');
|
||
}
|
||
} catch {
|
||
/* 评价失败不阻塞返回 */
|
||
} finally {
|
||
setLoading(false);
|
||
goBenefit();
|
||
}
|
||
}
|
||
|
||
return (
|
||
<PageShell variant="sub" className="redeem-success-page">
|
||
<SubPageHeader title="核销成功" onBack={goBenefit} />
|
||
<View className="sub-page-body">
|
||
<View className="redeem-success-icon">
|
||
<Text>✓</Text>
|
||
</View>
|
||
<Text className="redeem-success-title">核销成功</Text>
|
||
<BenefitFigure value={formatMoney(amount)} size="lg" className="redeem-success-amount" />
|
||
<Text className="redeem-success-desc">已在 {storeName} 完成核销</Text>
|
||
|
||
<View className="redeem-success-details">
|
||
<View className="redeem-success-detail-row">
|
||
<Text>核销门店</Text>
|
||
<Text>{storeName}</Text>
|
||
</View>
|
||
<View className="redeem-success-detail-row">
|
||
<Text>核销时间</Text>
|
||
<Text>{redeemedAt}</Text>
|
||
</View>
|
||
<View className="redeem-success-detail-row">
|
||
<Text>核销单号</Text>
|
||
<Text className="redeem-success-mono">{redeemNo}</Text>
|
||
</View>
|
||
</View>
|
||
|
||
<View className="redeem-success-rating">
|
||
<Text className="redeem-success-rating-title">为门店服务评分</Text>
|
||
<StarRating label="服务态度" value={serviceScore} onChange={setServiceScore} />
|
||
<StarRating label="用餐环境" value={envScore} onChange={setEnvScore} />
|
||
</View>
|
||
|
||
<View
|
||
className={`redeem-submit${loading ? ' redeem-submit--disabled' : ''}`}
|
||
onClick={() => {
|
||
if (!loading) void submitRatingAndFinish();
|
||
}}
|
||
>
|
||
<Text>{loading ? '提交中…' : '提交评价并返回'}</Text>
|
||
</View>
|
||
<View className="redeem-cancel-btn" onClick={goHome}>
|
||
<Text>回到首页</Text>
|
||
</View>
|
||
</View>
|
||
</PageShell>
|
||
);
|
||
}
|