Compare commits

...

10 Commits

  1. 22
      src/app.config.ts
  2. BIN
      src/assets/tabbar/logo.png
  3. BIN
      src/assets/tabbar/tv-active.png
  4. BIN
      src/assets/tabbar/tv.png
  5. 25
      src/pages/cms/category/components/ArticleList.tsx
  6. 59
      src/pages/cms/category/components/ArticleTabs.tsx
  7. 31
      src/pages/cms/category/components/Banner.tsx
  8. 4
      src/pages/cms/category/index.config.ts
  9. 0
      src/pages/cms/category/index.scss
  10. 71
      src/pages/cms/category/index.tsx
  11. 3
      src/pages/cms/detail/index.config.ts
  12. 0
      src/pages/cms/detail/index.scss
  13. 53
      src/pages/cms/detail/index.tsx
  14. 113
      src/pages/index/Banner.tsx
  15. 125
      src/pages/index/BestSellers.tsx
  16. 38
      src/pages/user/components/UserCard.tsx
  17. 39
      src/pages/user/components/UserGrid.tsx
  18. 7
      src/pages/user/user.scss
  19. 4
      src/user/poster/poster.config.ts
  20. 115
      src/user/poster/poster.tsx

22
src/app.config.ts

@ -4,7 +4,8 @@ export default {
'pages/cart/cart',
'pages/find/find',
'pages/user/user',
'pages/qr-login/index'
'pages/qr-login/index',
'pages/cms/category/index'
],
"subpackages": [
{
@ -55,7 +56,8 @@ export default {
"gift/redeem",
"gift/detail",
"store/verification",
"theme/index"
"theme/index",
"poster/poster",
]
},
{
@ -103,16 +105,16 @@ export default {
list: [
{
pagePath: "pages/index/index",
iconPath: "assets/tabbar/home.png",
selectedIconPath: "assets/tabbar/home-active.png",
iconPath: "assets/tabbar/logo.png",
selectedIconPath: "assets/tabbar/logo.png",
text: "首页",
},
// {
// pagePath: "pages/find/find",
// iconPath: "assets/tabbar/find.png",
// selectedIconPath: "assets/tabbar/find-active.png",
// text: "发现",
// },
{
pagePath: "pages/cms/category/index",
iconPath: "assets/tabbar/tv.png",
selectedIconPath: "assets/tabbar/tv-active.png",
text: "基地生活",
},
{
pagePath: "pages/cart/cart",
iconPath: "assets/tabbar/cart.png",

BIN
src/assets/tabbar/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
src/assets/tabbar/tv-active.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
src/assets/tabbar/tv.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

25
src/pages/cms/category/components/ArticleList.tsx

@ -0,0 +1,25 @@
import {Image, Cell} from '@nutui/nutui-react-taro'
import Taro from '@tarojs/taro'
const ArticleList = (props: any) => {
return (
<>
<div className={'px-3'}>
{props.data.map((item, index) => {
return (
<Cell
title={item.title}
extra={
<Image src={item.image} mode={'aspectFit'} lazyLoad={false} width={100} height="100"/>
}
key={index}
onClick={() => Taro.navigateTo({url: '/cms/detail/index?id=' + item.articleId})}
/>
)
})}
</div>
</>
)
}
export default ArticleList

59
src/pages/cms/category/components/ArticleTabs.tsx

@ -0,0 +1,59 @@
import {useEffect, useState} from "react";
import {Tabs, Loading} from '@nutui/nutui-react-taro'
import {pageCmsArticle} from "@/api/cms/cmsArticle";
import {CmsArticle} from "@/api/cms/cmsArticle/model";
import ArticleList from "./ArticleList";
const ArticleTabs = (props: any) => {
const [loading, setLoading] = useState<boolean>(true)
const [tab1value, setTab1value] = useState<string | number>('0')
const [list, setList] = useState<CmsArticle[]>([])
const reload = async (value) => {
const {data} = props
pageCmsArticle({
categoryId: data[value].navigationId,
page: 1,
status: 0,
limit: 10
}).then((res) => {
res && setList(res?.list || [])
})
.catch(err => {
console.log(err)
})
.finally(() => {
setTab1value(value)
setLoading(false)
})
}
useEffect(() => {
reload(0).then()
}, []);
if (loading) {
return (
<Loading className={'px-2'}></Loading>
)
}
return (
<>
<Tabs
value={tab1value}
onChange={(value) => {
reload(value).then()
}}
>
{props.data?.map((item, index) => {
return (
<Tabs.TabPane title={item.categoryName} key={index}/>
)
})}
</Tabs>
<ArticleList data={list}/>
</>
)
}
export default ArticleTabs

31
src/pages/cms/category/components/Banner.tsx

@ -0,0 +1,31 @@
import { useEffect, useState } from 'react'
import { Swiper } from '@nutui/nutui-react-taro'
import {CmsAd} from "@/api/cms/cmsAd/model";
import {Image} from '@nutui/nutui-react-taro'
import {getCmsAd} from "@/api/cms/cmsAd";
const MyPage = () => {
const [item, setItem] = useState<CmsAd>()
const reload = () => {
getCmsAd(439).then(data => {
setItem(data)
})
}
useEffect(() => {
reload()
}, [])
return (
<>
<Swiper defaultValue={0} height={item?.height} indicator style={{ height: item?.height + 'px', display: 'none' }}>
{item?.imageList?.map((item) => (
<Swiper.Item key={item}>
<Image width="100%" height="100%" src={item.url} mode={'scaleToFill'} lazyLoad={false} style={{ height: item.height + 'px' }} />
</Swiper.Item>
))}
</Swiper>
</>
)
}
export default MyPage

4
src/pages/cms/category/index.config.ts

@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '文章列表',
navigationBarTextStyle: 'black'
})

0
src/pages/cms/category/index.scss

71
src/pages/cms/category/index.tsx

@ -0,0 +1,71 @@
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

3
src/pages/cms/detail/index.config.ts

@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '文章详情'
})

0
src/pages/cms/detail/index.scss

53
src/pages/cms/detail/index.tsx

@ -0,0 +1,53 @@
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

113
src/pages/index/Banner.tsx

@ -5,28 +5,121 @@ import {Image} from '@nutui/nutui-react-taro'
import {getCmsAd} from "@/api/cms/cmsAd";
import navTo from "@/utils/common";
const MyPage = () => {
const [item, setItem] = useState<CmsAd>()
const reload = () => {
const [carouselData, setCarouselData] = useState<CmsAd>()
const [hotToday, setHotToday] = useState<CmsAd>()
const [groupBuy, setGroupBuy] = useState<CmsAd>()
// 加载数据
const loadData = () => {
// 轮播图
getCmsAd(439).then(data => {
setItem(data)
setCarouselData(data)
})
// 今日热卖素材(上层图片)
getCmsAd(444).then(data => {
setHotToday(data)
})
// 社区拼团素材(下层图片)
getCmsAd(445).then(data => {
setGroupBuy(data)
})
}
useEffect(() => {
reload()
loadData()
}, [])
// 轮播图高度,默认200px
const carouselHeight = carouselData?.height || 200;
return (
<>
<Swiper defaultValue={0} height={item?.height} indicator style={{ height: item?.height + 'px' }}>
{item?.imageList?.map((item) => (
<Swiper.Item key={item}>
<Image width="100%" height="100%" src={item.url} mode={'scaleToFill'} onClick={() => navTo(`${item.path}`)} lazyLoad={false} style={{ height: item.height + 'px' }} />
<div className="flex flex-row w-full gap-2 p-2 box-sizing-border" style={{height: `${carouselHeight}px`}}>
{/* 左侧轮播图区域 */}
<div className="flex-1" style={{height: '100%'}}>
<Swiper
defaultValue={0}
height={carouselHeight}
indicator
style={{height: `${carouselHeight}px`}}
>
{carouselData?.imageList?.map((img, index) => (
<Swiper.Item key={index}>
<Image
width="100%"
height="100%"
src={img.url}
mode={'scaleToFill'}
onClick={() => navTo(`${img.path}`)}
lazyLoad={false}
style={{height: `${carouselHeight}px`, borderRadius: '4px'}}
alt={`轮播图${index + 1}`}
/>
</Swiper.Item>
))}
</Swiper>
</>
</div>
{/* 右侧上下图片区域 - 从API获取数据 */}
<div className="flex-1 flex flex-col gap-2" style={{height: '100%'}}>
{/* 上层图片 - 使用今日热卖素材 */}
<div style={{height: '50%'}}>
{hotToday?.imageList?.length ? (
<Image
width="100%"
height="100%"
src={hotToday.imageList[0].url}
mode={'scaleToFill'}
onClick={() => navTo("/cms/category/index?id=4424" || '')}
lazyLoad={false}
style={{borderRadius: '4px'}}
alt="今日热卖"
/>
) : (
<div style={{
height: '100%',
backgroundColor: '#f5f5f5',
borderRadius: '4px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}>
<span style={{color: '#999', fontSize: '12px'}}>...</span>
</div>
)}
</div>
{/* 下层图片 - 使用社区拼团素材 */}
<div style={{height: '50%'}}>
{groupBuy?.imageList?.length ? (
<Image
width="100%"
height="100%"
src={groupBuy.imageList[0].url}
mode={'scaleToFill'}
onClick={() => navTo(groupBuy.imageList[0].path || '')}
lazyLoad={false}
style={{borderRadius: '4px'}}
alt="社区拼团"
/>
) : (
<div style={{
height: '100%',
backgroundColor: '#f5f5f5',
borderRadius: '4px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}>
<span style={{color: '#999', fontSize: '12px'}}>...</span>
</div>
)}
</div>
</div>
</div>
)
}
export default MyPage

125
src/pages/index/BestSellers.tsx

@ -1,8 +1,8 @@
import { useEffect, useState } from "react";
import {Image} from '@nutui/nutui-react-taro'
import { Image, Swiper, SwiperItem } from '@nutui/nutui-react-taro'
import { Share } from '@nutui/icons-react-taro'
import { View, Text } from '@tarojs/components';
import Taro, {useShareAppMessage} from "@tarojs/taro";
import Taro from "@tarojs/taro";
import { ShopGoods } from "@/api/shop/shopGoods/model";
import { pageShopGoods } from "@/api/shop/shopGoods";
import './BestSellers.scss'
@ -10,12 +10,46 @@ import './BestSellers.scss'
const BestSellers = () => {
const [list, setList] = useState<ShopGoods[]>([])
const [goods, setGoods] = useState<ShopGoods>()
const [goods, setGoods] = useState<ShopGoods | null>(null)
// 轮播图固定高度,可根据需求调整
const SWIPER_HEIGHT = 180;
const reload = () => {
pageShopGoods({}).then(res => {
setList(res?.list || []);
})
const processGoodsItem = (item: ShopGoods) => {
const pics: string[] = [];
// 添加主图
if (item.image) {
pics.push(item.image);
}
// 处理附加图片
if (item.files) {
try {
// 解析文件字符串为对象
const files = typeof item.files === "string"
? JSON.parse(item.files)
: item.files;
// 收集所有图片URL
Object.values(files).forEach(file => {
if (file?.url) {
pics.push(file.url);
}
});
} catch (error) {
console.error('解析文件失败:', error);
}
}
// 返回新对象,避免直接修改原对象
return { ...item, pics };
};
// 处理商品列表
const goods = (res?.list || []).map(processGoodsItem);
setList(goods);
}).catch(err => {
console.error('获取商品列表失败:', err);
});
}
// 处理分享点击
@ -27,11 +61,9 @@ const BestSellers = () => {
itemList: ['分享给好友'],
success: (res) => {
if (res.tapIndex === 0) {
// 分享给好友 - 触发转发
Taro.showShareMenu({
withShareTicket: true,
success: () => {
// 提示用户点击右上角分享
Taro.showToast({
title: '请点击右上角分享给好友',
icon: 'none',
@ -48,22 +80,65 @@ const BestSellers = () => {
}
useEffect(() => {
reload()
}, [])
reload();
}, []);
// 注意:不在这里配置分享,避免与首页分享冲突
// 商品分享应该在商品详情页处理,首页分享应该分享首页本身
// 配置分享内容
Taro.useShareAppMessage(() => {
if (goods) {
return {
title: goods.name,
path: `/shop/goodsDetail/index?id=${goods.goodsId}`,
imageUrl: goods.image || ''
};
}
return {
title: '热销商品',
path: '/pages/index/index'
};
});
return (
<>
<View className={'py-3'}>
<View className={'flex flex-col justify-between items-center rounded-lg px-2'}>
{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}
radius="10px 10px 0 0" height="180"
onClick={() => Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}/>
{list?.map((item) => (
<View
key={item.goodsId || item.id} // 使用商品唯一ID作为key
className={'flex flex-col rounded-lg bg-white shadow-sm w-full mb-5'}
>
{/* 轮播图组件 */}
{item.pics && item.pics.length > 0 ? (
<Swiper
defaultValue={0}
height={SWIPER_HEIGHT}
indicator
className="swiper-container"
autoPlay
interval={3000}
>
{item.pics.map((pic, picIndex) => (
<SwiperItem key={picIndex}>
<Image
radius="12px 12px 0 0"
height={SWIPER_HEIGHT}
src={pic}
mode={'aspectFill'} // 使用aspectFill保持比例并填充容器
lazyLoad
onClick={() => Taro.navigateTo({
url: `/shop/goodsDetail/index?id=${item.goodsId}`
})}
className="swiper-image"
/>
</SwiperItem>
))}
</Swiper>
) : (
// 没有图片时显示占位图
<View className="no-image-placeholder" style={{ height: `${SWIPER_HEIGHT}px` }}>
<Text className="placeholder-text"></Text>
</View>
)}
<View className={'flex flex-col p-2 rounded-lg'}>
<View>
<View className={'car-no text-sm'}>{item.name}</View>
@ -85,19 +160,23 @@ const BestSellers = () => {
<Share size={20}/>
</View>
</View>
<Text className={'text-white pl-4 pr-5'}
onClick={() => Taro.navigateTo({url: '/shop/goodsDetail/index?id=' + item.goodsId})}>
<Text
className={'text-white pl-4 pr-5'}
onClick={() => Taro.navigateTo({
url: `/shop/goodsDetail/index?id=${item.goodsId}`
})}
>
</Text>
</View>
</View>
</View>
</View>
</View>
)
})}
))}
</View>
</View>
</>
)
}
export default BestSellers

38
src/pages/user/components/UserCard.tsx

@ -1,6 +1,6 @@
import {Button} from '@nutui/nutui-react-taro'
import {Avatar, Tag} from '@nutui/nutui-react-taro'
import {View, Text} from '@tarojs/components'
import {View, Text, Image} from '@tarojs/components'
import {Scan} from '@nutui/icons-react-taro';
import {getUserInfo, getWxOpenId} from '@/api/layout';
import Taro from '@tarojs/taro';
@ -171,14 +171,13 @@ const UserCard = forwardRef<any, any>((_, ref) => {
return (
<View className={'header-bg pt-20'}>
<View className={'p-4'}>
{/* 使用相对定位容器,让个人资料图片可以绝对定位在右上角 */}
<View className="relative">
<View
className={'user-card w-full flex flex-col justify-around rounded-xl shadow-sm'}
className={'user-card w-full flex flex-col justify-around rounded-xl'}
style={{
background: 'linear-gradient(to bottom, #ffffff, #ffffff)', // 这种情况建议使用类名来控制样式(引入外联样式)
// width: '720rpx',
// margin: '10px auto 0px auto',
background: 'linear-gradient(to bottom, #ffffff, #ffffff)',
height: '170px',
// borderRadius: '22px 22px 0 0',
}}
>
<View className={'user-card-header flex w-full justify-between items-center pt-4'}>
@ -205,11 +204,8 @@ const UserCard = forwardRef<any, any>((_, ref) => {
) : ''}
</View>
</View>
{isAdmin() && <Scan className={'text-gray-900'} size={24} onClick={() => navTo('/user/store/verification', true)} />}
<View className={'mr-4 text-sm px-3 py-1 text-black border-gray-400 border-solid border-2 rounded-3xl'}
onClick={() => navTo('/user/profile/profile', true)}>
{'个人资料'}
</View>
{isAdmin() &&
<Scan className={'text-gray-900'} size={24} onClick={() => navTo('/user/store/verification', true)}/>}
</View>
<View className={'flex justify-around mt-1'}>
<View className={'item flex justify-center flex-col items-center'}
@ -233,9 +229,27 @@ const UserCard = forwardRef<any, any>((_, ref) => {
</View>
</View>
</View>
{/* 个人资料图片,定位在右上角 */}
<View
className="absolute top-0 right-0 overflow-hidden"
style={{
borderRadius:"0 0.75rem 0 0"
}}
onClick={() => navTo('/user/profile/profile', true)}
>
<Image
src="https://oss.wsdns.cn/20250913/7c3de38b377344b89131aba40214f63f.png"
alt="个人资料"
style={{
width:"200rpx"
}}
mode="widthFix"
/>
</View>
</View>
</View>
</View>
)
})

39
src/pages/user/components/UserGrid.tsx

@ -1,8 +1,18 @@
import {Grid, ConfigProvider} from '@nutui/nutui-react-taro'
import navTo from "@/utils/common";
import Taro from '@tarojs/taro'
import {View} from '@tarojs/components'
import {ShieldCheck, Location, Tips, Ask, Dongdong, People, AfterSaleService, Logout} from '@nutui/icons-react-taro'
import {View, Button} from '@tarojs/components'
import {
ShieldCheck,
Location,
Tips,
Ask,
Dongdong,
People,
AfterSaleService,
Logout,
ShoppingAdd, ShoppingRemove, Service
} from '@nutui/icons-react-taro'
import {useUser} from "@/hooks/useUser";
const UserCell = () => {
@ -38,9 +48,31 @@ const UserCell = () => {
border: 'none'
} as React.CSSProperties}
>
<Grid.Item text="收货地址" onClick={() => navTo('/user/address/index', true)}>
<Grid.Item text="企业采购" onClick={() => navTo('/user/poster/poster', true)}>
<View className="text-center">
<View className="w-12 h-12 bg-blue-50 rounded-xl flex items-center justify-center mx-auto mb-2">
<ShoppingAdd color="#3b82f6" size="20"/>
</View>
</View>
</Grid.Item>
{/* 修改联系我们为微信客服 */}
<Grid.Item text="联系我们">
<Button
open-type="contact"
className="w-full h-full flex flex-col items-center justify-center p-0 bg-transparent"
hover-class="none"
style={{border: 'none'}}
>
<View className="w-12 h-12 bg-blue-50 rounded-xl flex items-center justify-center mx-auto mb-2">
<Service color="#67C23A" size="20"/>
</View>
</Button>
</Grid.Item>
<Grid.Item text="收货地址" onClick={() => navTo('/user/address/index', true)}>
<View className="text-center">
<View className="w-12 h-12 bg-emerald-50 rounded-xl flex items-center justify-center mx-auto mb-2">
<Location color="#3b82f6" size="20"/>
</View>
</View>
@ -109,3 +141,4 @@ const UserCell = () => {
)
}
export default UserCell

7
src/pages/user/user.scss

@ -1,4 +1,9 @@
.header-bg{
background: url('https://oss.wsdns.cn/20250621/edb5d4da976b4d97ba185cb7077d2858.jpg') no-repeat top center;
//background: url('https://oss.wsdns.cn/20250621/edb5d4da976b4d97ba185cb7077d2858.jpg') no-repeat top center;
background: linear-gradient(to bottom, #03605c, #18ae4f);
background-size: 100%;
}
.my-bg{
background: url('https://oss.wsdns.cn/20250913/5ae575a50dbb4ccaab086c3679c5e2c3.png') no-repeat top center;
}

4
src/user/poster/poster.config.ts

@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '企业采购',
navigationBarTextStyle: 'black'
})

115
src/user/poster/poster.tsx

@ -0,0 +1,115 @@
import { useEffect, useState, useRef } from 'react'
import { Image } from '@nutui/nutui-react-taro'
import { CmsAd } from "@/api/cms/cmsAd/model";
import { getCmsAd } from "@/api/cms/cmsAd";
import navTo from "@/utils/common";
const NaturalFullscreenBanner = () => {
const [bannerData, setBannerData] = useState<CmsAd | null>(null)
const [isLoading, setIsLoading] = useState(true)
const containerRef = useRef<HTMLDivElement>(null)
const imageRef = useRef<HTMLImageElement>(null)
// 加载图片数据
const loadBannerData = () => {
setIsLoading(true)
getCmsAd(447)
.then(data => {
setBannerData(data)
setIsLoading(false)
})
.catch(error => {
console.error('图片数据加载失败:', error)
setIsLoading(false)
})
}
// 处理图片加载完成后调整显示方式
const handleImageLoad = () => {
if (imageRef.current && containerRef.current) {
// 获取图片原始宽高比
const imgRatio = imageRef.current.naturalWidth / imageRef.current.naturalHeight;
// 获取容器宽高比
const containerRatio = containerRef.current.offsetWidth / containerRef.current.offsetHeight;
// 根据比例差异微调显示方式
if (imgRatio > containerRatio) {
// 图片更宽,适当调整以显示更多垂直内容
imageRef.current.style.objectPosition = 'center';
} else {
// 图片更高,适当调整以显示更多水平内容
imageRef.current.style.objectPosition = 'center';
}
}
}
// 设置全屏尺寸
useEffect(() => {
const setFullscreenSize = () => {
if (containerRef.current) {
// 减去可能存在的导航栏高度,使显示更自然
const windowHeight = window.innerHeight - 48; // 假设导航栏高度为48px
const windowWidth = window.innerWidth;
containerRef.current.style.height = `${windowHeight}px`;
containerRef.current.style.width = `${windowWidth}px`;
}
};
// 初始化尺寸
setFullscreenSize();
// 监听窗口大小变化
const resizeHandler = () => setFullscreenSize();
window.addEventListener('resize', resizeHandler);
return () => window.removeEventListener('resize', resizeHandler);
}, []);
useEffect(() => {
loadBannerData()
}, [])
if (isLoading) {
return (
<div ref={containerRef} className="flex items-center justify-center bg-gray-100">
<span className="text-gray-500 text-sm">...</span>
</div>
)
}
// 获取第一张图片,如果有
const firstImage = bannerData?.imageList?.[0];
if (!firstImage) {
return (
<div ref={containerRef} className="flex items-center justify-center bg-gray-100">
<span className="text-gray-500 text-sm"></span>
</div>
)
}
return (
<div ref={containerRef} className="relative overflow-hidden bg-black">
<Image
ref={imageRef}
className="absolute inset-0"
src={firstImage.url}
mode={'scaleToFill'}
onClick={() => firstImage.path && navTo(firstImage.path)}
lazyLoad={false}
alt="全屏 banner 图"
onLoad={handleImageLoad}
style={{
width: '100%',
height: '100%',
// 优先保持比例,只裁剪必要部分
objectFit: 'cover',
// 默认居中显示,保留图片主体内容
objectPosition: 'center center'
}}
/>
</div>
)
}
export default NaturalFullscreenBanner
Loading…
Cancel
Save