forked from gxwebsoft/mp-10550
feat(home): 重构首页界面并更新API配置
- 移除底部导航栏中的"基地生活"选项卡 - 切换开发环境API地址为线上测试接口 - 添加完整的首页样式定义,包括英雄区域、商品卡片、快捷入口等 - 重构首页组件结构,集成商品列表、分类标签页和交互功能 - 更新主题管理逻辑,支持多种主题模式和用户ID兼容处理 - 添加商品数据获取和展示功能,实现首页内容动态加载
This commit is contained in:
@@ -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',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -116,12 +116,6 @@ export default {
|
|||||||
selectedIconPath: "assets/tabbar/home-active.png",
|
selectedIconPath: "assets/tabbar/home-active.png",
|
||||||
text: "首页",
|
text: "首页",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
pagePath: "pages/category/index",
|
|
||||||
iconPath: "assets/tabbar/category.png",
|
|
||||||
selectedIconPath: "assets/tabbar/category-active.png",
|
|
||||||
text: "基地生活",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
pagePath: "pages/cart/cart",
|
pagePath: "pages/cart/cart",
|
||||||
iconPath: "assets/tabbar/cart.png",
|
iconPath: "assets/tabbar/cart.png",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect, useCallback } from 'react'
|
||||||
import { gradientThemes, GradientTheme, gradientUtils } from '@/styles/gradients'
|
import { gradientThemes, type GradientTheme, gradientUtils } from '@/styles/gradients'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
|
|
||||||
export interface UseThemeReturn {
|
export interface UseThemeReturn {
|
||||||
@@ -14,28 +14,42 @@ export interface UseThemeReturn {
|
|||||||
* 提供主题切换和状态管理功能
|
* 提供主题切换和状态管理功能
|
||||||
*/
|
*/
|
||||||
export const useTheme = (): UseThemeReturn => {
|
export const useTheme = (): UseThemeReturn => {
|
||||||
const [currentTheme, setCurrentTheme] = useState<GradientTheme>(gradientThemes[0])
|
const getSavedThemeName = useCallback((): string => {
|
||||||
const [isAutoTheme, setIsAutoTheme] = useState<boolean>(true)
|
try {
|
||||||
|
return Taro.getStorageSync('user_theme') || 'nature'
|
||||||
// 获取当前主题
|
} catch {
|
||||||
const getCurrentTheme = (): GradientTheme => {
|
return 'nature'
|
||||||
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 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(() => {
|
useEffect(() => {
|
||||||
const savedTheme = Taro.getStorageSync('user_theme') || 'auto'
|
const savedTheme = getSavedThemeName()
|
||||||
setIsAutoTheme(savedTheme === 'auto')
|
setIsAutoTheme(savedTheme === 'auto')
|
||||||
setCurrentTheme(getCurrentTheme())
|
setCurrentTheme(resolveTheme(savedTheme))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
// 设置主题
|
// 设置主题
|
||||||
@@ -43,7 +57,7 @@ export const useTheme = (): UseThemeReturn => {
|
|||||||
try {
|
try {
|
||||||
Taro.setStorageSync('user_theme', themeName)
|
Taro.setStorageSync('user_theme', themeName)
|
||||||
setIsAutoTheme(themeName === 'auto')
|
setIsAutoTheme(themeName === 'auto')
|
||||||
setCurrentTheme(getCurrentTheme())
|
setCurrentTheme(resolveTheme(themeName))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存主题失败:', error)
|
console.error('保存主题失败:', error)
|
||||||
}
|
}
|
||||||
@@ -51,7 +65,7 @@ export const useTheme = (): UseThemeReturn => {
|
|||||||
|
|
||||||
// 刷新主题(用于自动主题模式下用户信息变更时)
|
// 刷新主题(用于自动主题模式下用户信息变更时)
|
||||||
const refreshTheme = () => {
|
const refreshTheme = () => {
|
||||||
setCurrentTheme(getCurrentTheme())
|
setCurrentTheme(resolveTheme(getSavedThemeName()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -4,6 +4,376 @@ page {
|
|||||||
background: linear-gradient(to bottom, #e9fff2, #ffffff);
|
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{
|
.buy-btn{
|
||||||
height: 70px;
|
height: 70px;
|
||||||
background: linear-gradient(to bottom, #1cd98a, #24ca94);
|
background: linear-gradient(to bottom, #1cd98a, #24ca94);
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
import Header from './Header';
|
import Header from './Header'
|
||||||
import BestSellers from './BestSellers';
|
import Taro, { useShareAppMessage } from '@tarojs/taro'
|
||||||
import Taro from '@tarojs/taro';
|
import { View, Text, Image, ScrollView } from '@tarojs/components'
|
||||||
import {useShareAppMessage} from "@tarojs/taro"
|
import { useEffect, useMemo, useState, type ReactNode } from 'react'
|
||||||
import {useEffect, useState} from "react";
|
import { Cart, Coupon, Gift, Ticket } from '@nutui/icons-react-taro'
|
||||||
import {getShopInfo} from "@/api/layout";
|
import { getShopInfo } from '@/api/layout'
|
||||||
import Menu from "./Menu";
|
import { checkAndHandleInviteRelation, hasPendingInvite } from '@/utils/invite'
|
||||||
import Banner from "./Banner";
|
import { pageShopGoods } from '@/api/shop/shopGoods'
|
||||||
import {checkAndHandleInviteRelation, hasPendingInvite} from "@/utils/invite";
|
import type { ShopGoods } from '@/api/shop/shopGoods/model'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
function Home() {
|
function Home() {
|
||||||
// 吸顶状态
|
const [activeTab, setActiveTab] = useState('推荐')
|
||||||
// const [stickyStatus, setStickyStatus] = useState<boolean>(false)
|
const [goodsList, setGoodsList] = useState<ShopGoods[]>([])
|
||||||
// Tabs粘性状态
|
|
||||||
const [_, setTabsStickyStatus] = useState<boolean>(false)
|
|
||||||
|
|
||||||
useShareAppMessage(() => {
|
useShareAppMessage(() => {
|
||||||
// 获取当前用户ID,用于生成邀请链接
|
// 获取当前用户ID,用于生成邀请链接
|
||||||
@@ -85,9 +83,7 @@ function Home() {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// 处理Tabs粘性状态变化
|
// 处理Tabs粘性状态变化
|
||||||
const handleTabsStickyChange = (isSticky: boolean) => {
|
// const handleTabsStickyChange = (isSticky: boolean) => {}
|
||||||
setTabsStickyStatus(isSticky)
|
|
||||||
}
|
|
||||||
|
|
||||||
const reload = () => {
|
const reload = () => {
|
||||||
|
|
||||||
@@ -99,6 +95,10 @@ function Home() {
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
pageShopGoods({}).then(res => {
|
||||||
|
setGoodsList(res?.list || [])
|
||||||
|
})
|
||||||
|
|
||||||
// 检查是否有待处理的邀请关系 - 异步处理,不阻塞页面加载
|
// 检查是否有待处理的邀请关系 - 异步处理,不阻塞页面加载
|
||||||
if (hasPendingInvite()) {
|
if (hasPendingInvite()) {
|
||||||
console.log('检测到待处理的邀请关系')
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Header区域 - 现在由Header组件内部处理吸顶逻辑 */}
|
{/* Header区域 - 现在由Header组件内部处理吸顶逻辑 */}
|
||||||
<Header />
|
<Header />
|
||||||
|
|
||||||
<div className={'flex flex-col mt-12'}>
|
<View className="home-page">
|
||||||
<Menu/>
|
{/* 顶部活动主视觉 */}
|
||||||
<Banner/>
|
<View className="home-hero">
|
||||||
<BestSellers onStickyChange={handleTabsStickyChange}/>
|
<View className="home-hero__bg" />
|
||||||
</div>
|
<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(() => {
|
useEffect(() => {
|
||||||
const savedTheme = Taro.getStorageSync('user_theme') || 'auto'
|
const savedTheme = Taro.getStorageSync('user_theme') || 'nature'
|
||||||
setSelectedTheme(savedTheme)
|
setSelectedTheme(savedTheme)
|
||||||
|
|
||||||
if (savedTheme === 'auto') {
|
if (savedTheme === 'auto') {
|
||||||
// 自动主题:根据用户ID生成
|
// 自动主题:根据用户ID生成
|
||||||
const userId = Taro.getStorageSync('userId') || '1'
|
const userId = Taro.getStorageSync('UserId') ?? Taro.getStorageSync('userId') ?? '1'
|
||||||
const theme = gradientUtils.getThemeByUserId(userId)
|
const theme = gradientUtils.getThemeByUserId(typeof userId === 'number' ? userId : parseInt(String(userId), 10))
|
||||||
setCurrentTheme(theme)
|
setCurrentTheme(theme)
|
||||||
} else {
|
} else {
|
||||||
// 手动选择的主题
|
// 手动选择的主题
|
||||||
@@ -33,8 +33,8 @@ const ThemeSelector: React.FC = () => {
|
|||||||
setSelectedTheme(themeName)
|
setSelectedTheme(themeName)
|
||||||
|
|
||||||
if (themeName === 'auto') {
|
if (themeName === 'auto') {
|
||||||
const userId = Taro.getStorageSync('userId') || '1'
|
const userId = Taro.getStorageSync('UserId') ?? Taro.getStorageSync('userId') ?? '1'
|
||||||
const theme = gradientUtils.getThemeByUserId(userId)
|
const theme = gradientUtils.getThemeByUserId(typeof userId === 'number' ? userId : parseInt(String(userId), 10))
|
||||||
setCurrentTheme(theme)
|
setCurrentTheme(theme)
|
||||||
} else {
|
} else {
|
||||||
const theme = gradientThemes.find(t => t.name === themeName)
|
const theme = gradientThemes.find(t => t.name === themeName)
|
||||||
@@ -61,8 +61,8 @@ const ThemeSelector: React.FC = () => {
|
|||||||
// 预览主题
|
// 预览主题
|
||||||
const previewTheme = (themeName: string) => {
|
const previewTheme = (themeName: string) => {
|
||||||
if (themeName === 'auto') {
|
if (themeName === 'auto') {
|
||||||
const userId = Taro.getStorageSync('userId') || '1'
|
const userId = Taro.getStorageSync('UserId') ?? Taro.getStorageSync('userId') ?? '1'
|
||||||
const theme = gradientUtils.getThemeByUserId(userId)
|
const theme = gradientUtils.getThemeByUserId(typeof userId === 'number' ? userId : parseInt(String(userId), 10))
|
||||||
setCurrentTheme(theme)
|
setCurrentTheme(theme)
|
||||||
} else {
|
} else {
|
||||||
const theme = gradientThemes.find(t => t.name === themeName)
|
const theme = gradientThemes.find(t => t.name === themeName)
|
||||||
|
|||||||
Reference in New Issue
Block a user