feat(home): 重构首页轮播图组件并优化广告数据处理

- 修改首页轮播图组件,替换为新的 Banner 组件实现
- 新增广告图片数据标准化处理函数,支持多种字段格式兼容
- 优化首页广告数据加载逻辑,改用 Promise.allSettled 并行请求
- 修复轮播图高度计算,添加数字转换安全处理
- 调整经销商申请页面文本,将"入驻申请"改为"门店入驻"
- 修复商品卡片图片显示,添加空值处理防止报错
- 临时隐藏搜索栏组件,设置为隐藏状态
- 恢复开发环境 API 地址配置,便于本地调试
- 移除经销商申请表单中邀请人 ID 的禁用状态
This commit is contained in:
2026-01-20 11:12:31 +08:00
parent 0770eb1699
commit 0542b93dc7
6 changed files with 86 additions and 144 deletions

View File

@@ -2,8 +2,8 @@
export const ENV_CONFIG = { export const ENV_CONFIG = {
// 开发环境 // 开发环境
development: { development: {
// API_BASE_URL: 'http://127.0.0.1:9200/api', API_BASE_URL: 'http://127.0.0.1:9200/api',
API_BASE_URL: 'https://cms-api.websoft.top/api', // API_BASE_URL: 'https://cms-api.websoft.top/api',
APP_NAME: '开发环境', APP_NAME: '开发环境',
DEBUG: 'true', DEBUG: 'true',
}, },

View File

@@ -383,7 +383,7 @@ const AddUserAddress = () => {
<View className={'bg-gray-100 h-3'}></View> <View className={'bg-gray-100 h-3'}></View>
<CellGroup style={{padding: '4px 0'}}> <CellGroup style={{padding: '4px 0'}}>
<Form.Item name="refereeId" label="邀请人ID" initialValue={FormData?.refereeId} required> <Form.Item name="refereeId" label="邀请人ID" initialValue={FormData?.refereeId} required>
<Input placeholder="邀请人ID" disabled={true}/> <Input placeholder="邀请人ID" disabled={false}/>
</Form.Item> </Form.Item>
<Form.Item name="phone" label="手机号" initialValue={FormData?.phone} required> <Form.Item name="phone" label="手机号" initialValue={FormData?.phone} required>
<View className="flex items-center justify-between"> <View className="flex items-center justify-between">

View File

@@ -6,13 +6,38 @@ import {Image} from '@nutui/nutui-react-taro'
import {getCmsAdByCode} from "@/api/cms/cmsAd"; import {getCmsAdByCode} from "@/api/cms/cmsAd";
import navTo from "@/utils/common"; import navTo from "@/utils/common";
import {pageCmsArticle} from "@/api/cms/cmsArticle"; import {pageCmsArticle} from "@/api/cms/cmsArticle";
import {CmsArticle} from "@/api/cms/cmsArticle/model";
type AdImage = {
url?: string
path?: string
title?: string
// Compatible keys (some backends use different fields)
src?: string
imageUrl?: string
}
function normalizeAdImages(ad?: CmsAd): AdImage[] {
const list = ad?.imageList
if (Array.isArray(list) && list.length) return list as AdImage[]
// Some APIs only return `images` as a JSON string.
const raw = ad?.images
if (!raw) return []
try {
const parsed = JSON.parse(raw)
return Array.isArray(parsed) ? (parsed as AdImage[]) : []
} catch {
return []
}
}
function toNumberPx(input: unknown, fallback: number) {
const n = typeof input === 'number' ? input : Number.parseInt(String(input ?? ''), 10)
return Number.isFinite(n) ? n : fallback
}
const MyPage = () => { const MyPage = () => {
const [carouselData, setCarouselData] = useState<CmsAd>() const [carouselData, setCarouselData] = useState<CmsAd>()
const [hotToday, setHotToday] = useState<CmsAd>()
const [item, setItem] = useState<CmsArticle>()
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
// const [disableSwiper, setDisableSwiper] = useState(false) // const [disableSwiper, setDisableSwiper] = useState(false)
@@ -21,24 +46,21 @@ const MyPage = () => {
// 加载数据 // 加载数据
const loadData = async () => { const loadData = async () => {
try {
setLoading(true) setLoading(true)
// 轮播图 try {
const flash = await getCmsAdByCode('flash') const [flashRes] = await Promise.allSettled([
// 今日热卖 getCmsAdByCode('flash'),
const hotToday = await getCmsAdByCode('hot_today') getCmsAdByCode('hot_today'),
// 时里动态 pageCmsArticle({ limit: 1, recommend: 1 }),
const news = await pageCmsArticle({limit:1,recommend:1}) ])
// 赋值
if(flash){ if (flashRes.status === 'fulfilled') {
setCarouselData(flash) console.log('flashflashflash', flashRes.value)
} setCarouselData(flashRes.value)
if(hotToday){ } else {
setHotToday(hotToday) console.error('Failed to fetch flash:', flashRes.reason)
}
if(news && news.list.length > 0){
setItem(news.list[0])
} }
} catch (error) { } catch (error) {
console.error('Banner数据加载失败:', error) console.error('Banner数据加载失败:', error)
} finally { } finally {
@@ -47,11 +69,13 @@ const MyPage = () => {
} }
useEffect(() => { useEffect(() => {
loadData() loadData().then()
}, []) }, [])
// 轮播图高度默认300px // 轮播图高度默认300px
const carouselHeight = carouselData?.height || 300; const carouselHeight = toNumberPx(carouselData?.height, 300)
const carouselImages = normalizeAdImages(carouselData)
console.log(carouselImages,'carouselImages')
// 骨架屏组件 // 骨架屏组件
const BannerSkeleton = () => ( const BannerSkeleton = () => (
@@ -100,12 +124,6 @@ const MyPage = () => {
} }
return ( return (
<View className="flex p-2 justify-between" style={{height: `${carouselHeight}px`}}>
{/* 左侧轮播图区域 */}
<View
style={{width: '50%', height: '100%'}}
className="banner-swiper-container"
>
<Swiper <Swiper
defaultValue={0} defaultValue={0}
height={carouselHeight} height={carouselHeight}
@@ -120,14 +138,17 @@ const MyPage = () => {
direction="horizontal" direction="horizontal"
className="custom-swiper" className="custom-swiper"
> >
{carouselData && carouselData?.imageList?.map((img, index) => ( {carouselImages.map((img, index) => {
const src = img.url || img.src || img.imageUrl
if (!src) return null
return (
<Swiper.Item key={index} style={{ touchAction: 'pan-x pan-y' }}> <Swiper.Item key={index} style={{ touchAction: 'pan-x pan-y' }}>
<Image <Image
width="100%" width="100%"
height="100%" height="100%"
src={img.url} src={src}
mode={'scaleToFill'} mode={'scaleToFill'}
onClick={() => navTo(`${img.path}`)} onClick={() => (img.path ? navTo(`${img.path}`) : undefined)}
lazyLoad={false} lazyLoad={false}
style={{ style={{
height: `${carouselHeight}px`, height: `${carouselHeight}px`,
@@ -136,58 +157,9 @@ const MyPage = () => {
}} }}
/> />
</Swiper.Item> </Swiper.Item>
))} )
})}
</Swiper> </Swiper>
</View>
{/* 右侧上下图片区域 - 从API获取数据 */}
<View className="flex flex-col" style={{width: '50%', height: '100%'}}>
{/* 上层图片 - 使用今日热卖素材 */}
<View className={'ml-2 bg-white rounded-lg shadow-sm'}>
<View className={'px-3 my-2 font-bold text-sm'}></View>
<View className={'px-3 flex'} style={{
height: '110px'
}}>
{
hotToday?.imageList?.map(item => (
<View className={'item flex flex-col mr-1'} key={item.url}>
<Image
width={70}
height={70}
src={item.url}
mode={'scaleToFill'}
lazyLoad={false}
style={{
borderRadius: '4px'
}}
onClick={() => navTo('/shop/category/index?id=4424')}
/>
<View className={'text-xs py-2 text-orange-600 whitespace-nowrap'}>{item.title || '到手价¥9.9'}</View>
</View>
))
}
</View>
</View>
{/* 下层图片 - 使用社区拼团素材 */}
<View className={'ml-2 bg-white rounded-lg mt-3 shadow-sm'}>
<View className={'px-3 my-2 font-bold text-sm'}></View>
<View className={'rounded-lg px-3 pb-3'}>
<Image
width={'100%'}
height={94}
src={item?.image}
mode={'scaleToFill'}
lazyLoad={false}
style={{
borderRadius: '4px'
}}
onClick={() => navTo('cms/detail/index?id=' + item?.articleId)}
/>
</View>
</View>
</View>
</View>
) )
} }

View File

@@ -30,7 +30,7 @@ function MySearch(props: any) {
return ( return (
<div className={'z-50 left-0 w-full'}> <div className={'z-50 left-0 w-full hidden'}>
<div className={'px-2'}> <div className={'px-2'}>
<div <div
style={{ style={{

View File

@@ -1,4 +1,5 @@
import Header from './Header' import Header from './Header'
import Banner from './Banner'
import Taro, { useShareAppMessage } from '@tarojs/taro' import Taro, { useShareAppMessage } from '@tarojs/taro'
import { View, Text, Image, ScrollView } from '@tarojs/components' import { View, Text, Image, ScrollView } from '@tarojs/components'
import { useEffect, useMemo, useState, type ReactNode } from 'react' import { useEffect, useMemo, useState, type ReactNode } from 'react'
@@ -199,39 +200,8 @@ function Home() {
<Header /> <Header />
<View className="home-page"> <View className="home-page">
{/* 顶部活动主视觉 */} {/* 顶部活动主视觉:使用 Banner 组件 */}
<View className="home-hero"> <Banner />
<View className="home-hero__bg" />
<View className="home-hero__content">
<View className="home-hero__left">
<View className="home-hero__topRow">
<View className="home-hero__brand">
<Text className="home-hero__brandText"></Text>
</View>
<View className="home-hero__tag">
<Text className="home-hero__tagText"></Text>
</View>
<View className="home-hero__date">
<Text className="home-hero__dateText">202X年X月X日 - X月X日</Text>
</View>
</View>
<View className="home-hero__headline">
<Text className="home-hero__headlineText"></Text>
<Text className="home-hero__headlineText">15L</Text>
</View>
</View>
<View className="home-hero__right">
<View className="home-hero__bottle">
<View className="home-hero__bottleCap" />
<View className="home-hero__bottleLabel">
<Text className="home-hero__bottleLabelText"></Text>
</View>
</View>
</View>
</View>
</View>
{/* 电子水票 */} {/* 电子水票 */}
<View className="ticket-card"> <View className="ticket-card">
@@ -283,7 +253,7 @@ function Home() {
<View className="goods-card__imgWrap"> <View className="goods-card__imgWrap">
<Image <Image
className="goods-card__img" className="goods-card__img"
src={item.image} src={item.image || ''}
mode="aspectFill" mode="aspectFill"
lazyLoad={false} lazyLoad={false}
onClick={() => onClick={() =>

View File

@@ -51,7 +51,7 @@ const IsDealer = () => {
<View style={{display: 'inline-flex', alignItems: 'center'}}> <View style={{display: 'inline-flex', alignItems: 'center'}}>
<Reward className={'text-orange-100 '} size={16}/> <Reward className={'text-orange-100 '} size={16}/>
<Text style={{fontSize: '16px'}} <Text style={{fontSize: '16px'}}
className={'pl-3 text-orange-100 font-medium'}>{config?.vipText || '入驻申请'}</Text> className={'pl-3 text-orange-100 font-medium'}>{config?.vipText || '门店入驻'}</Text>
{/*<Text className={'text-white opacity-80 pl-3'}>门店核销</Text>*/} {/*<Text className={'text-white opacity-80 pl-3'}>门店核销</Text>*/}
</View> </View>
} }
@@ -75,8 +75,8 @@ const IsDealer = () => {
title={ title={
<View style={{display: 'inline-flex', alignItems: 'center'}}> <View style={{display: 'inline-flex', alignItems: 'center'}}>
<Reward className={'text-orange-100 '} size={16}/> <Reward className={'text-orange-100 '} size={16}/>
<Text style={{fontSize: '16px'}} className={'pl-3 text-orange-100 font-medium'}>{config?.vipText || '开通VIP'}</Text> <Text style={{fontSize: '16px'}} className={'pl-3 text-orange-100 font-medium'}>{config?.vipText || '门店入驻'}</Text>
<Text className={'text-white opacity-80 pl-3'}>{config?.vipComments || '享优惠'}</Text> <Text className={'text-white opacity-80 pl-3'}>{config?.vipComments || ''}</Text>
</View> </View>
} }
extra={<ArrowRight color="#cccccc" size={18}/>} extra={<ArrowRight color="#cccccc" size={18}/>}