257 lines
9.0 KiB
TypeScript
257 lines
9.0 KiB
TypeScript
import { useCallback, useMemo, useState } from 'react';
|
|
import { View, Text, Image } from '@tarojs/components';
|
|
import Taro, { useDidShow, usePullDownRefresh, useShareAppMessage, useShareTimeline } from '@tarojs/taro';
|
|
import PageShell from '../../components/PageShell';
|
|
import WechatShareReady from '../../components/WechatShareReady';
|
|
import UserTabBar, { shouldRenderPageTabBar, syncTabBarSelected } from '../../components/UserTabBar';
|
|
import BenefitFigure from '../../components/BenefitFigure';
|
|
import { goLogin } from '../../lib/auth-nav';
|
|
import { isLoggedIn, request, toast } from '../../lib/api';
|
|
import { navBarStyle, tabNavContentStyle, useNavBarMetrics } from '../../lib/nav-bar';
|
|
import {
|
|
buildSceneSharePayload,
|
|
toWeappShareMessage,
|
|
toWeappShareTimeline,
|
|
} from '../../lib/wechat-share';
|
|
import { formatMoney } from '../../lib/money';
|
|
import iconBenefit from '../../assets/tabbar/benefit-active.png';
|
|
|
|
type BenefitSummary = {
|
|
totalBalance: number;
|
|
maxRedeemAmount: number;
|
|
activeCouponCount: number;
|
|
};
|
|
|
|
type CouponItem = {
|
|
id: string;
|
|
couponNo: string;
|
|
totalAmount: number;
|
|
usedAmount: number;
|
|
balance: number;
|
|
status: string;
|
|
sourceProduct: string;
|
|
};
|
|
|
|
type RedeemHistoryItem = {
|
|
id: string;
|
|
redeemNo: string;
|
|
amount: number;
|
|
storeName: string;
|
|
createdAt: string;
|
|
};
|
|
|
|
function usagePercent(coupon: CouponItem) {
|
|
const total = Number(coupon.totalAmount);
|
|
if (total <= 0) return 0;
|
|
return Math.min(100, Math.round((Number(coupon.usedAmount) / total) * 100));
|
|
}
|
|
|
|
export default function BenefitPage() {
|
|
const metrics = useNavBarMetrics();
|
|
const [loggedIn, setLoggedIn] = useState(() => isLoggedIn());
|
|
const [summary, setSummary] = useState<BenefitSummary | null>(null);
|
|
const [coupons, setCoupons] = useState<CouponItem[]>([]);
|
|
const [redeemHistory, setRedeemHistory] = useState<RedeemHistoryItem[]>([]);
|
|
const [tab, setTab] = useState<'available' | 'history'>('available');
|
|
|
|
const resetGuestState = useCallback(() => {
|
|
setSummary(null);
|
|
setCoupons([]);
|
|
setRedeemHistory([]);
|
|
}, []);
|
|
|
|
const loadBenefit = useCallback(() => {
|
|
if (!isLoggedIn()) return Promise.resolve();
|
|
return Promise.all([
|
|
request<BenefitSummary>('/benefit/summary'),
|
|
request<CouponItem[]>('/benefit/coupons'),
|
|
request<{ list?: RedeemHistoryItem[] } | RedeemHistoryItem[]>('/redeem/records?page=1&pageSize=50'),
|
|
])
|
|
.then(([s, list, records]) => {
|
|
setSummary(s);
|
|
setCoupons(Array.isArray(list) ? list : []);
|
|
const hist = Array.isArray(records)
|
|
? records
|
|
: Array.isArray(records?.list)
|
|
? records.list
|
|
: [];
|
|
setRedeemHistory(hist);
|
|
})
|
|
.catch((e) => toast(e instanceof Error ? e.message : '加载失败'));
|
|
}, []);
|
|
|
|
useDidShow(() => {
|
|
syncTabBarSelected(2);
|
|
const loggedInNow = isLoggedIn();
|
|
setLoggedIn(loggedInNow);
|
|
if (loggedInNow) {
|
|
void loadBenefit();
|
|
} else {
|
|
resetGuestState();
|
|
}
|
|
});
|
|
|
|
usePullDownRefresh(() => {
|
|
const loggedInNow = isLoggedIn();
|
|
setLoggedIn(loggedInNow);
|
|
if (!loggedInNow) {
|
|
resetGuestState();
|
|
Taro.stopPullDownRefresh();
|
|
return;
|
|
}
|
|
void loadBenefit().finally(() => Taro.stopPullDownRefresh());
|
|
});
|
|
|
|
const available = coupons.filter((c) => c.status === 'ACTIVE' && c.balance > 0);
|
|
|
|
const sharePayload = useMemo(
|
|
() =>
|
|
buildSceneSharePayload('benefit', {
|
|
path: '/pages/benefit/index',
|
|
}),
|
|
[],
|
|
);
|
|
|
|
useShareAppMessage(() => toWeappShareMessage(sharePayload));
|
|
useShareTimeline(() => toWeappShareTimeline(sharePayload));
|
|
|
|
return (
|
|
<PageShell variant="tab" className="benefit-page">
|
|
<WechatShareReady payload={sharePayload} />
|
|
<View className="benefit-header" style={navBarStyle(metrics)} aria-label="好客权益">
|
|
{process.env.TARO_ENV !== 'h5' ? (
|
|
<Text className="benefit-header-title">好客权益</Text>
|
|
) : null}
|
|
<View
|
|
className="benefit-header__content"
|
|
style={tabNavContentStyle(metrics)}
|
|
>
|
|
<View className="benefit-header-city">
|
|
<View className="benefit-header-city-pin" />
|
|
<Text>郑州市</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{!loggedIn ? (
|
|
<View className="benefit-login-gate">
|
|
<View className="u-empty">登录后查看好客权益余额</View>
|
|
<View
|
|
className="u-btn u-btn--block"
|
|
style={{ maxWidth: 240, margin: '0 auto' }}
|
|
onClick={() => goLogin('/pages/benefit/index')}
|
|
>
|
|
<Text>去登录</Text>
|
|
</View>
|
|
</View>
|
|
) : (
|
|
<View className="benefit-main">
|
|
<View className="benefit-hero">
|
|
<View className="benefit-hero-top">
|
|
<View>
|
|
<Text className="benefit-hero-label">当前好客权益余额</Text>
|
|
<View className="benefit-hero-amount">
|
|
<BenefitFigure
|
|
value={summary ? formatMoney(summary.totalBalance) : '--'}
|
|
size="xl"
|
|
className="benefit-hero-value"
|
|
/>
|
|
</View>
|
|
</View>
|
|
<View className="benefit-hero-logo">
|
|
<Image className="benefit-hero-logo-img" src={iconBenefit} mode="aspectFit" />
|
|
</View>
|
|
</View>
|
|
<View
|
|
className="benefit-hero-cta"
|
|
onClick={() => Taro.navigateTo({ url: '/pages/redeem/index' })}
|
|
>
|
|
<Text>去使用</Text>
|
|
</View>
|
|
</View>
|
|
|
|
<View className="benefit-tabs">
|
|
<Text
|
|
className={`benefit-tab${tab === 'available' ? ' benefit-tab--active' : ''}`}
|
|
onClick={() => setTab('available')}
|
|
>
|
|
可用权益
|
|
</Text>
|
|
<Text
|
|
className={`benefit-tab${tab === 'history' ? ' benefit-tab--active' : ''}`}
|
|
onClick={() => setTab('history')}
|
|
>
|
|
历史记录
|
|
</Text>
|
|
</View>
|
|
|
|
{tab === 'available' ? (
|
|
available.length === 0 ? (
|
|
<View className="u-empty">暂无可用权益</View>
|
|
) : (
|
|
available.map((c) => (
|
|
<View key={c.id} className="benefit-coupon">
|
|
<View className="benefit-coupon-notch" />
|
|
<View className="benefit-coupon-notch benefit-coupon-notch--right" />
|
|
<View className="benefit-coupon-head">
|
|
<Text className="benefit-coupon-name">{c.sourceProduct || '好客权益'}</Text>
|
|
<BenefitFigure value={formatMoney(c.balance)} size="md" className="benefit-coupon-balance" />
|
|
</View>
|
|
<Text className="benefit-coupon-no">NO. {c.couponNo}</Text>
|
|
<View className="benefit-progress">
|
|
<View className="benefit-progress-bar" style={{ width: `${usagePercent(c)}%` }} />
|
|
</View>
|
|
<View className="benefit-coupon-footer">
|
|
<View className="benefit-coupon-meta">
|
|
<Text>已用</Text>
|
|
<BenefitFigure value={formatMoney(c.usedAmount)} size="sm" />
|
|
<Text>/ 总额</Text>
|
|
<BenefitFigure value={formatMoney(c.totalAmount)} size="sm" />
|
|
</View>
|
|
<Text
|
|
className="benefit-coupon-btn"
|
|
onClick={() =>
|
|
Taro.navigateTo({
|
|
url: `/pages/redeem/index?couponId=${c.id}&amount=${c.balance}`,
|
|
})
|
|
}
|
|
>
|
|
立即核销
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
))
|
|
)
|
|
) : redeemHistory.length === 0 ? (
|
|
<View className="u-empty">暂无核销记录</View>
|
|
) : (
|
|
redeemHistory.map((r) => (
|
|
<View key={r.id} className="benefit-coupon">
|
|
<View className="benefit-coupon-notch" />
|
|
<View className="benefit-coupon-notch benefit-coupon-notch--right" />
|
|
<View className="benefit-coupon-head">
|
|
<Text className="benefit-coupon-name">{r.storeName || '门店核销'}</Text>
|
|
<BenefitFigure
|
|
prefix="-"
|
|
value={formatMoney(Number(r.amount))}
|
|
size="md"
|
|
className="benefit-coupon-balance"
|
|
/>
|
|
</View>
|
|
<Text className="benefit-coupon-no">NO. {r.redeemNo}</Text>
|
|
<View className="benefit-coupon-footer">
|
|
<Text className="benefit-coupon-meta">
|
|
{r.createdAt ? String(r.createdAt).slice(0, 19).replace('T', ' ') : '-'}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
))
|
|
)}
|
|
</View>
|
|
)}
|
|
|
|
{shouldRenderPageTabBar() ? <UserTabBar selected={2} /> : null}
|
|
</PageShell>
|
|
);
|
|
}
|