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 = {
// 开发环境
development: {
// API_BASE_URL: 'http://127.0.0.1:9200/api',
API_BASE_URL: 'https://cms-api.websoft.top/api',
API_BASE_URL: 'http://127.0.0.1:9200/api',
// API_BASE_URL: 'https://cms-api.websoft.top/api',
APP_NAME: '开发环境',
DEBUG: 'true',
},

View File

@@ -383,7 +383,7 @@ const AddUserAddress = () => {
<View className={'bg-gray-100 h-3'}></View>
<CellGroup style={{padding: '4px 0'}}>
<Form.Item name="refereeId" label="邀请人ID" initialValue={FormData?.refereeId} required>
<Input placeholder="邀请人ID" disabled={true}/>
<Input placeholder="邀请人ID" disabled={false}/>
</Form.Item>
<Form.Item name="phone" label="手机号" initialValue={FormData?.phone} required>
<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 navTo from "@/utils/common";
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 [carouselData, setCarouselData] = useState<CmsAd>()
const [hotToday, setHotToday] = useState<CmsAd>()
const [item, setItem] = useState<CmsArticle>()
const [loading, setLoading] = useState(true)
// const [disableSwiper, setDisableSwiper] = useState(false)
@@ -21,24 +46,21 @@ const MyPage = () => {
// 加载数据
const loadData = async () => {
setLoading(true)
try {
setLoading(true)
// 轮播图
const flash = await getCmsAdByCode('flash')
// 今日热卖
const hotToday = await getCmsAdByCode('hot_today')
// 时里动态
const news = await pageCmsArticle({limit:1,recommend:1})
// 赋值
if(flash){
setCarouselData(flash)
}
if(hotToday){
setHotToday(hotToday)
}
if(news && news.list.length > 0){
setItem(news.list[0])
const [flashRes] = await Promise.allSettled([
getCmsAdByCode('flash'),
getCmsAdByCode('hot_today'),
pageCmsArticle({ limit: 1, recommend: 1 }),
])
if (flashRes.status === 'fulfilled') {
console.log('flashflashflash', flashRes.value)
setCarouselData(flashRes.value)
} else {
console.error('Failed to fetch flash:', flashRes.reason)
}
} catch (error) {
console.error('Banner数据加载失败:', error)
} finally {
@@ -47,11 +69,13 @@ const MyPage = () => {
}
useEffect(() => {
loadData()
loadData().then()
}, [])
// 轮播图高度默认300px
const carouselHeight = carouselData?.height || 300;
const carouselHeight = toNumberPx(carouselData?.height, 300)
const carouselImages = normalizeAdImages(carouselData)
console.log(carouselImages,'carouselImages')
// 骨架屏组件
const BannerSkeleton = () => (
@@ -100,94 +124,42 @@ const MyPage = () => {
}
return (
<View className="flex p-2 justify-between" style={{height: `${carouselHeight}px`}}>
{/* 左侧轮播图区域 */}
<View
style={{width: '50%', height: '100%'}}
className="banner-swiper-container"
>
<Swiper
defaultValue={0}
height={carouselHeight}
indicator
autoPlay
duration={3000}
style={{
height: `${carouselHeight}px`,
touchAction: 'pan-y' // 关键修改:允许垂直滑动
}}
disableTouch={false}
direction="horizontal"
className="custom-swiper"
>
{carouselData && carouselData?.imageList?.map((img, index) => (
<Swiper.Item key={index} style={{ touchAction: 'pan-x pan-y' }}>
<Image
width="100%"
height="100%"
src={img.url}
mode={'scaleToFill'}
onClick={() => navTo(`${img.path}`)}
lazyLoad={false}
style={{
height: `${carouselHeight}px`,
borderRadius: '4px',
touchAction: 'manipulation' // 关键修改:优化触摸操作
}}
/>
</Swiper.Item>
))}
</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'}>
<Swiper
defaultValue={0}
height={carouselHeight}
indicator
autoPlay
duration={3000}
style={{
height: `${carouselHeight}px`,
touchAction: 'pan-y' // 关键修改:允许垂直滑动
}}
disableTouch={false}
direction="horizontal"
className="custom-swiper"
>
{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' }}>
<Image
width={'100%'}
height={94}
src={item?.image}
width="100%"
height="100%"
src={src}
mode={'scaleToFill'}
onClick={() => (img.path ? navTo(`${img.path}`) : undefined)}
lazyLoad={false}
style={{
borderRadius: '4px'
height: `${carouselHeight}px`,
borderRadius: '4px',
touchAction: 'manipulation' // 关键修改:优化触摸操作
}}
onClick={() => navTo('cms/detail/index?id=' + item?.articleId)}
/>
</View>
</View>
</View>
</View>
</Swiper.Item>
)
})}
</Swiper>
)
}

View File

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

View File

@@ -1,4 +1,5 @@
import Header from './Header'
import Banner from './Banner'
import Taro, { useShareAppMessage } from '@tarojs/taro'
import { View, Text, Image, ScrollView } from '@tarojs/components'
import { useEffect, useMemo, useState, type ReactNode } from 'react'
@@ -199,39 +200,8 @@ function Home() {
<Header />
<View className="home-page">
{/* 顶部活动主视觉 */}
<View className="home-hero">
<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>
{/* 顶部活动主视觉:使用 Banner 组件 */}
<Banner />
{/* 电子水票 */}
<View className="ticket-card">
@@ -283,7 +253,7 @@ function Home() {
<View className="goods-card__imgWrap">
<Image
className="goods-card__img"
src={item.image}
src={item.image || ''}
mode="aspectFill"
lazyLoad={false}
onClick={() =>

View File

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