|
@@ -5,8 +5,11 @@ import { goodsCategoryClient, advertisementClient } from '@/api';
|
|
|
import CategorySidebar from '@/components/category/CategorySidebar';
|
|
import CategorySidebar from '@/components/category/CategorySidebar';
|
|
|
import CategorySidebarItem from '@/components/category/CategorySidebarItem';
|
|
import CategorySidebarItem from '@/components/category/CategorySidebarItem';
|
|
|
import CategoryTabbar from '@/components/category/CategoryTabbar';
|
|
import CategoryTabbar from '@/components/category/CategoryTabbar';
|
|
|
|
|
+import { Image } from '@/components/ui/image';
|
|
|
|
|
+import TDesignToast from '@/components/tdesign/toast';
|
|
|
import { navigateTo } from '@tarojs/taro';
|
|
import { navigateTo } from '@tarojs/taro';
|
|
|
import { InferResponseType } from 'hono';
|
|
import { InferResponseType } from 'hono';
|
|
|
|
|
+import { TabBarLayout } from '@/layouts/tab-bar-layout';
|
|
|
import './index.css';
|
|
import './index.css';
|
|
|
|
|
|
|
|
type GoodsCategoryResponse = InferResponseType<typeof goodsCategoryClient.$get, 200>
|
|
type GoodsCategoryResponse = InferResponseType<typeof goodsCategoryClient.$get, 200>
|
|
@@ -18,6 +21,8 @@ type Advertisement = AdvertisementResponse['data'][0]
|
|
|
const CategoryPage: React.FC = () => {
|
|
const CategoryPage: React.FC = () => {
|
|
|
const [activeCategoryIndex, setActiveCategoryIndex] = useState<number>(0);
|
|
const [activeCategoryIndex, setActiveCategoryIndex] = useState<number>(0);
|
|
|
const [activeSubCategoryId, setActiveSubCategoryId] = useState<string>('');
|
|
const [activeSubCategoryId, setActiveSubCategoryId] = useState<string>('');
|
|
|
|
|
+ const [toastVisible, setToastVisible] = useState<boolean>(false);
|
|
|
|
|
+ const [toastMessage, setToastMessage] = useState<string>('');
|
|
|
|
|
|
|
|
// 获取分类数据
|
|
// 获取分类数据
|
|
|
const { data: categoryData, isLoading, error } = useQuery({
|
|
const { data: categoryData, isLoading, error } = useQuery({
|
|
@@ -38,6 +43,14 @@ const CategoryPage: React.FC = () => {
|
|
|
staleTime: 5 * 60 * 1000, // 5分钟缓存
|
|
staleTime: 5 * 60 * 1000, // 5分钟缓存
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+ // 显示错误Toast
|
|
|
|
|
+ React.useEffect(() => {
|
|
|
|
|
+ if (error) {
|
|
|
|
|
+ setToastMessage('获取分类数据失败,请重试');
|
|
|
|
|
+ setToastVisible(true);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [error]);
|
|
|
|
|
+
|
|
|
// 获取广告数据
|
|
// 获取广告数据
|
|
|
const { data: advertisementData } = useQuery({
|
|
const { data: advertisementData } = useQuery({
|
|
|
queryKey: ['category-advertisements'],
|
|
queryKey: ['category-advertisements'],
|
|
@@ -95,103 +108,125 @@ const CategoryPage: React.FC = () => {
|
|
|
|
|
|
|
|
if (isLoading) {
|
|
if (isLoading) {
|
|
|
return (
|
|
return (
|
|
|
- <View className="category-page">
|
|
|
|
|
- <View className="loading">加载中...</View>
|
|
|
|
|
- </View>
|
|
|
|
|
|
|
+ <TabBarLayout activeKey="category">
|
|
|
|
|
+ <View className="category-page">
|
|
|
|
|
+ <View className="loading">加载中...</View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </TabBarLayout>
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (error) {
|
|
if (error) {
|
|
|
return (
|
|
return (
|
|
|
- <View className="category-page">
|
|
|
|
|
- <View className="error">加载失败,请重试</View>
|
|
|
|
|
- </View>
|
|
|
|
|
|
|
+ <TabBarLayout activeKey="category">
|
|
|
|
|
+ <View className="category-page">
|
|
|
|
|
+ <View className="error">加载失败,请重试</View>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ </TabBarLayout>
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
- <View className="category-page">
|
|
|
|
|
- <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>
|
|
|
|
|
|
|
+ <TabBarLayout activeKey="category">
|
|
|
|
|
+ <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,
|
|
|
|
|
- name: cat.name,
|
|
|
|
|
- }))}
|
|
|
|
|
- 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.image ? (
|
|
|
|
|
- <View className="image-placeholder">
|
|
|
|
|
- {/* 这里需要集成 Shadcn Image 组件 */}
|
|
|
|
|
- <View className="image-fallback">
|
|
|
|
|
- {category.name.charAt(0)}
|
|
|
|
|
- </View>
|
|
|
|
|
- </View>
|
|
|
|
|
- ) : (
|
|
|
|
|
- <View className="image-placeholder">
|
|
|
|
|
- <View className="image-fallback">
|
|
|
|
|
- {category.name.charAt(0)}
|
|
|
|
|
|
|
+ {/* 右侧内容区 */}
|
|
|
|
|
+ <View className="category-content">
|
|
|
|
|
+ {/* 二级分类标签栏 */}
|
|
|
|
|
+ {subCategories.length > 0 && (
|
|
|
|
|
+ <View className="sub-category-tabbar">
|
|
|
|
|
+ <CategoryTabbar
|
|
|
|
|
+ tabList={subCategories.map((cat: Category) => ({
|
|
|
|
|
+ id: cat.id,
|
|
|
|
|
+ name: cat.name,
|
|
|
|
|
+ }))}
|
|
|
|
|
+ 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>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ <View className="sub-category-name">
|
|
|
|
|
+ {category.name}
|
|
|
</View>
|
|
</View>
|
|
|
</View>
|
|
</View>
|
|
|
- <View className="sub-category-name">
|
|
|
|
|
- {category.name}
|
|
|
|
|
- </View>
|
|
|
|
|
- </View>
|
|
|
|
|
- ))}
|
|
|
|
|
- </View>
|
|
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </View>
|
|
|
|
|
|
|
|
- {/* 广告图 */}
|
|
|
|
|
- {advertisementImage && (
|
|
|
|
|
- <View className="advertisement-container">
|
|
|
|
|
- <View className="advertisement-image">
|
|
|
|
|
- {/* 这里需要集成 Shadcn Image 组件 */}
|
|
|
|
|
- <View className="ad-image-placeholder">
|
|
|
|
|
- 广告图
|
|
|
|
|
|
|
+ {/* 广告图 */}
|
|
|
|
|
+ {advertisementImage && (
|
|
|
|
|
+ <View className="advertisement-container">
|
|
|
|
|
+ <View className="advertisement-image">
|
|
|
|
|
+ <Image
|
|
|
|
|
+ src={advertisementImage}
|
|
|
|
|
+ alt="分类页广告"
|
|
|
|
|
+ mode="aspectFill"
|
|
|
|
|
+ rounded="lg"
|
|
|
|
|
+ className="w-full h-full"
|
|
|
|
|
+ showLoading={true}
|
|
|
|
|
+ showError={true}
|
|
|
|
|
+ />
|
|
|
</View>
|
|
</View>
|
|
|
</View>
|
|
</View>
|
|
|
- </View>
|
|
|
|
|
- )}
|
|
|
|
|
|
|
+ )}
|
|
|
|
|
+ </View>
|
|
|
</View>
|
|
</View>
|
|
|
</View>
|
|
</View>
|
|
|
- </View>
|
|
|
|
|
|
|
+ </TabBarLayout>
|
|
|
);
|
|
);
|
|
|
};
|
|
};
|
|
|
|
|
|