| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- import React, { useState, useEffect } from 'react';
- import { View } from '@tarojs/components';
- import { useQuery } from '@tanstack/react-query';
- import { goodsCategoryClient, advertisementClient } from '@/api';
- import CategorySidebar from '@/components/category/CategorySidebar';
- import CategorySidebarItem from '@/components/category/CategorySidebarItem';
- import CategoryTabbar, { TabItem } from '@/components/category/CategoryTabbar';
- import { Image } from '@/components/ui/image';
- import TDesignToast from '@/components/tdesign/toast';
- import Taro,{ useRouter, navigateTo,useShareAppMessage,useShareTimeline } from '@tarojs/taro';
- import { InferResponseType } from 'hono';
- import { TabBarLayout } from '@/layouts/tab-bar-layout';
- import { Navbar } from '@/components/ui/navbar';
- import TDesignIcon from '@/components/tdesign/icon';
- import './index.css';
- type GoodsCategoryResponse = InferResponseType<typeof goodsCategoryClient.$get, 200>
- type Category = GoodsCategoryResponse['data'][0]
- type AdvertisementResponse = InferResponseType<typeof advertisementClient.$get, 200>
- type Advertisement = AdvertisementResponse['data'][0]
- const CategoryPage: React.FC = () => {
- const [activeCategoryIndex, setActiveCategoryIndex] = useState<number>(0);
- const [activeSubCategoryId, setActiveSubCategoryId] = useState<string>('');
- const [toastVisible, setToastVisible] = useState<boolean>(false);
- const [toastMessage, setToastMessage] = useState<string>('');
- // 使用useRouter钩子获取路由参数
- const router = useRouter()
- const params = router.params
- const goodsId = params?.id ? parseInt(params.id) : 0
- const fromPage = params?.from || ''
- // 动态设置导航栏和tabbar高度
- useEffect(() => {
- const setDynamicHeights = () => {
- try {
- // 获取窗口信息
- const windowInfo = Taro.getWindowInfo();
- // 计算导航栏高度(状态栏高度 + 导航栏高度)
- // 状态栏高度已经是rpx单位,导航栏高度通常为88rpx
- const navbarHeightRpx = windowInfo.statusBarHeight + 88;
- // 计算tabbar高度(通常为100rpx)
- const tabbarHeightRpx = 100; // 标准tabbar高度
- // 设置CSS变量
- const rootElement = document.documentElement;
- if (rootElement) {
- rootElement.style.setProperty('--navbar-height', `${navbarHeightRpx}rpx`);
- rootElement.style.setProperty('--tabbar-height', `${tabbarHeightRpx}rpx`);
- }
- } catch (error) {
- console.error('设置动态高度失败:', error);
- // 使用默认值作为fallback
- const rootElement = document.documentElement;
- if (rootElement) {
- rootElement.style.setProperty('--navbar-height', '96rpx');
- rootElement.style.setProperty('--tabbar-height', '100rpx');
- }
- }
- };
- setDynamicHeights();
- }, []);
- // 分享功能
- useShareAppMessage(() => {
- // 如果有当前选中的分类,使用分类图片作为分享图片
- // 如果没有,使用默认图片或空字符串
- const shareImageUrl = currentCategory?.imageFile?.fullUrl ||
- subCategories?.[0]?.imageFile?.fullUrl ||
- '';
- return {
- title: currentCategory ? `${currentCategory.name} - 发现好物` : '商品分类 - 发现好物',
- path: `/pages/category/index?from=share`,
- imageUrl: shareImageUrl
- }
- })
- // 分享到朋友圈功能
- useShareTimeline(() => {
- const shareImageUrl = currentCategory?.imageFile?.fullUrl ||
- subCategories?.[0]?.imageFile?.fullUrl ||
- '';
- return {
- title: currentCategory ? `${currentCategory.name} - 发现好物` : '商品分类 - 发现好物',
- path: `/pages/category/index?from=share`,
- imageUrl: shareImageUrl
- }
- })
- // 获取分类数据
- const { data: categoryData, isLoading, error } = useQuery({
- queryKey: ['goods-categories'],
- queryFn: async () => {
- const response = await goodsCategoryClient.$get({
- query: {
- page: 1,
- pageSize: 100,
- filters: JSON.stringify({ level: 1, state: 1 }), // 只显示启用的分类
- sortBy: 'sort',
- sortOrder: 'DESC'
- }
- });
- if (response.status !== 200) {
- throw new Error('获取分类数据失败');
- }
- return response.json();
- },
- staleTime: 5 * 60 * 1000, // 5分钟缓存
- });
- // 显示错误Toast
- React.useEffect(() => {
- if (error) {
- setToastMessage('获取分类数据失败,请重试');
- setToastVisible(true);
- }
- }, [error]);
- // 获取广告数据
- const { data: advertisementData } = useQuery({
- queryKey: ['category-advertisements'],
- queryFn: async () => {
- const response = await advertisementClient.$get({
- query: {
- filters: JSON.stringify({ status: 1, typeId: 2 }), // 过滤启用的分类页广告
- sortBy: 'sort',
- sortOrder: 'ASC'
- }
- });
- if (response.status !== 200) {
- console.debug('分类页广告API响应状态:', response.status, response.statusText);
- throw new Error(`获取广告数据失败 (状态码: ${response.status})`);
- }
- return response.json();
- },
- staleTime: 5 * 60 * 1000
- });
- //console.log("categoryData:",categoryData);
- const categories = categoryData?.data || [];
- const advertisements = advertisementData?.data || [];
- // 当前选中的一级分类
- const currentCategory = categories[activeCategoryIndex];
- // 当前一级分类的子分类, 当前先用当前选中的分类自身代替
- const subCategories = currentCategory ? categories.filter(item => item.id === currentCategory.id) : [];
- // 当前一级分类的子分类
- // const subCategories = currentCategory?.child_cate || [];
- // 广告图片
- const advertisementImage = advertisements[0]?.imageFile?.fullUrl || '';
- // 处理一级分类切换
- const handleCategoryChange = (index: number) => {
- setActiveCategoryIndex(index);
- // 重置二级分类选中状态
- setActiveSubCategoryId('');
- };
- // 处理二级分类切换
- const handleSubCategoryChange = (id: string) => {
- setActiveSubCategoryId(id);
- };
- // 处理分类跳转
- const handleCategoryClick = (categoryId: string) => {
- navigateTo({
- url: `/pages/goods-list/index?cateId=${encodeURIComponent(categoryId)}`,
- });
- };
- // 处理二级分类点击
- const handleSubCategoryClick = (category: Category) => {
- handleCategoryClick(String(category.id));
- };
- if (isLoading) {
- return (
- <TabBarLayout activeKey="category">
- <View className="category-page">
- <View className="loading">加载中...</View>
- </View>
- </TabBarLayout>
- );
- }
- if (error) {
- return (
- <TabBarLayout activeKey="category">
- <View className="category-page">
- <View className="error">加载失败,请重试</View>
- </View>
- </TabBarLayout>
- );
- }
- return (
- <TabBarLayout activeKey="category">
- <Navbar
- title="商品分类"
- leftIcon=""
- onClickLeft={() => {
- // 根据来源页面决定返回逻辑
- // 分享,返回首页
- if (fromPage === 'share') {
- Taro.switchTab({
- url: `/pages/index/index`
- })
- } else {
- // 从其他页面来的
- Taro.navigateBack()
- }
- }}
- rightIcon=""
- onClickRight={() => {}}
- />
- <View className="category-page">
- {/* Toast 组件 */}
- <TDesignToast
- visible={toastVisible}
- message={toastMessage}
- theme="error"
- duration={2000}
- onClose={() => setToastVisible(false)}
- />
- <View className="category-container">
- {/* 左侧边栏 - 一级分类 */}
- <View className="category-sidebar-container">
- <CategorySidebar
- activeKey={activeCategoryIndex}
- onChange={handleCategoryChange}
- >
- {categories.map((category: Category, index: number) => (
- <CategorySidebarItem
- key={category.id}
- title={category.name}
- onClick={() => handleCategoryChange(index)}
- />
- ))}
- </CategorySidebar>
- </View>
- {/* 右侧内容区 */}
- <View className="category-content">
- {/* 二级分类标签栏 */}
- {/* {subCategories.length > 0 && (
- <View className="sub-category-tabbar">
- <CategoryTabbar
- tabList={subCategories.map((cat: Category) => ({
- id: cat.id.toString(),
- name: cat.name,
- } as TabItem))}
- currentActive={activeSubCategoryId}
- onChange={handleSubCategoryChange}
- showMore={true}
- />
- </View>
- )} */}
- {/* 二级分类网格布局 */}
- <View className="sub-category-grid">
- {subCategories.map((category: Category) => (
- <View
- key={category.id}
- className="sub-category-item"
- onClick={() => handleSubCategoryClick(category)}
- >
- <View className="sub-category-image-container">
- <View className="sub-category-image">
- {category.imageFile?.fullUrl ? (
- <Image
- src={category.imageFile.fullUrl}
- alt={category.name}
- mode="aspectFill"
- rounded="lg"
- className="w-full h-full"
- showLoading={true}
- showError={true}
- />
- ) : (
- <View className="image-placeholder">
- <View className="image-fallback">
- {category.name.charAt(0)}
- </View>
- </View>
- )}
- </View>
- </View>
- <View className="sub-category-name">
- {category.name}
- </View>
- </View>
- ))}
- </View>
- {/* 广告图 */}
- {advertisementImage && (
- <View className="advertisement-container">
- <View className="advertisement-image">
- <Image
- src={advertisementImage}
- alt="分类页广告"
- mode="heightFix"
- rounded="lg"
- className="w-full h-full"
- showLoading={true}
- showError={true}
- />
- </View>
- </View>
- )}
- </View>
- </View>
- </View>
- </TabBarLayout>
- );
- };
- export default CategoryPage;
|