Browse Source
- 新增主题切换系统,支持智能主题和手动选择 - 更新经销商首页、团队、订单、提现等页面样式 - 添加主题相关的Hook和样式工具函数 - 优化部分组件样式以适配新主题dev
23 changed files with 742 additions and 267 deletions
@ -0,0 +1,191 @@ |
|||
# 🎨 主题切换系统使用指南 |
|||
|
|||
## 📖 功能概述 |
|||
|
|||
我们为你的小程序实现了一套完整的主题切换系统,用户可以选择不同的渐变主题来个性化界面。 |
|||
|
|||
## 🎯 功能特点 |
|||
|
|||
### ✨ 智能主题 |
|||
- **自动选择**:根据用户ID自动分配个性化主题 |
|||
- **8种精美主题**:海洋蓝紫、日落橙红、清新蓝绿、自然绿青、温暖橙黄、梦幻紫粉、经典蓝白、优雅灰黑 |
|||
- **持久化存储**:用户选择会自动保存 |
|||
|
|||
### 🎨 手动选择 |
|||
- **实时预览**:选择主题时立即看到效果 |
|||
- **一键保存**:保存后自动返回上级页面 |
|||
- **全局应用**:主题会应用到所有支持的页面 |
|||
|
|||
## 🚀 如何使用 |
|||
|
|||
### 1. 访问主题设置页面 |
|||
|
|||
用户可以通过以下方式进入主题设置: |
|||
|
|||
```javascript |
|||
// 在任何页面中跳转到主题设置 |
|||
Taro.navigateTo({ |
|||
url: '/user/theme/index' |
|||
}) |
|||
``` |
|||
|
|||
### 2. 在用户中心添加入口 |
|||
|
|||
你可以在用户中心页面添加"主题设置"入口: |
|||
|
|||
```jsx |
|||
<Cell |
|||
title="主题设置" |
|||
extra="个性化界面" |
|||
onClick={() => Taro.navigateTo({ url: '/user/theme/index' })} |
|||
/> |
|||
``` |
|||
|
|||
### 3. 在组件中使用主题 |
|||
|
|||
#### 使用 useThemeStyles Hook |
|||
|
|||
```jsx |
|||
import { useThemeStyles } from '@/hooks/useTheme' |
|||
|
|||
const MyComponent = () => { |
|||
const themeStyles = useThemeStyles() |
|||
|
|||
return ( |
|||
<View style={themeStyles.primaryBackground}> |
|||
<Text style={{ color: themeStyles.textColor }}> |
|||
这里会应用当前主题的样式 |
|||
</Text> |
|||
<Button style={themeStyles.primaryButton}> |
|||
主题按钮 |
|||
</Button> |
|||
</View> |
|||
) |
|||
} |
|||
``` |
|||
|
|||
#### 使用 useTheme Hook |
|||
|
|||
```jsx |
|||
import { useTheme } from '@/hooks/useTheme' |
|||
|
|||
const MyComponent = () => { |
|||
const { currentTheme, setTheme, isAutoTheme } = useTheme() |
|||
|
|||
return ( |
|||
<View style={{ background: currentTheme.background }}> |
|||
<Text style={{ color: currentTheme.textColor }}> |
|||
当前主题:{currentTheme.description} |
|||
</Text> |
|||
{isAutoTheme && <Text>使用智能主题</Text>} |
|||
</View> |
|||
) |
|||
} |
|||
``` |
|||
|
|||
## 🎨 可用主题列表 |
|||
|
|||
| 主题名称 | 主色调 | 描述 | 适用场景 | |
|||
|---------|--------|------|----------| |
|||
| ocean | 蓝紫色 | 海洋蓝紫 - 科技感与专业感 | 商务、科技类应用 | |
|||
| sunset | 橙红色 | 日落橙红 - 活力与热情 | 社交、娱乐类应用 | |
|||
| fresh | 蓝绿色 | 清新蓝绿 - 清新与活力 | 健康、运动类应用 | |
|||
| nature | 绿青色 | 自然绿青 - 生机与成长 | 环保、教育类应用 | |
|||
| warm | 橙黄色 | 温暖橙黄 - 温馨与舒适 | 生活、家居类应用 | |
|||
| dream | 紫粉色 | 梦幻紫粉 - 浪漫与梦幻 | 时尚、美妆类应用 | |
|||
| classic | 蓝白色 | 经典蓝白 - 简约与专业 | 办公、工具类应用 | |
|||
| elegant | 灰黑色 | 优雅灰黑 - 高端与品质 | 奢侈品、艺术类应用 | |
|||
|
|||
## 🔧 开发者指南 |
|||
|
|||
### 添加新主题 |
|||
|
|||
在 `src/styles/gradients.ts` 中添加新主题: |
|||
|
|||
```typescript |
|||
export const gradientThemes: GradientTheme[] = [ |
|||
// 现有主题... |
|||
{ |
|||
name: 'custom', |
|||
primary: '#your-primary-color', |
|||
secondary: '#your-secondary-color', |
|||
background: 'linear-gradient(135deg, #color1 0%, #color2 100%)', |
|||
textColor: '#ffffff', |
|||
description: '自定义主题 - 你的描述' |
|||
} |
|||
] |
|||
``` |
|||
|
|||
### 在新页面中应用主题 |
|||
|
|||
```jsx |
|||
import { useThemeStyles } from '@/hooks/useTheme' |
|||
|
|||
const NewPage = () => { |
|||
const themeStyles = useThemeStyles() |
|||
|
|||
return ( |
|||
<View className="min-h-screen bg-gray-50"> |
|||
{/* 使用主题背景的头部 */} |
|||
<View |
|||
className="px-4 py-6 relative overflow-hidden" |
|||
style={themeStyles.primaryBackground} |
|||
> |
|||
<Text className="text-lg font-bold" style={{ color: themeStyles.textColor }}> |
|||
页面标题 |
|||
</Text> |
|||
</View> |
|||
|
|||
{/* 其他内容 */} |
|||
<View className="p-4"> |
|||
<Button style={themeStyles.primaryButton}> |
|||
主题按钮 |
|||
</Button> |
|||
</View> |
|||
</View> |
|||
) |
|||
} |
|||
``` |
|||
|
|||
## 📱 用户体验 |
|||
|
|||
### 智能主题算法 |
|||
|
|||
系统会根据用户ID生成一个稳定的主题选择: |
|||
|
|||
```typescript |
|||
// 用户ID为 "12345" 的用户总是会得到相同的主题 |
|||
const theme = gradientUtils.getThemeByUserId("12345") |
|||
``` |
|||
|
|||
### 主题持久化 |
|||
|
|||
- 用户选择会保存在本地存储中 |
|||
- 重新打开应用时会自动应用上次选择的主题 |
|||
- 支持"智能主题"和"手动选择"两种模式 |
|||
|
|||
## 🎉 效果展示 |
|||
|
|||
### 智能主题模式 |
|||
- 每个用户都有独特的个性化主题 |
|||
- 基于用户ID算法分配,确保稳定性 |
|||
- 提升用户归属感和个性化体验 |
|||
|
|||
### 手动选择模式 |
|||
- 用户可以自由选择喜欢的主题 |
|||
- 实时预览效果 |
|||
- 一键保存并应用 |
|||
|
|||
## 🔄 更新日志 |
|||
|
|||
### v1.0.0 (2025-01-18) |
|||
- ✅ 实现8种精美渐变主题 |
|||
- ✅ 智能主题自动分配算法 |
|||
- ✅ 主题切换页面UI |
|||
- ✅ useTheme 和 useThemeStyles Hooks |
|||
- ✅ 主题持久化存储 |
|||
- ✅ 小程序兼容性优化 |
|||
|
|||
--- |
|||
|
|||
**现在你的用户可以享受个性化的主题体验了!** 🎨✨ |
@ -0,0 +1,8 @@ |
|||
/* 添加这段样式后,Primary Button 会变成绿色 */ |
|||
:root { |
|||
--nutui-color-primary: green; |
|||
--nutui-color-primary-stop1: green; |
|||
--nutui-color-primary-stop2: green; |
|||
// 间隔线/容错线,用于结构或信息分割 |
|||
--nutui-black-2: rgba(255, 0, 0, 0.08); |
|||
} |
@ -0,0 +1,95 @@ |
|||
import { useState, useEffect } from 'react' |
|||
import { gradientThemes, GradientTheme, gradientUtils } from '@/styles/gradients' |
|||
import Taro from '@tarojs/taro' |
|||
|
|||
export interface UseThemeReturn { |
|||
currentTheme: GradientTheme |
|||
setTheme: (themeName: string) => void |
|||
isAutoTheme: boolean |
|||
refreshTheme: () => void |
|||
} |
|||
|
|||
/** |
|||
* 主题管理Hook |
|||
* 提供主题切换和状态管理功能 |
|||
*/ |
|||
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] |
|||
} |
|||
} |
|||
|
|||
// 初始化主题
|
|||
useEffect(() => { |
|||
const savedTheme = Taro.getStorageSync('user_theme') || 'auto' |
|||
setIsAutoTheme(savedTheme === 'auto') |
|||
setCurrentTheme(getCurrentTheme()) |
|||
}, []) |
|||
|
|||
// 设置主题
|
|||
const setTheme = (themeName: string) => { |
|||
try { |
|||
Taro.setStorageSync('user_theme', themeName) |
|||
setIsAutoTheme(themeName === 'auto') |
|||
setCurrentTheme(getCurrentTheme()) |
|||
} catch (error) { |
|||
console.error('保存主题失败:', error) |
|||
} |
|||
} |
|||
|
|||
// 刷新主题(用于自动主题模式下用户信息变更时)
|
|||
const refreshTheme = () => { |
|||
setCurrentTheme(getCurrentTheme()) |
|||
} |
|||
|
|||
return { |
|||
currentTheme, |
|||
setTheme, |
|||
isAutoTheme, |
|||
refreshTheme |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取当前主题的样式对象 |
|||
* 用于直接应用到组件样式中 |
|||
*/ |
|||
export const useThemeStyles = () => { |
|||
const { currentTheme } = useTheme() |
|||
|
|||
return { |
|||
// 主要背景样式
|
|||
primaryBackground: { |
|||
background: currentTheme.background, |
|||
color: currentTheme.textColor |
|||
}, |
|||
|
|||
// 按钮样式
|
|||
primaryButton: { |
|||
background: currentTheme.background, |
|||
border: 'none', |
|||
color: currentTheme.textColor |
|||
}, |
|||
|
|||
// 强调色
|
|||
accentColor: currentTheme.primary, |
|||
|
|||
// 文字颜色
|
|||
textColor: currentTheme.textColor, |
|||
|
|||
// 完整主题对象
|
|||
theme: currentTheme |
|||
} |
|||
} |
@ -0,0 +1,4 @@ |
|||
export default definePageConfig({ |
|||
navigationBarTitleText: '主题设置', |
|||
navigationBarTextStyle: 'black' |
|||
}) |
@ -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 |
Loading…
Reference in new issue