forked from gxwebsoft/mp-10550
feat(home): 重构首页轮播图组件并优化广告数据处理
- 修改首页轮播图组件,替换为新的 Banner 组件实现 - 新增广告图片数据标准化处理函数,支持多种字段格式兼容 - 优化首页广告数据加载逻辑,改用 Promise.allSettled 并行请求 - 修复轮播图高度计算,添加数字转换安全处理 - 调整经销商申请页面文本,将"入驻申请"改为"门店入驻" - 修复商品卡片图片显示,添加空值处理防止报错 - 临时隐藏搜索栏组件,设置为隐藏状态 - 恢复开发环境 API 地址配置,便于本地调试 - 移除经销商申请表单中邀请人 ID 的禁用状态
This commit is contained in:
@@ -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',
|
||||
},
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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={{
|
||||
|
||||
@@ -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={() =>
|
||||
|
||||
@@ -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}/>}
|
||||
|
||||
Reference in New Issue
Block a user