登录页面

This commit is contained in:
2026-07-12 15:17:54 +08:00
parent becf91da52
commit bfac6c6663
63 changed files with 4662 additions and 431 deletions
@@ -0,0 +1,177 @@
import { useEffect, useState } from 'react';
import { View, Text, Image } from '@tarojs/components';
import Taro, { usePageScroll, useRouter } from '@tarojs/taro';
import type { ProductDetailContentDto } from '@dukang/shared-types';
import PageShell from '../../components/PageShell';
import PageNavBar from '../../components/PageNavBar';
import ProductCarousel from '../../components/ProductCarousel';
import { request, toast } from '../../lib/api';
import {
getProductCarouselImages,
getProductDetailImages,
type ProductImageSource,
} from '../../lib/product-images';
import iconHome from '../../assets/tabbar/home.png';
type Product = ProductImageSource & {
id: string;
name: string;
subtitle?: string | null;
price: number;
benefitAmount?: number;
benefitDisplay?: number;
detailContent?: ProductDetailContentDto | null;
};
export default function ProductDetailPage() {
const router = useRouter();
const productId = router.params.id ?? '';
const [product, setProduct] = useState<Product | null>(null);
const [headerSolid, setHeaderSolid] = useState(false);
usePageScroll(({ scrollTop }) => {
setHeaderSolid(scrollTop > 100);
});
useEffect(() => {
if (!productId) return;
request<Product>(`/catalog/products/${productId}`)
.then(setProduct)
.catch((e) => toast(e instanceof Error ? e.message : '加载失败'));
}, [productId]);
function goBack() {
const pages = Taro.getCurrentPages();
if (pages.length > 1) {
Taro.navigateBack();
return;
}
Taro.switchTab({ url: '/pages/home/index' });
}
function goHome() {
Taro.switchTab({ url: '/pages/home/index' });
}
function goBuy() {
if (!productId) return;
Taro.navigateTo({ url: `/pages/order-confirm/index?productId=${productId}&qty=2` });
}
if (!product) {
return (
<PageShell variant="scroll" className="product-detail-page">
<PageNavBar title="商品详情" solid onBack={goBack} />
<View className="page-with-nav-bar u-empty"></View>
</PageShell>
);
}
const benefit = Number(product.benefitDisplay ?? product.benefitAmount ?? product.price);
const carouselImages = getProductCarouselImages(product);
const detailImages = getProductDetailImages(product);
const detail = product.detailContent ?? {};
const features = detail.features ?? [];
return (
<PageShell variant="scroll" className="product-detail-page" hasFixedFooter>
<PageNavBar
title={product.name}
solid={headerSolid}
titleVisible={headerSolid}
onBack={goBack}
right={(
<View className="page-nav-bar__btn" onClick={() => toast('分享功能开发中')}>
<Text className="page-nav-bar__icon page-nav-bar__icon--share"></Text>
</View>
)}
/>
<View className="product-detail-main">
<View className="product-detail-hero full-bleed">
<ProductCarousel images={carouselImages} alt={product.name} variant="detail" />
</View>
<View className="product-detail-info">
<View className="product-detail-price">
<Text className="product-detail-price-symbol">¥</Text>
<Text className="product-detail-price-value">{Number(product.price).toFixed(2)}</Text>
</View>
<Text className="product-detail-name">{product.name}</Text>
{product.subtitle ? (
<Text className="product-detail-subtitle">{product.subtitle}</Text>
) : null}
<View className="product-detail-promo">
<View className="product-detail-promo-glow" />
<View className="product-detail-promo-head">
<View className="product-detail-promo-icon">
<Text className="product-detail-promo-icon-text"></Text>
</View>
<Text className="product-detail-promo-title">
·
<Text className="product-detail-promo-amount"> ¥{benefit}</Text>
</Text>
</View>
<Text className="product-detail-promo-desc">
</Text>
</View>
</View>
<View className="product-detail-content">
<View className="product-detail-section-head">
<View className="product-detail-section-bar" />
<Text className="product-detail-section-title"></Text>
</View>
{detailImages.map((src, index) => (
<Image
key={`${src}-${index}`}
className="product-detail-banner full-bleed"
src={src}
mode="widthFix"
/>
))}
{(detail.storyTitle || detail.storyText || features.length > 0) ? (
<View className="product-detail-copy">
{(detail.storyTitle || detail.storyText) ? (
<View className="product-detail-story">
{detail.storyTitle ? (
<Text className="product-detail-story-title">{detail.storyTitle}</Text>
) : null}
{detail.storyText ? (
<Text className="product-detail-story-text">{detail.storyText}</Text>
) : null}
</View>
) : null}
{features.length > 0 ? (
<View className="product-detail-features">
{features.map((f) => (
<View key={`${f.title}-${f.icon}`} className="product-detail-feature">
<Text className="product-detail-feature-icon"></Text>
<Text className="product-detail-feature-title">{f.title}</Text>
<Text className="product-detail-feature-desc">{f.desc}</Text>
</View>
))}
</View>
) : null}
</View>
) : null}
</View>
</View>
<View className="product-detail-bar">
<View className="product-detail-bar-home" onClick={goHome}>
<Image className="product-detail-bar-home-icon" src={iconHome} mode="aspectFit" />
<Text className="product-detail-bar-home-label"></Text>
</View>
<View className="product-detail-buy-btn" onClick={goBuy}>
<Text className="product-detail-buy-btn-text"></Text>
</View>
</View>
</PageShell>
);
}