feat(theme): 实现主题切换系统并优化经销商相关页面

- 新增主题切换系统,支持智能主题和手动选择
- 更新经销商首页、团队、订单、提现等页面样式
- 添加主题相关的Hook和样式工具函数
- 优化部分组件样式以适配新主题
This commit is contained in:
2025-08-19 00:08:26 +08:00
parent 8efeb9a5bd
commit 9d9762ef17
23 changed files with 739 additions and 264 deletions

View File

@@ -16,8 +16,8 @@ const getInfiniteUlStyle = (showSearch: boolean = false): CSSProperties => ({
width: '100%',
padding: '0',
overflowY: 'auto',
overflowX: 'hidden',
boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)',
overflowX: 'hidden'
// 注意:小程序不支持 boxShadow
})
// 统一的订单状态标签配置,与后端 statusFilter 保持一致
@@ -362,8 +362,8 @@ function OrderList(props: OrderListProps) {
borderBottom: '1px solid #e5e5e5'
}}
tabStyle={{
backgroundColor: '#ffffff',
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
backgroundColor: '#ffffff'
// 注意:小程序不支持 boxShadow
}}
value={tapIndex}
onChange={(paneKey) => {

View File

@@ -31,6 +31,7 @@ const StoreVerification: React.FC = () => {
const json = JSON.parse(res.result)
console.log(json, 'json')
if (json.businessType === 'gift') {
// 调用解密接口
handleDecryptAndVerify(json.token, json.data).then()
}
}
@@ -48,20 +49,35 @@ const StoreVerification: React.FC = () => {
// 调用解密接口
const handleDecryptAndVerify = async (token: string, encryptedData: string) => {
const decryptedData = await decryptQrData({token, encryptedData})
console.log('解密成功:', decryptedData)
setScanResult(`${decryptedData}`)
setVerificationCode(`${decryptedData}`)
await handleVerification(`${decryptedData}`)
setLoading(false)
decryptQrData({token, encryptedData}).then(res => {
const decryptedData = res;
console.log('解密结果:', decryptedData)
console.log('解密成功:', decryptedData)
setScanResult(`${decryptedData}`)
setVerificationCode(`${decryptedData}`)
handleVerification(`${decryptedData}`)
}).catch(() => {
console.error('解密失败:')
Taro.showToast({
title: `token失效请刷新二维码重试`,
icon: 'none'
})
}).finally(() => {
setLoading(false)
})
}
// 验证商品信息
const handleVerification = async (code?: string) => {
setGiftInfo(null)
setVerificationCode(`${code}`)
// 这里应该调用后端API验证核销码
const gift = await getShopGiftByCode(`${code}`)
// 设置礼品信息用于显示
setGiftInfo(gift)
if(gift){
// 设置礼品信息用于显示
setGiftInfo(gift)
}
}
// 手动输入核销码验证
@@ -264,10 +280,8 @@ const StoreVerification: React.FC = () => {
{giftInfo.description && (
<>
<View className="text-sm text-gray-600 mb-2" style={{
display: '-webkit-box',
WebkitLineClamp: 2,
WebkitBoxOrient: 'vertical',
overflow: 'hidden'
// 注意:小程序不支持 WebKit 文本截断属性
}}>
{giftInfo.description}
</View>

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '主题设置',
navigationBarTextStyle: 'black'
})

179
src/user/theme/index.tsx Normal file
View File

@@ -0,0 +1,179 @@
import React, { useState, useEffect } from 'react'
import { View, Text } from '@tarojs/components'
import { Cell, CellGroup, Radio } from '@nutui/nutui-react-taro'
import { gradientThemes, GradientTheme, gradientUtils } from '@/styles/gradients'
import Taro from '@tarojs/taro'
import FixedButton from "@/components/FixedButton";
const ThemeSelector: React.FC = () => {
const [selectedTheme, setSelectedTheme] = useState<string>('')
const [currentTheme, setCurrentTheme] = useState<GradientTheme | null>(null)
// 获取当前主题
useEffect(() => {
const savedTheme = Taro.getStorageSync('user_theme') || 'auto'
setSelectedTheme(savedTheme)
if (savedTheme === 'auto') {
// 自动主题根据用户ID生成
const userId = Taro.getStorageSync('userId') || '1'
const theme = gradientUtils.getThemeByUserId(userId)
setCurrentTheme(theme)
} else {
// 手动选择的主题
const theme = gradientThemes.find(t => t.name === savedTheme)
setCurrentTheme(theme || gradientThemes[0])
}
}, [])
// 保存主题设置
const saveTheme = (themeName: string) => {
try {
Taro.setStorageSync('user_theme', themeName)
setSelectedTheme(themeName)
if (themeName === 'auto') {
const userId = Taro.getStorageSync('userId') || '1'
const theme = gradientUtils.getThemeByUserId(userId)
setCurrentTheme(theme)
} else {
const theme = gradientThemes.find(t => t.name === themeName)
setCurrentTheme(theme || gradientThemes[0])
}
Taro.showToast({
title: '主题已保存',
icon: 'success',
})
// 延迟返回,让用户看到效果
setTimeout(() => {
Taro.navigateBack()
}, 1000)
} catch (error) {
Taro.showToast({
title: '保存失败',
icon: 'error',
})
}
}
// 预览主题
const previewTheme = (themeName: string) => {
if (themeName === 'auto') {
const userId = Taro.getStorageSync('userId') || '1'
const theme = gradientUtils.getThemeByUserId(userId)
setCurrentTheme(theme)
} else {
const theme = gradientThemes.find(t => t.name === themeName)
setCurrentTheme(theme || gradientThemes[0])
}
}
return (
<View className="min-h-screen bg-gray-50">
{/* 当前主题预览 */}
{currentTheme && (
<View
className="mx-4 mt-4 rounded-xl p-6 text-center"
style={{
background: currentTheme.background,
color: currentTheme.textColor
}}
>
<Text className="text-lg font-bold mb-2"></Text>
<Text className="text-sm opacity-90 px-2">{currentTheme.description}</Text>
<View className="mt-4 flex justify-center space-x-4">
<View
className="w-8 h-8 rounded-full"
style={{ backgroundColor: currentTheme.primary }}
></View>
{currentTheme.secondary && (
<View
className="w-8 h-8 rounded-full"
style={{ backgroundColor: currentTheme.secondary }}
></View>
)}
</View>
</View>
)}
{/* 主题选择 */}
<View className="mt-4">
<CellGroup>
<Cell
className="px-4 py-2"
title={
<View className="flex items-center justify-between w-full">
<View>
<Text className="font-medium"></Text>
<Text className="text-sm text-gray-500 mt-1">
ID自动选择个性化主题
</Text>
</View>
<Radio
checked={selectedTheme === 'auto'}
onChange={() => {
setSelectedTheme('auto')
previewTheme('auto')
}}
/>
</View>
}
onClick={() => {
setSelectedTheme('auto')
previewTheme('auto')
}}
/>
</CellGroup>
<View className="mt-4">
<Text className="text-sm text-gray-600 px-4 mb-2"></Text>
<CellGroup>
{gradientThemes.map((theme) => (
<Cell
key={theme.name}
className="px-4 py-3"
title={
<View className="flex items-center justify-between w-full">
<View className="flex items-center">
<View
className="w-6 h-6 rounded-full mr-3"
style={{ background: theme.background }}
></View>
<View>
<Text className="font-medium">{theme.description.split(' - ')[0]}</Text>
<Text className="text-sm text-gray-500 mt-1">
{theme.description.split(' - ')[1]}
</Text>
</View>
</View>
<Radio
checked={selectedTheme === theme.name}
onChange={() => {
setSelectedTheme(theme.name)
previewTheme(theme.name)
}}
/>
</View>
}
onClick={() => {
setSelectedTheme(theme.name)
previewTheme(theme.name)
}}
/>
))}
</CellGroup>
</View>
</View>
{/* 保存按钮 */}
<FixedButton text={'保存主题设置'} background={currentTheme?.background || '#1890ff'} onClick={() => saveTheme(selectedTheme)} />
{/* 底部安全区域 */}
<View className="h-20"></View>
</View>
)
}
export default ThemeSelector