forked from gxwebsoft/mp-10550
feat(home): 重构首页界面并更新API配置
- 移除底部导航栏中的"基地生活"选项卡 - 切换开发环境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',
|
||||
},
|
||||
|
||||
@@ -116,12 +116,6 @@ export default {
|
||||
selectedIconPath: "assets/tabbar/home-active.png",
|
||||
text: "首页",
|
||||
},
|
||||
{
|
||||
pagePath: "pages/category/index",
|
||||
iconPath: "assets/tabbar/category.png",
|
||||
selectedIconPath: "assets/tabbar/category-active.png",
|
||||
text: "基地生活",
|
||||
},
|
||||
{
|
||||
pagePath: "pages/cart/cart",
|
||||
iconPath: "assets/tabbar/cart.png",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { gradientThemes, GradientTheme, gradientUtils } from '@/styles/gradients'
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { gradientThemes, type GradientTheme, gradientUtils } from '@/styles/gradients'
|
||||
import Taro from '@tarojs/taro'
|
||||
|
||||
export interface UseThemeReturn {
|
||||
@@ -14,28 +14,42 @@ export interface UseThemeReturn {
|
||||
* 提供主题切换和状态管理功能
|
||||
*/
|
||||
export const useTheme = (): UseThemeReturn => {
|
||||
const [currentTheme, setCurrentTheme] = useState<GradientTheme>(gradientThemes[0])
|
||||
const [isAutoTheme, setIsAutoTheme] = useState<boolean>(true)
|
||||
|
||||
// 获取当前主题
|
||||
const getCurrentTheme = (): GradientTheme => {
|
||||
const savedTheme = Taro.getStorageSync('user_theme') || 'auto'
|
||||
|
||||
if (savedTheme === 'auto') {
|
||||
// 自动主题:根据用户ID生成
|
||||
const userId = Taro.getStorageSync('userId') || '1'
|
||||
return gradientUtils.getThemeByUserId(userId)
|
||||
} else {
|
||||
// 手动选择的主题
|
||||
return gradientThemes.find(t => t.name === savedTheme) || gradientThemes[0]
|
||||
const getSavedThemeName = useCallback((): string => {
|
||||
try {
|
||||
return Taro.getStorageSync('user_theme') || 'nature'
|
||||
} catch {
|
||||
return 'nature'
|
||||
}
|
||||
}, [])
|
||||
|
||||
const getStoredUserId = useCallback((): number => {
|
||||
try {
|
||||
const raw = Taro.getStorageSync('UserId') ?? Taro.getStorageSync('userId')
|
||||
const asNumber = typeof raw === 'number' ? raw : parseInt(String(raw || '1'), 10)
|
||||
return Number.isFinite(asNumber) ? asNumber : 1
|
||||
} catch {
|
||||
return 1
|
||||
}
|
||||
}, [])
|
||||
|
||||
const resolveTheme = useCallback(
|
||||
(themeName: string): GradientTheme => {
|
||||
if (themeName === 'auto') {
|
||||
return gradientUtils.getThemeByUserId(getStoredUserId())
|
||||
}
|
||||
return gradientThemes.find(t => t.name === themeName) || gradientUtils.getThemeByName('nature') || gradientThemes[0]
|
||||
},
|
||||
[getStoredUserId]
|
||||
)
|
||||
|
||||
const [isAutoTheme, setIsAutoTheme] = useState<boolean>(() => getSavedThemeName() === 'auto')
|
||||
const [currentTheme, setCurrentTheme] = useState<GradientTheme>(() => resolveTheme(getSavedThemeName()))
|
||||
|
||||
// 初始化主题
|
||||
useEffect(() => {
|
||||
const savedTheme = Taro.getStorageSync('user_theme') || 'auto'
|
||||
const savedTheme = getSavedThemeName()
|
||||
setIsAutoTheme(savedTheme === 'auto')
|
||||
setCurrentTheme(getCurrentTheme())
|
||||
setCurrentTheme(resolveTheme(savedTheme))
|
||||
}, [])
|
||||
|
||||
// 设置主题
|
||||
@@ -43,7 +57,7 @@ export const useTheme = (): UseThemeReturn => {
|
||||
try {
|
||||
Taro.setStorageSync('user_theme', themeName)
|
||||
setIsAutoTheme(themeName === 'auto')
|
||||
setCurrentTheme(getCurrentTheme())
|
||||
setCurrentTheme(resolveTheme(themeName))
|
||||
} catch (error) {
|
||||
console.error('保存主题失败:', error)
|
||||
}
|
||||
@@ -51,7 +65,7 @@ export const useTheme = (): UseThemeReturn => {
|
||||
|
||||
// 刷新主题(用于自动主题模式下用户信息变更时)
|
||||
const refreshTheme = () => {
|
||||
setCurrentTheme(getCurrentTheme())
|
||||
setCurrentTheme(resolveTheme(getSavedThemeName()))
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -4,6 +4,376 @@ page {
|
||||
background: linear-gradient(to bottom, #e9fff2, #ffffff);
|
||||
}
|
||||
|
||||
.home-page {
|
||||
padding: 24rpx 24rpx calc(32rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.home-hero {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 28rpx;
|
||||
background: linear-gradient(180deg, #bfefff 0%, #eafaff 40%, #fff7ec 100%);
|
||||
box-shadow: 0 18rpx 36rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.home-hero__bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(360rpx 240rpx at 18% 16%, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0)),
|
||||
radial-gradient(320rpx 220rpx at 84% 18%, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0)),
|
||||
linear-gradient(180deg, rgba(0, 207, 255, 0.12), rgba(0, 0, 0, 0));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.home-hero__content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 18rpx;
|
||||
padding: 26rpx 24rpx 28rpx;
|
||||
min-height: 320rpx;
|
||||
}
|
||||
|
||||
.home-hero__left {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.home-hero__topRow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.home-hero__brand {
|
||||
flex: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 8rpx 14rpx;
|
||||
border-radius: 999rpx;
|
||||
background: rgba(255, 214, 84, 0.92);
|
||||
color: #2a2a2a;
|
||||
font-weight: 700;
|
||||
font-size: 24rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.home-hero__brandText {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.home-hero__tag {
|
||||
flex: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 10rpx 18rpx;
|
||||
border-radius: 18rpx;
|
||||
background: linear-gradient(90deg, #22d64a 0%, #7df4b0 100%);
|
||||
box-shadow: 0 14rpx 24rpx rgba(36, 202, 148, 0.22);
|
||||
}
|
||||
|
||||
.home-hero__tagText {
|
||||
font-size: 56rpx;
|
||||
font-weight: 900;
|
||||
color: #ffffff;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.home-hero__date {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10rpx 14rpx;
|
||||
border-radius: 999rpx;
|
||||
background: rgba(255, 255, 255, 0.75);
|
||||
}
|
||||
|
||||
.home-hero__dateText {
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
color: #1a1a1a;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.home-hero__headline {
|
||||
margin-top: 22rpx;
|
||||
}
|
||||
|
||||
.home-hero__headlineText {
|
||||
display: block;
|
||||
font-size: 42rpx;
|
||||
font-weight: 900;
|
||||
color: #0b0b0b;
|
||||
letter-spacing: 0.5px;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.home-hero__right {
|
||||
width: 200rpx;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.home-hero__bottle {
|
||||
position: relative;
|
||||
width: 190rpx;
|
||||
height: 250rpx;
|
||||
border-radius: 28rpx;
|
||||
background:
|
||||
radial-gradient(240rpx 360rpx at 60% 30%, rgba(255, 255, 255, 0.95), rgba(255, 255, 255, 0.18)),
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.1));
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.65);
|
||||
box-shadow: 0 18rpx 36rpx rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.home-hero__bottleCap {
|
||||
position: absolute;
|
||||
top: 14rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 88rpx;
|
||||
height: 26rpx;
|
||||
border-radius: 999rpx;
|
||||
background: linear-gradient(180deg, #d7e6f3, #b0cadd);
|
||||
box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.home-hero__bottleLabel {
|
||||
position: absolute;
|
||||
left: 18rpx;
|
||||
right: 18rpx;
|
||||
bottom: 30rpx;
|
||||
padding: 12rpx 12rpx;
|
||||
border-radius: 18rpx;
|
||||
background: linear-gradient(90deg, rgba(0, 150, 255, 0.18), rgba(0, 255, 210, 0.18));
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.45);
|
||||
}
|
||||
|
||||
.home-hero__bottleLabelText {
|
||||
font-size: 30rpx;
|
||||
font-weight: 800;
|
||||
color: rgba(0, 80, 140, 0.95);
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ticket-card {
|
||||
margin-top: 18rpx;
|
||||
border-radius: 22rpx;
|
||||
overflow: hidden;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 18rpx 36rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.ticket-card__head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 18rpx 20rpx;
|
||||
background: linear-gradient(90deg, #22d64a 0%, #7df4b0 100%);
|
||||
}
|
||||
|
||||
.ticket-card__title {
|
||||
color: #ffffff;
|
||||
font-weight: 800;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.ticket-card__count {
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.ticket-card__countNum {
|
||||
color: #ffffff;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.ticket-card__body {
|
||||
padding: 20rpx 10rpx 22rpx;
|
||||
}
|
||||
|
||||
.shortcut-grid {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.shortcut-grid__item {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.shortcut-grid__icon {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
border-radius: 18rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: transparent;
|
||||
color: #20c26a;
|
||||
border: 2rpx solid rgba(32, 194, 106, 0.35);
|
||||
}
|
||||
|
||||
.shortcut-grid__text {
|
||||
font-size: 24rpx;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.home-tabs {
|
||||
margin-top: 18rpx;
|
||||
}
|
||||
|
||||
.home-tabs__inner {
|
||||
display: flex;
|
||||
gap: 18rpx;
|
||||
padding: 0 4rpx;
|
||||
}
|
||||
|
||||
.home-tabs__item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10rpx 18rpx;
|
||||
border-radius: 999rpx;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.home-tabs__item--active {
|
||||
background: rgba(32, 194, 106, 0.16);
|
||||
}
|
||||
|
||||
.home-tabs__itemText {
|
||||
font-size: 28rpx;
|
||||
color: #2a2a2a;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.home-tabs__item--active .home-tabs__itemText {
|
||||
color: #16b65a;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.goods-grid {
|
||||
margin-top: 18rpx;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 18rpx;
|
||||
}
|
||||
|
||||
.goods-card {
|
||||
border-radius: 22rpx;
|
||||
overflow: hidden;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 18rpx 36rpx rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.goods-card__imgWrap {
|
||||
padding: 18rpx 18rpx 0;
|
||||
}
|
||||
|
||||
.goods-card__img {
|
||||
width: 100%;
|
||||
height: 280rpx;
|
||||
border-radius: 18rpx;
|
||||
background: #f4f4f4;
|
||||
}
|
||||
|
||||
.goods-card__body {
|
||||
padding: 18rpx 18rpx 20rpx;
|
||||
}
|
||||
|
||||
.goods-card__title {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
color: #1c1c1c;
|
||||
min-height: 72rpx;
|
||||
}
|
||||
|
||||
.goods-card__meta {
|
||||
margin-top: 10rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.goods-card__sold {
|
||||
font-size: 22rpx;
|
||||
color: #9a9a9a;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.goods-card__price {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 4rpx;
|
||||
color: #27c86b;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.goods-card__priceUnit {
|
||||
font-size: 22rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.goods-card__priceValue {
|
||||
font-size: 36rpx;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.goods-card__actions {
|
||||
margin-top: 16rpx;
|
||||
display: flex;
|
||||
gap: 14rpx;
|
||||
}
|
||||
|
||||
.goods-card__btn {
|
||||
flex: 1;
|
||||
height: 64rpx;
|
||||
border-radius: 999rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.goods-card__btn--ghost {
|
||||
border: 2rpx solid rgba(32, 194, 106, 0.7);
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.goods-card__btn--primary {
|
||||
background: linear-gradient(90deg, #24d34c 0%, #6df09a 100%);
|
||||
}
|
||||
|
||||
.goods-card__btnText {
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
color: #18b85a;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.goods-card__btnText--primary {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.buy-btn{
|
||||
height: 70px;
|
||||
background: linear-gradient(to bottom, #1cd98a, #24ca94);
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
import Header from './Header';
|
||||
import BestSellers from './BestSellers';
|
||||
import Taro from '@tarojs/taro';
|
||||
import {useShareAppMessage} from "@tarojs/taro"
|
||||
import {useEffect, useState} from "react";
|
||||
import {getShopInfo} from "@/api/layout";
|
||||
import Menu from "./Menu";
|
||||
import Banner from "./Banner";
|
||||
import {checkAndHandleInviteRelation, hasPendingInvite} from "@/utils/invite";
|
||||
import Header from './Header'
|
||||
import Taro, { useShareAppMessage } from '@tarojs/taro'
|
||||
import { View, Text, Image, ScrollView } from '@tarojs/components'
|
||||
import { useEffect, useMemo, useState, type ReactNode } from 'react'
|
||||
import { Cart, Coupon, Gift, Ticket } from '@nutui/icons-react-taro'
|
||||
import { getShopInfo } from '@/api/layout'
|
||||
import { checkAndHandleInviteRelation, hasPendingInvite } from '@/utils/invite'
|
||||
import { pageShopGoods } from '@/api/shop/shopGoods'
|
||||
import type { ShopGoods } from '@/api/shop/shopGoods/model'
|
||||
import './index.scss'
|
||||
|
||||
function Home() {
|
||||
// 吸顶状态
|
||||
// const [stickyStatus, setStickyStatus] = useState<boolean>(false)
|
||||
// Tabs粘性状态
|
||||
const [_, setTabsStickyStatus] = useState<boolean>(false)
|
||||
const [activeTab, setActiveTab] = useState('推荐')
|
||||
const [goodsList, setGoodsList] = useState<ShopGoods[]>([])
|
||||
|
||||
useShareAppMessage(() => {
|
||||
// 获取当前用户ID,用于生成邀请链接
|
||||
@@ -85,9 +83,7 @@ function Home() {
|
||||
// }
|
||||
|
||||
// 处理Tabs粘性状态变化
|
||||
const handleTabsStickyChange = (isSticky: boolean) => {
|
||||
setTabsStickyStatus(isSticky)
|
||||
}
|
||||
// const handleTabsStickyChange = (isSticky: boolean) => {}
|
||||
|
||||
const reload = () => {
|
||||
|
||||
@@ -99,6 +95,10 @@ function Home() {
|
||||
|
||||
})
|
||||
|
||||
pageShopGoods({}).then(res => {
|
||||
setGoodsList(res?.list || [])
|
||||
})
|
||||
|
||||
// 检查是否有待处理的邀请关系 - 异步处理,不阻塞页面加载
|
||||
if (hasPendingInvite()) {
|
||||
console.log('检测到待处理的邀请关系')
|
||||
@@ -152,16 +152,177 @@ function Home() {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const tabs = useMemo(() => ['推荐', '桶装水', '优惠组合', '购机套餐', '饮水设备'], [])
|
||||
|
||||
const shortcuts = useMemo<
|
||||
Array<{ key: string; title: string; icon: ReactNode; onClick: () => void }>
|
||||
>(
|
||||
() => [
|
||||
{
|
||||
key: 'ticket',
|
||||
title: '充值水票',
|
||||
icon: <Ticket size={30} />,
|
||||
onClick: () => Taro.navigateTo({ url: '/user/wallet/wallet' }),
|
||||
},
|
||||
{
|
||||
key: 'order',
|
||||
title: '立即订水',
|
||||
icon: <Cart size={30} />,
|
||||
onClick: () => Taro.switchTab({ url: '/pages/category/index' }),
|
||||
},
|
||||
{
|
||||
key: 'invite',
|
||||
title: '邀请有礼',
|
||||
icon: <Gift size={30} />,
|
||||
onClick: () => Taro.navigateTo({ url: '/dealer/qrcode/index' }),
|
||||
},
|
||||
{
|
||||
key: 'coupon',
|
||||
title: '领券中心',
|
||||
icon: <Coupon size={30} />,
|
||||
onClick: () => Taro.navigateTo({ url: '/coupon/index' }),
|
||||
},
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
||||
const visibleGoods = useMemo(() => {
|
||||
// 先按效果图展示两列卡片,数据不够时也保持布局稳定
|
||||
const list = goodsList || []
|
||||
if (list.length <= 6) return list
|
||||
return list.slice(0, 6)
|
||||
}, [goodsList])
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Header区域 - 现在由Header组件内部处理吸顶逻辑 */}
|
||||
<Header />
|
||||
|
||||
<div className={'flex flex-col mt-12'}>
|
||||
<Menu/>
|
||||
<Banner/>
|
||||
<BestSellers onStickyChange={handleTabsStickyChange}/>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
{/* 电子水票 */}
|
||||
<View className="ticket-card">
|
||||
<View className="ticket-card__head">
|
||||
<Text className="ticket-card__title">电子水票</Text>
|
||||
<Text className="ticket-card__count">
|
||||
您还有 <Text className="ticket-card__countNum">0</Text> 张水票
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View className="ticket-card__body">
|
||||
<View className="shortcut-grid">
|
||||
{shortcuts.map((item) => (
|
||||
<View
|
||||
key={item.key}
|
||||
className="shortcut-grid__item"
|
||||
onClick={item.onClick}
|
||||
>
|
||||
<View className="shortcut-grid__icon">{item.icon}</View>
|
||||
<Text className="shortcut-grid__text">{item.title}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 分类Tabs */}
|
||||
<ScrollView className="home-tabs" scrollX enableFlex>
|
||||
<View className="home-tabs__inner">
|
||||
{tabs.map((tab) => {
|
||||
const active = tab === activeTab
|
||||
return (
|
||||
<View
|
||||
key={tab}
|
||||
className={`home-tabs__item ${active ? 'home-tabs__item--active' : ''}`}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
>
|
||||
<Text className="home-tabs__itemText">{tab}</Text>
|
||||
</View>
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
{/* 商品列表 */}
|
||||
<View className="goods-grid">
|
||||
{visibleGoods.map((item) => (
|
||||
<View key={item.goodsId} className="goods-card">
|
||||
<View className="goods-card__imgWrap">
|
||||
<Image
|
||||
className="goods-card__img"
|
||||
src={item.image}
|
||||
mode="aspectFill"
|
||||
lazyLoad={false}
|
||||
onClick={() =>
|
||||
Taro.navigateTo({ url: `/shop/goodsDetail/index?id=${item.goodsId}` })
|
||||
}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View className="goods-card__body">
|
||||
<Text className="goods-card__title">{item.name}</Text>
|
||||
<View className="goods-card__meta">
|
||||
<Text className="goods-card__sold">已购:{item.sales || 0}人</Text>
|
||||
<View className="goods-card__price">
|
||||
<Text className="goods-card__priceUnit">¥</Text>
|
||||
<Text className="goods-card__priceValue">{item.price}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className="goods-card__actions">
|
||||
<View
|
||||
className="goods-card__btn goods-card__btn--ghost"
|
||||
onClick={() => Taro.navigateTo({ url: '/user/coupon/index' })}
|
||||
>
|
||||
<Text className="goods-card__btnText">买水票更优惠</Text>
|
||||
</View>
|
||||
<View
|
||||
className="goods-card__btn goods-card__btn--primary"
|
||||
onClick={() =>
|
||||
Taro.navigateTo({ url: `/shop/goodsDetail/index?id=${item.goodsId}` })
|
||||
}
|
||||
>
|
||||
<Text className="goods-card__btnText goods-card__btnText--primary">立即购买</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ const ThemeSelector: React.FC = () => {
|
||||
|
||||
// 获取当前主题
|
||||
useEffect(() => {
|
||||
const savedTheme = Taro.getStorageSync('user_theme') || 'auto'
|
||||
const savedTheme = Taro.getStorageSync('user_theme') || 'nature'
|
||||
setSelectedTheme(savedTheme)
|
||||
|
||||
if (savedTheme === 'auto') {
|
||||
// 自动主题:根据用户ID生成
|
||||
const userId = Taro.getStorageSync('userId') || '1'
|
||||
const theme = gradientUtils.getThemeByUserId(userId)
|
||||
const userId = Taro.getStorageSync('UserId') ?? Taro.getStorageSync('userId') ?? '1'
|
||||
const theme = gradientUtils.getThemeByUserId(typeof userId === 'number' ? userId : parseInt(String(userId), 10))
|
||||
setCurrentTheme(theme)
|
||||
} else {
|
||||
// 手动选择的主题
|
||||
@@ -33,8 +33,8 @@ const ThemeSelector: React.FC = () => {
|
||||
setSelectedTheme(themeName)
|
||||
|
||||
if (themeName === 'auto') {
|
||||
const userId = Taro.getStorageSync('userId') || '1'
|
||||
const theme = gradientUtils.getThemeByUserId(userId)
|
||||
const userId = Taro.getStorageSync('UserId') ?? Taro.getStorageSync('userId') ?? '1'
|
||||
const theme = gradientUtils.getThemeByUserId(typeof userId === 'number' ? userId : parseInt(String(userId), 10))
|
||||
setCurrentTheme(theme)
|
||||
} else {
|
||||
const theme = gradientThemes.find(t => t.name === themeName)
|
||||
@@ -61,8 +61,8 @@ const ThemeSelector: React.FC = () => {
|
||||
// 预览主题
|
||||
const previewTheme = (themeName: string) => {
|
||||
if (themeName === 'auto') {
|
||||
const userId = Taro.getStorageSync('userId') || '1'
|
||||
const theme = gradientUtils.getThemeByUserId(userId)
|
||||
const userId = Taro.getStorageSync('UserId') ?? Taro.getStorageSync('userId') ?? '1'
|
||||
const theme = gradientUtils.getThemeByUserId(typeof userId === 'number' ? userId : parseInt(String(userId), 10))
|
||||
setCurrentTheme(theme)
|
||||
} else {
|
||||
const theme = gradientThemes.find(t => t.name === themeName)
|
||||
|
||||
Reference in New Issue
Block a user