forked from gxwebsoft/mp-10550
refactor(category):重构文章分类页面结构和加载逻辑
- 调整文章列表组件路径并优化渲染逻辑 - 添加骨架屏加载效果提升用户体验 - 完善错误处理机制增强页面稳定性- 更新页面配置文件路径引用 - 移除冗余的页面配置和组件引用 -优化首页Banner组件加载状态处理 - 增强热销商品Tab切换功能和空状态展示- 调整用户经销商组件调试日志- 修改全局应用配置中的页面路径引用 - 调整主题处理逻辑执行时机
This commit is contained in:
@@ -4,7 +4,7 @@ export default {
|
||||
'pages/cart/cart',
|
||||
'pages/find/find',
|
||||
'pages/user/user',
|
||||
'pages/cms/category/index'
|
||||
'pages/category/index'
|
||||
],
|
||||
"subpackages": [
|
||||
{
|
||||
@@ -116,7 +116,7 @@ export default {
|
||||
text: "首页",
|
||||
},
|
||||
{
|
||||
pagePath: "pages/cms/category/index",
|
||||
pagePath: "pages/category/index",
|
||||
iconPath: "assets/tabbar/category.png",
|
||||
selectedIconPath: "assets/tabbar/category-active.png",
|
||||
text: "基地生活",
|
||||
|
||||
@@ -53,7 +53,6 @@ function App(props: { children: any; }) {
|
||||
// 处理小程序启动参数中的邀请信息
|
||||
const options = Taro.getLaunchOptionsSync()
|
||||
handleLaunchOptions(options)
|
||||
handleTheme()
|
||||
})
|
||||
|
||||
// 处理启动参数
|
||||
@@ -103,6 +102,7 @@ function App(props: { children: any; }) {
|
||||
|
||||
// 对应 onHide
|
||||
useDidHide(() => {
|
||||
handleTheme()
|
||||
})
|
||||
|
||||
return props.children
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
import {Image, Cell} from '@nutui/nutui-react-taro'
|
||||
import {View, Text} from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
|
||||
const ArticleList = (props: any) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'px-3'}>
|
||||
{props.data.map((item, index) => {
|
||||
<View className={'px-3'}>
|
||||
{props.data.map((item: any, index: number) => {
|
||||
return (
|
||||
<Cell
|
||||
title={item.title}
|
||||
title={
|
||||
<View>
|
||||
<View className="text-base font-medium mb-1">{item.title}</View>
|
||||
{item.comments && (
|
||||
<Text className="text-sm text-gray-500 leading-relaxed">
|
||||
{item.comments}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
}
|
||||
extra={
|
||||
<Image src={item.image} mode={'aspectFit'} lazyLoad={false} width={100} height="100"/>
|
||||
}
|
||||
@@ -18,7 +28,7 @@ const ArticleList = (props: any) => {
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
}
|
||||
96
src/pages/category/index.tsx
Normal file
96
src/pages/category/index.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import Taro from '@tarojs/taro'
|
||||
import {useShareAppMessage} from "@tarojs/taro"
|
||||
import {useEffect, useState} from "react"
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {View} from '@tarojs/components'
|
||||
import {getCmsNavigation, listCmsNavigation} from "@/api/cms/cmsNavigation";
|
||||
import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
|
||||
import {pageCmsArticle} from "@/api/cms/cmsArticle";
|
||||
import {CmsArticle} from "@/api/cms/cmsArticle/model";
|
||||
import ArticleList from './components/ArticleList'
|
||||
import ArticleTabs from "./components/ArticleTabs";
|
||||
import './index.scss'
|
||||
|
||||
function Category() {
|
||||
const {params} = useRouter();
|
||||
const [categoryId, setCategoryId] = useState<number>(0)
|
||||
const [category, setCategory] = useState<CmsNavigation[]>([])
|
||||
const [loading, setLoading] = useState<boolean>(true)
|
||||
const [nav, setNav] = useState<CmsNavigation>()
|
||||
const [list, setList] = useState<CmsArticle[]>([])
|
||||
|
||||
const reload = async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
// 1.加载远程数据
|
||||
const id = Number(params.id || 4328)
|
||||
const nav = await getCmsNavigation(id)
|
||||
const categoryList = await listCmsNavigation({parentId: id})
|
||||
const shopGoods = await pageCmsArticle({categoryId: id})
|
||||
|
||||
// 2.赋值
|
||||
setCategoryId(id)
|
||||
setNav(nav)
|
||||
setList(shopGoods?.list || [])
|
||||
setCategory(categoryList)
|
||||
Taro.setNavigationBarTitle({
|
||||
title: `${nav?.categoryName}`
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('文章分类加载失败:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, []);
|
||||
|
||||
useShareAppMessage(() => {
|
||||
return {
|
||||
title: `${nav?.categoryName}_时里院子市集`,
|
||||
path: `/shop/category/index?id=${categoryId}`,
|
||||
success: function () {
|
||||
console.log('分享成功');
|
||||
},
|
||||
fail: function () {
|
||||
console.log('分享失败');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// 骨架屏组件
|
||||
const ArticleSkeleton = () => (
|
||||
<View className="px-3">
|
||||
{[1, 2, 3, 4, 5].map(i => (
|
||||
<View key={i} className="flex items-center py-4 border-b border-gray-100">
|
||||
{/* 左侧文字骨架屏 */}
|
||||
<View className="flex-1 pr-4">
|
||||
{/* 标题骨架屏 */}
|
||||
<View className="bg-gray-200 h-5 rounded animate-pulse mb-2" style={{width: '75%'}}/>
|
||||
{/* 副标题骨架屏 */}
|
||||
<View className="bg-gray-200 h-4 rounded animate-pulse" style={{width: '50%'}}/>
|
||||
</View>
|
||||
{/* 右侧图片骨架屏 */}
|
||||
<View
|
||||
className="bg-gray-200 rounded animate-pulse flex-shrink-0"
|
||||
style={{width: '100px', height: '100px'}}
|
||||
/>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
)
|
||||
|
||||
if (loading) {
|
||||
return <ArticleSkeleton />
|
||||
}
|
||||
|
||||
if(category.length > 0){
|
||||
return <ArticleTabs data={category}/>
|
||||
}
|
||||
|
||||
return <ArticleList data={list}/>
|
||||
}
|
||||
|
||||
export default Category
|
||||
@@ -1,71 +0,0 @@
|
||||
import Taro from '@tarojs/taro'
|
||||
import {useShareAppMessage} from "@tarojs/taro"
|
||||
import {Loading} from '@nutui/nutui-react-taro'
|
||||
import {useEffect, useState} from "react"
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {getCmsNavigation, listCmsNavigation} from "@/api/cms/cmsNavigation";
|
||||
import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
|
||||
import {pageCmsArticle} from "@/api/cms/cmsArticle";
|
||||
import {CmsArticle} from "@/api/cms/cmsArticle/model";
|
||||
import ArticleList from './components/ArticleList'
|
||||
import ArticleTabs from "./components/ArticleTabs";
|
||||
import './index.scss'
|
||||
|
||||
function Category() {
|
||||
const {params} = useRouter();
|
||||
const [categoryId, setCategoryId] = useState<number>(0)
|
||||
const [category, setCategory] = useState<CmsNavigation[]>([])
|
||||
const [loading, setLoading] = useState<boolean>(true)
|
||||
const [nav, setNav] = useState<CmsNavigation>()
|
||||
const [list, setList] = useState<CmsArticle[]>([])
|
||||
|
||||
const reload = async () => {
|
||||
// 1.加载远程数据
|
||||
const id = Number(params.id || 4328)
|
||||
const nav = await getCmsNavigation(id)
|
||||
const categoryList = await listCmsNavigation({parentId: id})
|
||||
const shopGoods = await pageCmsArticle({categoryId: id})
|
||||
|
||||
// 2.赋值
|
||||
setCategoryId(id)
|
||||
setNav(nav)
|
||||
setList(shopGoods?.list || [])
|
||||
setCategory(categoryList)
|
||||
Taro.setNavigationBarTitle({
|
||||
title: `${nav?.categoryName}`
|
||||
})
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
reload().then(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
}, []);
|
||||
|
||||
useShareAppMessage(() => {
|
||||
return {
|
||||
title: `${nav?.categoryName}_时里院子市集`,
|
||||
path: `/shop/category/index?id=${categoryId}`,
|
||||
success: function () {
|
||||
console.log('分享成功');
|
||||
},
|
||||
fail: function () {
|
||||
console.log('分享失败');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Loading className={'px-2'}>加载中</Loading>
|
||||
)
|
||||
}
|
||||
|
||||
if(category.length > 0){
|
||||
return <ArticleTabs data={category}/>
|
||||
}
|
||||
|
||||
return <ArticleList data={list}/>
|
||||
}
|
||||
|
||||
export default Category
|
||||
@@ -1,3 +0,0 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '文章详情'
|
||||
})
|
||||
@@ -1,53 +0,0 @@
|
||||
import Taro from '@tarojs/taro'
|
||||
import {useEffect, useState} from 'react'
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {Loading} from '@nutui/nutui-react-taro'
|
||||
import {View, RichText} from '@tarojs/components'
|
||||
import {wxParse} from "@/utils/common";
|
||||
import {getCmsArticle} from "@/api/cms/cmsArticle";
|
||||
import {CmsArticle} from "@/api/cms/cmsArticle/model"
|
||||
import Line from "@/components/Gap";
|
||||
import './index.scss'
|
||||
|
||||
function Detail() {
|
||||
const {params} = useRouter();
|
||||
const [loading, setLoading] = useState<boolean>(true)
|
||||
// 文章详情
|
||||
const [item, setItem] = useState<CmsArticle>()
|
||||
const reload = async () => {
|
||||
const item = await getCmsArticle(Number(params.id))
|
||||
|
||||
if (item) {
|
||||
item.content = wxParse(item.content)
|
||||
setItem(item)
|
||||
Taro.setNavigationBarTitle({
|
||||
title: `${item?.categoryName}`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload().then(() => {
|
||||
setLoading(false)
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Loading className={'px-2'}>加载中</Loading>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={'bg-white'}>
|
||||
<div className={'p-4 font-bold text-lg'}>{item?.title}</div>
|
||||
<div className={'text-gray-400 text-sm px-4 '}>{item?.createTime}</div>
|
||||
<View className={'content p-4'}>
|
||||
<RichText nodes={item?.content}/>
|
||||
</View>
|
||||
<Line height={44}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Detail
|
||||
@@ -13,9 +13,12 @@ const MyPage = () => {
|
||||
const [carouselData, setCarouselData] = useState<CmsAd>()
|
||||
const [hotToday, setHotToday] = useState<CmsAd>()
|
||||
const [item, setItem] = useState<CmsArticle>()
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
// 加载数据
|
||||
const loadData = async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
// 轮播图
|
||||
const flash = await getCmsAdByCode('flash')
|
||||
// 今日热卖
|
||||
@@ -32,6 +35,11 @@ const MyPage = () => {
|
||||
if(news && news.list.length > 0){
|
||||
setItem(news.list[0])
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Banner数据加载失败:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -41,17 +49,73 @@ const MyPage = () => {
|
||||
// 轮播图高度,默认200px
|
||||
const carouselHeight = carouselData?.height || 200;
|
||||
|
||||
// 骨架屏组件
|
||||
const BannerSkeleton = () => (
|
||||
<View className="flex p-2 justify-between" style={{height: `${carouselHeight}px`}}>
|
||||
{/* 左侧轮播图骨架屏 */}
|
||||
<View style={{width: '50%', height: '100%'}}>
|
||||
<View
|
||||
className="bg-gray-200 rounded animate-pulse"
|
||||
style={{height: `${carouselHeight}px`}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* 右侧骨架屏 */}
|
||||
<View className="flex flex-col" style={{width: '50%', height: '100%'}}>
|
||||
{/* 上层骨架屏 */}
|
||||
<View className="ml-2 bg-white rounded-lg">
|
||||
<View className="px-3 my-2">
|
||||
<View className="bg-gray-200 h-4 w-16 rounded animate-pulse"/>
|
||||
</View>
|
||||
<View className="px-3 flex" style={{height: '110px'}}>
|
||||
{[1, 2].map(i => (
|
||||
<View key={i} className="item flex flex-col mr-4">
|
||||
<View className="bg-gray-200 rounded animate-pulse" style={{width: 70, height: 70}}/>
|
||||
<View className="bg-gray-200 h-3 w-16 rounded mt-2 animate-pulse"/>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 下层骨架屏 */}
|
||||
<View className="ml-2 bg-white rounded-lg mt-3">
|
||||
<View className="px-3 my-2">
|
||||
<View className="bg-gray-200 h-4 w-20 rounded animate-pulse"/>
|
||||
</View>
|
||||
<View className="rounded-lg px-3 pb-3">
|
||||
<View className="bg-gray-200 rounded animate-pulse" style={{width: '100%', height: 106}}/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
||||
// 如果正在加载,显示骨架屏
|
||||
if (loading) {
|
||||
return <BannerSkeleton />
|
||||
}
|
||||
|
||||
return (
|
||||
<View className="flex p-2 justify-between" style={{height: `${carouselHeight}px`}}>
|
||||
{/* 左侧轮播图区域 */}
|
||||
<View style={{width: '50%', height: '100%'}}>
|
||||
<View
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
touchAction: 'pan-y',
|
||||
pointerEvents: 'none'
|
||||
}}
|
||||
>
|
||||
<Swiper
|
||||
defaultValue={0}
|
||||
height={carouselHeight}
|
||||
indicator
|
||||
autoPlay
|
||||
duration={3000}
|
||||
style={{height: `${carouselHeight}px`}}
|
||||
>
|
||||
{carouselData?.imageList?.map((img, index) => (
|
||||
{carouselData && carouselData?.imageList?.map((img, index) => (
|
||||
<Swiper.Item key={index}>
|
||||
<Image
|
||||
width="100%"
|
||||
@@ -60,12 +124,17 @@ const MyPage = () => {
|
||||
mode={'scaleToFill'}
|
||||
onClick={() => navTo(`${img.path}`)}
|
||||
lazyLoad={false}
|
||||
style={{height: `${carouselHeight}px`, borderRadius: '4px'}}
|
||||
style={{
|
||||
height: `${carouselHeight}px`,
|
||||
borderRadius: '4px',
|
||||
pointerEvents: 'auto'
|
||||
}}
|
||||
/>
|
||||
</Swiper.Item>
|
||||
))}
|
||||
</Swiper>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 右侧上下图片区域 - 从API获取数据 */}
|
||||
<View className="flex flex-col" style={{width: '50%', height: '100%'}}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {Image} from '@nutui/nutui-react-taro'
|
||||
import {Image, Tabs, Empty} from '@nutui/nutui-react-taro'
|
||||
import {Share} from '@nutui/icons-react-taro'
|
||||
import {View, Text} from '@tarojs/components';
|
||||
import Taro from "@tarojs/taro";
|
||||
@@ -7,6 +7,7 @@ import {ShopGoods} from "@/api/shop/shopGoods/model";
|
||||
import {pageShopGoods} from "@/api/shop/shopGoods";
|
||||
|
||||
const BestSellers = () => {
|
||||
const [tab1value, setTab1value] = useState<string | number>('0')
|
||||
const [list, setList] = useState<ShopGoods[]>([])
|
||||
const [goods, setGoods] = useState<ShopGoods>()
|
||||
|
||||
@@ -57,8 +58,29 @@ const BestSellers = () => {
|
||||
return (
|
||||
<>
|
||||
<View className={'py-3'}>
|
||||
{/* Tabs切换组件 */}
|
||||
<Tabs
|
||||
value={tab1value}
|
||||
className={'w-full mb-4'}
|
||||
onChange={(value) => {
|
||||
setTab1value(value)
|
||||
}}
|
||||
style={{
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
activeType="smile"
|
||||
>
|
||||
<Tabs.TabPane title="今日主推">
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane title="即将到期">
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane title="活动预告">
|
||||
</Tabs.TabPane>
|
||||
</Tabs>
|
||||
|
||||
<View className={'flex flex-col justify-between items-center rounded-lg px-2'}>
|
||||
{list?.map((item, index) => {
|
||||
{/* 今日主推 */}
|
||||
{tab1value == '0' && list?.map((item, index) => {
|
||||
return (
|
||||
<View key={index} className={'flex flex-col rounded-lg bg-white shadow-sm w-full mb-5'}>
|
||||
<Image src={item.image} mode={'aspectFit'} lazyLoad={false}
|
||||
@@ -95,6 +117,28 @@ const BestSellers = () => {
|
||||
</View>
|
||||
)
|
||||
})}
|
||||
|
||||
{/* 即将到期 */}
|
||||
{tab1value == '1' && (
|
||||
<Empty
|
||||
size={'small'}
|
||||
description="暂无即将到期的商品"
|
||||
style={{
|
||||
background: 'transparent',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 活动预告 */}
|
||||
{tab1value == '2' && (
|
||||
<Empty
|
||||
size={'small'}
|
||||
description="暂无活动预告"
|
||||
style={{
|
||||
background: 'transparent',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
|
||||
@@ -21,7 +21,7 @@ const IsDealer = () => {
|
||||
setConfig(data)
|
||||
})
|
||||
}, [])
|
||||
|
||||
console.log(dealerUser,'dealerUserdealerUserdealerUserdealerUserdealerUser')
|
||||
/**
|
||||
* 管理中心
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user