feat(assoc): v4.0.1 合伙人关联码、分佣账单与 H5 用户管理

订单佣金只认关联用户;合伙人备注写入独立表;H5 增加用户管理与首页统计。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-30 14:35:39 +08:00
parent 3b669f7e38
commit 9c8d5f2cad
125 changed files with 6355 additions and 1436 deletions
+68 -36
View File
@@ -1,5 +1,5 @@
import { useCallback, useMemo, useState } from 'react';
import { View, Text, Image } from '@tarojs/components';
import { View, Text } from '@tarojs/components';
import Taro, { useDidShow, usePullDownRefresh, useShareAppMessage, useShareTimeline } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import WechatShareReady from '../../components/WechatShareReady';
@@ -7,14 +7,16 @@ import UserTabBar, { shouldRenderPageTabBar, syncTabBarSelected } from '../../co
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 { navBarStyle, 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';
import { BENEFIT_SLOGAN } from '../../lib/benefit-copy';
import BenefitIntroCard from '../../components/BenefitIntroCard';
import type { StoreRatingDto } from '@dukang/shared-types';
type BenefitSummary = {
totalBalance: number;
@@ -36,10 +38,30 @@ type RedeemHistoryItem = {
id: string;
redeemNo: string;
amount: number;
storeId?: string;
storeName: string;
createdAt: string;
rating?: StoreRatingDto | null;
};
const BENEFIT_TAB_KEY = 'dukang_benefit_tab';
function readBenefitTab(): 'available' | 'history' {
try {
return Taro.getStorageSync(BENEFIT_TAB_KEY) === 'history' ? 'history' : 'available';
} catch {
return 'available';
}
}
function writeBenefitTab(next: 'available' | 'history') {
try {
Taro.setStorageSync(BENEFIT_TAB_KEY, next);
} catch {
/* ignore */
}
}
function usagePercent(coupon: CouponItem) {
const total = Number(coupon.totalAmount);
if (total <= 0) return 0;
@@ -52,7 +74,7 @@ export default function BenefitPage() {
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 [tab, setTab] = useState<'available' | 'history'>(readBenefitTab);
const resetGuestState = useCallback(() => {
setSummary(null);
@@ -119,18 +141,7 @@ export default function BenefitPage() {
<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>
<Text className="benefit-header-title"></Text>
</View>
{!loggedIn ? (
@@ -146,40 +157,50 @@ export default function BenefitPage() {
</View>
) : (
<View className="benefit-main">
<BenefitIntroCard className="benefit-intro-card--page" />
<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" />
<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-cta"
onClick={() => Taro.navigateTo({ url: '/pages/redeem/index' })}
>
<Text>使</Text>
<View className="benefit-hero-actions">
<View
className="benefit-hero-cta benefit-hero-cta--primary"
onClick={() => Taro.switchTab({ url: '/pages/home/index' })}
>
<Text></Text>
</View>
<View
className="benefit-hero-cta benefit-hero-cta--secondary"
onClick={() => Taro.navigateTo({ url: '/pages/redeem/index' })}
>
<Text>使</Text>
</View>
</View>
</View>
<View className="benefit-tabs">
<Text
className={`benefit-tab${tab === 'available' ? ' benefit-tab--active' : ''}`}
onClick={() => setTab('available')}
onClick={() => {
setTab('available');
writeBenefitTab('available');
}}
>
</Text>
<Text
className={`benefit-tab${tab === 'history' ? ' benefit-tab--active' : ''}`}
onClick={() => setTab('history')}
onClick={() => {
setTab('history');
writeBenefitTab('history');
}}
>
</Text>
@@ -187,7 +208,7 @@ export default function BenefitPage() {
{tab === 'available' ? (
available.length === 0 ? (
<View className="u-empty"></View>
<View className="u-empty">{BENEFIT_SLOGAN}</View>
) : (
available.map((c) => (
<View key={c.id} className="benefit-coupon">
@@ -243,6 +264,17 @@ export default function BenefitPage() {
<Text className="benefit-coupon-meta">
{r.createdAt ? String(r.createdAt).slice(0, 19).replace('T', ' ') : '-'}
</Text>
<Text
className={`benefit-coupon-btn${r.rating ? ' benefit-coupon-btn--ghost' : ''}`}
onClick={() => {
writeBenefitTab('history');
Taro.navigateTo({
url: `/pages/redeem-success/index?id=${r.id}&from=history`,
});
}}
>
{r.rating ? '已评价' : '去评价'}
</Text>
</View>
</View>
))