forked from gxwebsoft/mp-10550
feat(theme): 实现主题切换系统并优化经销商相关页面
- 新增主题切换系统,支持智能主题和手动选择 - 更新经销商首页、团队、订单、提现等页面样式 - 添加主题相关的Hook和样式工具函数 - 优化部分组件样式以适配新主题
This commit is contained in:
191
docs/THEME_SYSTEM_GUIDE.md
Normal file
191
docs/THEME_SYSTEM_GUIDE.md
Normal file
@@ -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
|
||||||
|
- ✅ 主题持久化存储
|
||||||
|
- ✅ 小程序兼容性优化
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**现在你的用户可以享受个性化的主题体验了!** 🎨✨
|
||||||
@@ -43,7 +43,8 @@ export default defineAppConfig({
|
|||||||
"gift/index",
|
"gift/index",
|
||||||
"gift/redeem",
|
"gift/redeem",
|
||||||
"gift/detail",
|
"gift/detail",
|
||||||
"store/verification"
|
"store/verification",
|
||||||
|
"theme/index"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useState } from 'react'
|
import React, {useState} from 'react'
|
||||||
import { View, Text } from '@tarojs/components'
|
import {View, Text} from '@tarojs/components'
|
||||||
import { Button, Popup, Radio, RadioGroup, Divider } from '@nutui/nutui-react-taro'
|
import {Button, Popup, Radio, RadioGroup, Divider} from '@nutui/nutui-react-taro'
|
||||||
import { Filter, Close } from '@nutui/icons-react-taro'
|
import {Filter, Close} from '@nutui/icons-react-taro'
|
||||||
|
|
||||||
export interface CouponFilterProps {
|
export interface CouponFilterProps {
|
||||||
/** 是否显示筛选器 */
|
/** 是否显示筛选器 */
|
||||||
@@ -20,41 +20,41 @@ export interface CouponFilterProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const CouponFilter: React.FC<CouponFilterProps> = ({
|
const CouponFilter: React.FC<CouponFilterProps> = ({
|
||||||
visible,
|
visible,
|
||||||
filters,
|
filters,
|
||||||
onFiltersChange,
|
onFiltersChange,
|
||||||
onClose
|
onClose
|
||||||
}) => {
|
}) => {
|
||||||
const [tempFilters, setTempFilters] = useState(filters)
|
const [tempFilters, setTempFilters] = useState(filters)
|
||||||
|
|
||||||
// 优惠券类型选项
|
// 优惠券类型选项
|
||||||
const typeOptions = [
|
const typeOptions = [
|
||||||
{ label: '全部类型', value: '' },
|
{label: '全部类型', value: ''},
|
||||||
{ label: '满减券', value: '10' },
|
{label: '满减券', value: '10'},
|
||||||
{ label: '折扣券', value: '20' },
|
{label: '折扣券', value: '20'},
|
||||||
{ label: '免费券', value: '30' }
|
{label: '免费券', value: '30'}
|
||||||
]
|
]
|
||||||
|
|
||||||
// 最低金额选项
|
// 最低金额选项
|
||||||
const minAmountOptions = [
|
const minAmountOptions = [
|
||||||
{ label: '不限', value: '' },
|
{label: '不限', value: ''},
|
||||||
{ label: '10元以上', value: '10' },
|
{label: '10元以上', value: '10'},
|
||||||
{ label: '50元以上', value: '50' },
|
{label: '50元以上', value: '50'},
|
||||||
{ label: '100元以上', value: '100' },
|
{label: '100元以上', value: '100'},
|
||||||
{ label: '200元以上', value: '200' }
|
{label: '200元以上', value: '200'}
|
||||||
]
|
]
|
||||||
|
|
||||||
// 排序选项
|
// 排序选项
|
||||||
const sortOptions = [
|
const sortOptions = [
|
||||||
{ label: '创建时间', value: 'createTime' },
|
{label: '创建时间', value: 'createTime'},
|
||||||
{ label: '优惠金额', value: 'amount' },
|
{label: '优惠金额', value: 'amount'},
|
||||||
{ label: '到期时间', value: 'expireTime' }
|
{label: '到期时间', value: 'expireTime'}
|
||||||
]
|
]
|
||||||
|
|
||||||
// 排序方向选项
|
// 排序方向选项
|
||||||
const sortOrderOptions = [
|
const sortOrderOptions = [
|
||||||
{ label: '升序', value: 'asc' },
|
{label: '升序', value: 'asc'},
|
||||||
{ label: '降序', value: 'desc' }
|
{label: '降序', value: 'desc'}
|
||||||
]
|
]
|
||||||
|
|
||||||
// 重置筛选条件
|
// 重置筛选条件
|
||||||
@@ -86,17 +86,17 @@ const CouponFilter: React.FC<CouponFilterProps> = ({
|
|||||||
<Popup
|
<Popup
|
||||||
visible={visible}
|
visible={visible}
|
||||||
position="right"
|
position="right"
|
||||||
style={{ width: '80%', height: '100%' }}
|
style={{width: '80%', height: '100%'}}
|
||||||
>
|
>
|
||||||
<View className="h-full flex flex-col">
|
<View className="h-full flex flex-col">
|
||||||
{/* 头部 */}
|
{/* 头部 */}
|
||||||
<View className="flex items-center justify-between p-4 border-b border-gray-100">
|
<View className="flex items-center justify-between p-4 border-b border-gray-100">
|
||||||
<View className="flex items-center">
|
<View className="flex items-center">
|
||||||
<Filter size="20" className="text-gray-600 mr-2" />
|
<Filter size="20" className="text-gray-600 mr-2"/>
|
||||||
<Text className="text-lg font-semibold">筛选条件</Text>
|
<Text className="text-lg font-semibold">筛选条件</Text>
|
||||||
</View>
|
</View>
|
||||||
<View onClick={onClose}>
|
<View onClick={onClose}>
|
||||||
<Close size="20" className="text-gray-600" />
|
<Close size="20" className="text-gray-600"/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ const CouponFilter: React.FC<CouponFilterProps> = ({
|
|||||||
</Text>
|
</Text>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={tempFilters.type?.[0]?.toString() || ''}
|
value={tempFilters.type?.[0]?.toString() || ''}
|
||||||
onChange={(value) => {
|
onChange={(value: any) => {
|
||||||
updateTempFilters('type', value ? [parseInt(value)] : [])
|
updateTempFilters('type', value ? [parseInt(value)] : [])
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -121,7 +121,7 @@ const CouponFilter: React.FC<CouponFilterProps> = ({
|
|||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Divider />
|
<Divider/>
|
||||||
|
|
||||||
{/* 最低消费金额 */}
|
{/* 最低消费金额 */}
|
||||||
<View className="mb-6">
|
<View className="mb-6">
|
||||||
@@ -130,7 +130,7 @@ const CouponFilter: React.FC<CouponFilterProps> = ({
|
|||||||
</Text>
|
</Text>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={tempFilters.minAmount?.toString() || ''}
|
value={tempFilters.minAmount?.toString() || ''}
|
||||||
onChange={(value) => {
|
onChange={(value: any) => {
|
||||||
updateTempFilters('minAmount', value ? parseInt(value) : undefined)
|
updateTempFilters('minAmount', value ? parseInt(value) : undefined)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -142,7 +142,7 @@ const CouponFilter: React.FC<CouponFilterProps> = ({
|
|||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Divider />
|
<Divider/>
|
||||||
|
|
||||||
{/* 排序方式 */}
|
{/* 排序方式 */}
|
||||||
<View className="mb-6">
|
<View className="mb-6">
|
||||||
@@ -161,7 +161,7 @@ const CouponFilter: React.FC<CouponFilterProps> = ({
|
|||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Divider />
|
<Divider/>
|
||||||
|
|
||||||
{/* 排序方向 */}
|
{/* 排序方向 */}
|
||||||
<View className="mb-6">
|
<View className="mb-6">
|
||||||
|
|||||||
@@ -77,9 +77,6 @@ const CouponList: React.FC<CouponListProps> = ({
|
|||||||
scrollX
|
scrollX
|
||||||
className="flex p-4 gap-2 overflow-x-auto"
|
className="flex p-4 gap-2 overflow-x-auto"
|
||||||
showScrollbar={false}
|
showScrollbar={false}
|
||||||
style={{
|
|
||||||
WebkitOverflowScrolling: 'touch'
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{coupons.map((coupon, index) => (
|
{coupons.map((coupon, index) => (
|
||||||
<View
|
<View
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ interface FixedButtonProps {
|
|||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
icon?: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
background?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function FixedButton({text, onClick, icon, disabled}: FixedButtonProps) {
|
function FixedButton({text, onClick, icon, disabled, background}: FixedButtonProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* 底部安全区域占位 */}
|
{/* 底部安全区域占位 */}
|
||||||
@@ -17,7 +18,10 @@ function FixedButton({text, onClick, icon, disabled}: FixedButtonProps) {
|
|||||||
<View
|
<View
|
||||||
className="fixed z-50 bottom-0 left-0 right-0 bg-white border-t border-gray-200 px-4 py-3 safe-area-bottom">
|
className="fixed z-50 bottom-0 left-0 right-0 bg-white border-t border-gray-200 px-4 py-3 safe-area-bottom">
|
||||||
<Button
|
<Button
|
||||||
type="success"
|
type="primary"
|
||||||
|
style={{
|
||||||
|
background
|
||||||
|
}}
|
||||||
size="large"
|
size="large"
|
||||||
block
|
block
|
||||||
icon={icon}
|
icon={icon}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const GradientThemeSelector: React.FC<GradientThemeSelectorProps> = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View className="w-full h-full flex items-center justify-center">
|
<View className="w-full h-full flex items-center justify-center">
|
||||||
<Text className="text-white text-xs font-bold drop-shadow-sm">
|
<Text className="text-white text-xs font-bold">
|
||||||
预览
|
预览
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
8
src/dealer/index.scss
Normal file
8
src/dealer/index.scss
Normal file
@@ -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);
|
||||||
|
}
|
||||||
@@ -1,34 +1,28 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {View, Text} from '@tarojs/components'
|
import {View, Text} from '@tarojs/components'
|
||||||
import {Button, Cell, CellGroup, Tag, Grid, Avatar, Divider} from '@nutui/nutui-react-taro'
|
import {ConfigProvider, Button, Grid, Avatar} from '@nutui/nutui-react-taro'
|
||||||
import {
|
import {
|
||||||
User,
|
User,
|
||||||
Shopping,
|
Shopping,
|
||||||
Dongdong,
|
Dongdong,
|
||||||
Share,
|
|
||||||
Service,
|
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
Purse,
|
Purse,
|
||||||
People
|
People
|
||||||
} from '@nutui/icons-react-taro'
|
} from '@nutui/icons-react-taro'
|
||||||
import {useDealerUser} from '@/hooks/useDealerUser'
|
import {useDealerUser} from '@/hooks/useDealerUser'
|
||||||
import { gradientUtils, businessGradients, cardGradients, textGradients } from '@/styles/gradients'
|
import { useThemeStyles } from '@/hooks/useTheme'
|
||||||
|
import {businessGradients, cardGradients, gradientUtils} from '@/styles/gradients'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
|
|
||||||
const DealerIndex: React.FC = () => {
|
const DealerIndex: React.FC = () => {
|
||||||
const {
|
const {
|
||||||
dealerUser,
|
dealerUser,
|
||||||
loading,
|
|
||||||
error,
|
error,
|
||||||
refresh,
|
refresh,
|
||||||
} = useDealerUser()
|
} = useDealerUser()
|
||||||
|
|
||||||
// 跳转到申请页面
|
// 使用主题样式
|
||||||
const navigateToApply = () => {
|
const themeStyles = useThemeStyles()
|
||||||
Taro.navigateTo({
|
|
||||||
url: '/dealer/apply/add'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 导航到各个功能页面
|
// 导航到各个功能页面
|
||||||
const navigateToPage = (url: string) => {
|
const navigateToPage = (url: string) => {
|
||||||
@@ -59,6 +53,8 @@ const DealerIndex: React.FC = () => {
|
|||||||
return userTheme.background
|
return userTheme.background
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(getGradientBackground(),'getGradientBackground()')
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<View className="p-4">
|
<View className="p-4">
|
||||||
@@ -77,46 +73,45 @@ const DealerIndex: React.FC = () => {
|
|||||||
<View>
|
<View>
|
||||||
{/*头部信息*/}
|
{/*头部信息*/}
|
||||||
{dealerUser && (
|
{dealerUser && (
|
||||||
<View className="px-4 py-6 relative overflow-hidden" style={{
|
<View className="px-4 py-6 relative overflow-hidden" style={themeStyles.primaryBackground}>
|
||||||
background: getGradientBackground(dealerUser?.themeColor)
|
{/* 装饰性背景元素 - 小程序兼容版本 */}
|
||||||
}}>
|
<View className="absolute w-32 h-32 rounded-full" style={{
|
||||||
{/* 装饰性背景元素 */}
|
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||||
<View className="absolute top-0 right-0 w-32 h-32 rounded-full opacity-10" style={{
|
top: '-16px',
|
||||||
background: 'radial-gradient(circle, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0.1) 100%)',
|
right: '-16px'
|
||||||
transform: 'translate(50%, -50%)'
|
|
||||||
}}></View>
|
}}></View>
|
||||||
<View className="absolute bottom-0 left-0 w-24 h-24 rounded-full opacity-10" style={{
|
<View className="absolute w-24 h-24 rounded-full" style={{
|
||||||
background: 'radial-gradient(circle, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0.05) 100%)',
|
backgroundColor: 'rgba(255, 255, 255, 0.08)',
|
||||||
transform: 'translate(-50%, 50%)'
|
bottom: '-12px',
|
||||||
|
left: '-12px'
|
||||||
}}></View>
|
}}></View>
|
||||||
<View className="absolute top-1/2 left-1/2 w-16 h-16 rounded-full opacity-5" style={{
|
<View className="absolute w-16 h-16 rounded-full" style={{
|
||||||
background: 'radial-gradient(circle, rgba(255, 255, 255, 0.4) 0%, transparent 70%)',
|
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
||||||
transform: 'translate(-50%, -50%)'
|
top: '60px',
|
||||||
|
left: '120px'
|
||||||
}}></View>
|
}}></View>
|
||||||
<View className="flex items-center justify-between relative z-10">
|
<View className="flex items-center justify-between relative z-10 mb-4">
|
||||||
<Avatar
|
<Avatar
|
||||||
size="50"
|
size="50"
|
||||||
src={dealerUser?.qrcode}
|
src={dealerUser?.qrcode}
|
||||||
icon={<User/>}
|
icon={<User/>}
|
||||||
className="mr-4"
|
className="mr-4"
|
||||||
style={{
|
style={{
|
||||||
border: '2px solid rgba(255, 255, 255, 0.3)',
|
border: '2px solid rgba(255, 255, 255, 0.3)'
|
||||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)'
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<View className="flex-1">
|
<View className="flex-1 flex-col">
|
||||||
<Text className="text-white text-lg font-bold mb-1" style={{
|
<View className="text-white text-lg font-bold mb-1" style={{
|
||||||
textShadow: '0 1px 2px rgba(0, 0, 0, 0.1)'
|
|
||||||
}}>
|
}}>
|
||||||
{dealerUser?.realName || '分销商'}
|
{dealerUser?.realName || '分销商'}
|
||||||
</Text>
|
</View>
|
||||||
<Text className="text-sm" style={{
|
<View className="text-sm" style={{
|
||||||
color: 'rgba(255, 255, 255, 0.8)'
|
color: 'rgba(255, 255, 255, 0.8)'
|
||||||
}}>
|
}}>
|
||||||
ID: {dealerUser.userId} | 推荐人: {dealerUser.refereeId || '无'}
|
ID: {dealerUser.userId} | 推荐人: {dealerUser.refereeId || '无'}
|
||||||
</Text>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View className="text-right">
|
<View className="text-right hidden">
|
||||||
<Text className="text-xs" style={{
|
<Text className="text-xs" style={{
|
||||||
color: 'rgba(255, 255, 255, 0.9)'
|
color: 'rgba(255, 255, 255, 0.9)'
|
||||||
}}>加入时间</Text>
|
}}>加入时间</Text>
|
||||||
@@ -133,34 +128,33 @@ const DealerIndex: React.FC = () => {
|
|||||||
{/* 佣金统计卡片 */}
|
{/* 佣金统计卡片 */}
|
||||||
{dealerUser && (
|
{dealerUser && (
|
||||||
<View className="mx-4 -mt-6 rounded-xl p-4 relative z-10" style={cardGradients.elevated}>
|
<View className="mx-4 -mt-6 rounded-xl p-4 relative z-10" style={cardGradients.elevated}>
|
||||||
<Text className="font-semibold mb-4 text-gray-800">佣金统计</Text>
|
<View className="mb-4">
|
||||||
|
<Text className="font-semibold text-gray-800">佣金统计</Text>
|
||||||
|
</View>
|
||||||
<View className="grid grid-cols-3 gap-4">
|
<View className="grid grid-cols-3 gap-4">
|
||||||
<View className="text-center p-3 rounded-lg" style={{
|
<View className="text-center p-3 rounded-lg" style={{
|
||||||
background: businessGradients.money.available,
|
background: businessGradients.money.available
|
||||||
backgroundImage: 'linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%)'
|
|
||||||
}}>
|
}}>
|
||||||
<Text className="text-2xl font-bold mb-1 text-white drop-shadow-sm">
|
<Text className="text-2xl font-bold mb-1 text-white">
|
||||||
¥{formatMoney(dealerUser.money)}
|
¥{formatMoney(dealerUser.money)}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className="text-xs text-white text-opacity-90">可提现</Text>
|
<Text className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.9)' }}>可提现</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="text-center p-3 rounded-lg" style={{
|
<View className="text-center p-3 rounded-lg" style={{
|
||||||
background: businessGradients.money.frozen,
|
background: businessGradients.money.frozen
|
||||||
backgroundImage: 'linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%)'
|
|
||||||
}}>
|
}}>
|
||||||
<Text className="text-2xl font-bold mb-1 text-white drop-shadow-sm">
|
<Text className="text-2xl font-bold mb-1 text-white">
|
||||||
¥{formatMoney(dealerUser.freezeMoney)}
|
¥{formatMoney(dealerUser.freezeMoney)}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className="text-xs text-white text-opacity-90">冻结中</Text>
|
<Text className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.9)' }}>冻结中</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="text-center p-3 rounded-lg" style={{
|
<View className="text-center p-3 rounded-lg" style={{
|
||||||
background: businessGradients.money.total,
|
background: businessGradients.money.total
|
||||||
backgroundImage: 'linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%)'
|
|
||||||
}}>
|
}}>
|
||||||
<Text className="text-2xl font-bold mb-1 text-white drop-shadow-sm">
|
<Text className="text-2xl font-bold mb-1 text-white">
|
||||||
¥{formatMoney(dealerUser.totalMoney)}
|
¥{formatMoney(dealerUser.totalMoney)}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className="text-xs text-white text-opacity-90">累计收益</Text>
|
<Text className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.9)' }}>累计收益</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -171,27 +165,28 @@ const DealerIndex: React.FC = () => {
|
|||||||
<View className="bg-white mx-4 mt-4 rounded-xl p-4">
|
<View className="bg-white mx-4 mt-4 rounded-xl p-4">
|
||||||
<View className="flex items-center justify-between mb-4">
|
<View className="flex items-center justify-between mb-4">
|
||||||
<Text className="font-semibold text-gray-800">我的团队</Text>
|
<Text className="font-semibold text-gray-800">我的团队</Text>
|
||||||
<Text
|
<View
|
||||||
className="text-blue-500 text-sm"
|
className="text-gray-400 text-sm flex items-center"
|
||||||
onClick={() => navigateToPage('/dealer/team/index')}
|
onClick={() => navigateToPage('/dealer/team/index')}
|
||||||
>
|
>
|
||||||
查看详情 <ArrowRight size="12"/>
|
<Text>查看详情</Text>
|
||||||
</Text>
|
<ArrowRight size="12"/>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View className="grid grid-cols-3 gap-4">
|
<View className="grid grid-cols-3 gap-4">
|
||||||
<View className="text-center">
|
<View className="text-center grid">
|
||||||
<Text className="text-xl font-bold text-purple-500 mb-1">
|
<Text className="text-xl font-bold text-purple-500 mb-1">
|
||||||
{dealerUser.firstNum || 0}
|
{dealerUser.firstNum || 0}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className="text-xs text-gray-500">一级成员</Text>
|
<Text className="text-xs text-gray-500">一级成员</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="text-center">
|
<View className="text-center grid">
|
||||||
<Text className="text-xl font-bold text-indigo-500 mb-1">
|
<Text className="text-xl font-bold text-indigo-500 mb-1">
|
||||||
{dealerUser.secondNum || 0}
|
{dealerUser.secondNum || 0}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className="text-xs text-gray-500">二级成员</Text>
|
<Text className="text-xs text-gray-500">二级成员</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="text-center">
|
<View className="text-center grid">
|
||||||
<Text className="text-xl font-bold text-pink-500 mb-1">
|
<Text className="text-xl font-bold text-pink-500 mb-1">
|
||||||
{dealerUser.thirdNum || 0}
|
{dealerUser.thirdNum || 0}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -203,44 +198,45 @@ const DealerIndex: React.FC = () => {
|
|||||||
|
|
||||||
{/* 功能导航 */}
|
{/* 功能导航 */}
|
||||||
<View className="bg-white mx-4 mt-4 rounded-xl p-4">
|
<View className="bg-white mx-4 mt-4 rounded-xl p-4">
|
||||||
<Text className="font-semibold mb-4 text-gray-800">分销工具</Text>
|
<View className="font-semibold mb-4 text-gray-800">分销工具</View>
|
||||||
<Grid columns={4} gap={16}>
|
<ConfigProvider>
|
||||||
<Grid.Item onClick={() => navigateToPage('/dealer/orders/index')}>
|
<Grid columns={4}>
|
||||||
<View className="text-center">
|
<Grid.Item text="分销订单" onClick={() => navigateToPage('/dealer/orders/index')}>
|
||||||
<View className="w-12 h-12 bg-blue-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
<View className="text-center">
|
||||||
<Shopping color="#3b82f6" size="20"/>
|
<View className="w-12 h-12 bg-blue-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
||||||
|
<Shopping color="#3b82f6" size="20"/>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Text className="text-xs text-gray-600">分销订单</Text>
|
</Grid.Item>
|
||||||
</View>
|
|
||||||
</Grid.Item>
|
|
||||||
|
|
||||||
<Grid.Item onClick={() => navigateToPage('/dealer/withdraw/index')}>
|
<Grid.Item onClick={() => navigateToPage('/dealer/withdraw/index')}>
|
||||||
<View className="text-center">
|
<View className="text-center">
|
||||||
<View className="w-12 h-12 bg-green-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
<View className="w-12 h-12 bg-green-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
||||||
<Purse color="#10b981" size="20"/>
|
<Purse color="#10b981" size="20"/>
|
||||||
|
</View>
|
||||||
|
<Text className="text-xs text-gray-600">提现申请</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text className="text-xs text-gray-600">提现申请</Text>
|
</Grid.Item>
|
||||||
</View>
|
|
||||||
</Grid.Item>
|
|
||||||
|
|
||||||
<Grid.Item onClick={() => navigateToPage('/dealer/team/index')}>
|
<Grid.Item onClick={() => navigateToPage('/dealer/team/index')}>
|
||||||
<View className="text-center">
|
<View className="text-center">
|
||||||
<View className="w-12 h-12 bg-purple-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
<View className="w-12 h-12 bg-purple-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
||||||
<People color="#8b5cf6" size="20"/>
|
<People color="#8b5cf6" size="20"/>
|
||||||
|
</View>
|
||||||
|
<Text className="text-xs text-gray-600">我的团队</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text className="text-xs text-gray-600">我的团队</Text>
|
</Grid.Item>
|
||||||
</View>
|
|
||||||
</Grid.Item>
|
|
||||||
|
|
||||||
<Grid.Item onClick={() => navigateToPage('/dealer/qrcode/index')}>
|
<Grid.Item onClick={() => navigateToPage('/dealer/qrcode/index')}>
|
||||||
<View className="text-center">
|
<View className="text-center">
|
||||||
<View className="w-12 h-12 bg-orange-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
<View className="w-12 h-12 bg-orange-50 rounded-xl flex items-center justify-center mx-auto mb-2">
|
||||||
<Dongdong color="#f59e0b" size="20"/>
|
<Dongdong color="#f59e0b" size="20"/>
|
||||||
|
</View>
|
||||||
|
<Text className="text-xs text-gray-600">推广二维码</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text className="text-xs text-gray-600">推广二维码</Text>
|
</Grid.Item>
|
||||||
</View>
|
</Grid>
|
||||||
</Grid.Item>
|
</ConfigProvider>
|
||||||
</Grid>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ const DealerInfo: React.FC = () => {
|
|||||||
</CellGroup>
|
</CellGroup>
|
||||||
|
|
||||||
{/* 操作按钮 */}
|
{/* 操作按钮 */}
|
||||||
<View className="mt-6 space-y-3">
|
<View className="mt-6 gap-2">
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
size="large"
|
size="large"
|
||||||
@@ -104,7 +104,7 @@ const DealerInfo: React.FC = () => {
|
|||||||
{/* 经销商权益 */}
|
{/* 经销商权益 */}
|
||||||
<View className="bg-white mx-4 mt-4 rounded-lg p-4">
|
<View className="bg-white mx-4 mt-4 rounded-lg p-4">
|
||||||
<Text className="font-semibold mb-3">经销商权益</Text>
|
<Text className="font-semibold mb-3">经销商权益</Text>
|
||||||
<View className="space-y-2">
|
<View className="gap-2">
|
||||||
<Text className="text-sm text-gray-600">
|
<Text className="text-sm text-gray-600">
|
||||||
• 享受经销商专属价格
|
• 享受经销商专属价格
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { View, Text } from '@tarojs/components'
|
import { View, Text } from '@tarojs/components'
|
||||||
import { Cell, Empty, Tabs, Tag, Button, PullToRefresh } from '@nutui/nutui-react-taro'
|
import { Empty, Tabs, Tag, PullToRefresh } from '@nutui/nutui-react-taro'
|
||||||
|
|
||||||
const DealerOrders: React.FC = () => {
|
const DealerOrders: React.FC = () => {
|
||||||
const [activeTab, setActiveTab] = useState('0')
|
const [activeTab, setActiveTab] = useState<string>('0')
|
||||||
const [refreshing, setRefreshing] = useState(false)
|
const [refreshing, setRefreshing] = useState<boolean>(false)
|
||||||
|
|
||||||
// 模拟订单数据
|
// 模拟订单数据
|
||||||
const mockOrders = [
|
const mockOrders = [
|
||||||
@@ -65,7 +65,7 @@ const DealerOrders: React.FC = () => {
|
|||||||
客户:{order.customerName}
|
客户:{order.customerName}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Tag type={getStatusColor(order.status)} size="small">
|
<Tag type={getStatusColor(order.status)}>
|
||||||
{getStatusText(order.status)}
|
{getStatusText(order.status)}
|
||||||
</Tag>
|
</Tag>
|
||||||
</View>
|
</View>
|
||||||
@@ -107,9 +107,10 @@ const DealerOrders: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 订单列表 */}
|
{/* 订单列表 */}
|
||||||
<Tabs value={activeTab} onChange={setActiveTab}>
|
<Tabs value={activeTab} onChange={() => setActiveTab}>
|
||||||
<Tabs.TabPane title="全部" value="0">
|
<Tabs.TabPane title="全部" value="0">
|
||||||
<PullToRefresh
|
<PullToRefresh
|
||||||
|
// @ts-ignore
|
||||||
loading={refreshing}
|
loading={refreshing}
|
||||||
onRefresh={handleRefresh}
|
onRefresh={handleRefresh}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { View, Text } from '@tarojs/components'
|
import { View, Text } from '@tarojs/components'
|
||||||
import { Empty, Tabs, Avatar, Tag, Cell, Progress } from '@nutui/nutui-react-taro'
|
import { Empty, Tabs, Avatar, Tag, Progress } from '@nutui/nutui-react-taro'
|
||||||
import { User, Crown, Star } from '@nutui/icons-react-taro'
|
import { User, Star, StarFill } from '@nutui/icons-react-taro'
|
||||||
import { businessGradients, cardGradients } from '@/styles/gradients'
|
|
||||||
|
|
||||||
const DealerTeam: React.FC = () => {
|
const DealerTeam: React.FC = () => {
|
||||||
const [activeTab, setActiveTab] = useState('0')
|
const [activeTab, setActiveTab] = useState('0')
|
||||||
@@ -63,7 +62,7 @@ const DealerTeam: React.FC = () => {
|
|||||||
|
|
||||||
const getLevelIcon = (level: number) => {
|
const getLevelIcon = (level: number) => {
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case 1: return <Crown color={getLevelColor(level)} size="16" />
|
case 1: return <StarFill color={getLevelColor(level)} size="16" />
|
||||||
case 2: return <Star color={getLevelColor(level)} size="16" />
|
case 2: return <Star color={getLevelColor(level)} size="16" />
|
||||||
case 3: return <User color={getLevelColor(level)} size="16" />
|
case 3: return <User color={getLevelColor(level)} size="16" />
|
||||||
default: return <User color={getLevelColor(level)} size="16" />
|
default: return <User color={getLevelColor(level)} size="16" />
|
||||||
@@ -96,7 +95,6 @@ const DealerTeam: React.FC = () => {
|
|||||||
<View className="text-right">
|
<View className="text-right">
|
||||||
<Tag
|
<Tag
|
||||||
type={member.status === 'active' ? 'success' : 'default'}
|
type={member.status === 'active' ? 'success' : 'default'}
|
||||||
size="small"
|
|
||||||
>
|
>
|
||||||
{member.status === 'active' ? '活跃' : '沉默'}
|
{member.status === 'active' ? '活跃' : '沉默'}
|
||||||
</Tag>
|
</Tag>
|
||||||
@@ -132,26 +130,28 @@ const DealerTeam: React.FC = () => {
|
|||||||
<View className="rounded-xl p-6 mb-6 text-white relative overflow-hidden" style={{
|
<View className="rounded-xl p-6 mb-6 text-white relative overflow-hidden" style={{
|
||||||
background: 'linear-gradient(135deg, #8b5cf6 0%, #ec4899 100%)'
|
background: 'linear-gradient(135deg, #8b5cf6 0%, #ec4899 100%)'
|
||||||
}}>
|
}}>
|
||||||
{/* 装饰背景 */}
|
{/* 装饰背景 - 小程序兼容版本 */}
|
||||||
<View className="absolute top-0 right-0 w-32 h-32 rounded-full opacity-10" style={{
|
<View className="absolute w-32 h-32 rounded-full" style={{
|
||||||
background: 'radial-gradient(circle, rgba(255, 255, 255, 0.3) 0%, transparent 70%)',
|
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||||
transform: 'translate(50%, -50%)'
|
top: '-16px',
|
||||||
|
right: '-16px'
|
||||||
}}></View>
|
}}></View>
|
||||||
<View className="absolute bottom-0 left-0 w-20 h-20 rounded-full opacity-5" style={{
|
<View className="absolute w-20 h-20 rounded-full" style={{
|
||||||
background: 'radial-gradient(circle, rgba(255, 255, 255, 0.4) 0%, transparent 70%)',
|
backgroundColor: 'rgba(255, 255, 255, 0.05)',
|
||||||
transform: 'translate(-50%, 50%)'
|
bottom: '-10px',
|
||||||
|
left: '-10px'
|
||||||
}}></View>
|
}}></View>
|
||||||
|
|
||||||
<View className="relative z-10">
|
<View className="relative z-10">
|
||||||
<Text className="text-lg font-bold mb-4 text-white drop-shadow-sm">团队总览</Text>
|
<Text className="text-lg font-bold mb-4 text-white">团队总览</Text>
|
||||||
<View className="grid grid-cols-2 gap-4">
|
<View className="grid grid-cols-2 gap-4">
|
||||||
<View>
|
<View>
|
||||||
<Text className="text-2xl font-bold mb-1 text-white drop-shadow-sm">{teamStats.total}</Text>
|
<Text className="text-2xl font-bold mb-1 text-white">{teamStats.total}</Text>
|
||||||
<Text className="text-white text-opacity-80 text-sm">团队总人数</Text>
|
<Text className="text-sm" style={{ color: 'rgba(255, 255, 255, 0.8)' }}>团队总人数</Text>
|
||||||
</View>
|
</View>
|
||||||
<View>
|
<View>
|
||||||
<Text className="text-2xl font-bold mb-1 text-white drop-shadow-sm">¥{teamStats.monthlyCommission}</Text>
|
<Text className="text-2xl font-bold mb-1 text-white">¥{teamStats.monthlyCommission}</Text>
|
||||||
<Text className="text-white text-opacity-80 text-sm">本月团队佣金</Text>
|
<Text className="text-sm" style={{ color: 'rgba(255, 255, 255, 0.8)' }}>本月团队佣金</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -160,18 +160,18 @@ const DealerTeam: React.FC = () => {
|
|||||||
{/* 层级分布 */}
|
{/* 层级分布 */}
|
||||||
<View className="bg-white rounded-xl p-4 mb-4">
|
<View className="bg-white rounded-xl p-4 mb-4">
|
||||||
<Text className="font-semibold mb-4 text-gray-800">层级分布</Text>
|
<Text className="font-semibold mb-4 text-gray-800">层级分布</Text>
|
||||||
<View className="space-y-3">
|
<View className="gap-2">
|
||||||
<View className="flex items-center justify-between">
|
<View className="flex items-center justify-between">
|
||||||
<View className="flex items-center">
|
<View className="flex items-center">
|
||||||
<Crown color="#f59e0b" size="16" className="mr-2" />
|
<StarFill color="#f59e0b" size="16" className="mr-2" />
|
||||||
<Text className="text-sm">一级成员</Text>
|
<Text className="text-sm">一级成员</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="flex items-center">
|
<View className="flex items-center">
|
||||||
<Text className="text-sm font-semibold mr-2">{teamStats.firstLevel}</Text>
|
<Text className="text-sm font-semibold mr-2">{teamStats.firstLevel}</Text>
|
||||||
<Progress
|
<Progress
|
||||||
percentage={(teamStats.firstLevel / teamStats.total) * 100}
|
percent={(teamStats.firstLevel / teamStats.total) * 100}
|
||||||
strokeWidth="6"
|
strokeWidth="6"
|
||||||
strokeColor="#f59e0b"
|
background="#f59e0b"
|
||||||
className="w-20"
|
className="w-20"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -185,9 +185,9 @@ const DealerTeam: React.FC = () => {
|
|||||||
<View className="flex items-center">
|
<View className="flex items-center">
|
||||||
<Text className="text-sm font-semibold mr-2">{teamStats.secondLevel}</Text>
|
<Text className="text-sm font-semibold mr-2">{teamStats.secondLevel}</Text>
|
||||||
<Progress
|
<Progress
|
||||||
percentage={(teamStats.secondLevel / teamStats.total) * 100}
|
percent={(teamStats.secondLevel / teamStats.total) * 100}
|
||||||
strokeWidth="6"
|
strokeWidth="6"
|
||||||
strokeColor="#8b5cf6"
|
background="#8b5cf6"
|
||||||
className="w-20"
|
className="w-20"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -201,9 +201,9 @@ const DealerTeam: React.FC = () => {
|
|||||||
<View className="flex items-center">
|
<View className="flex items-center">
|
||||||
<Text className="text-sm font-semibold mr-2">{teamStats.thirdLevel}</Text>
|
<Text className="text-sm font-semibold mr-2">{teamStats.thirdLevel}</Text>
|
||||||
<Progress
|
<Progress
|
||||||
percentage={(teamStats.thirdLevel / teamStats.total) * 100}
|
percent={(teamStats.thirdLevel / teamStats.total) * 100}
|
||||||
strokeWidth="6"
|
strokeWidth="6"
|
||||||
strokeColor="#ec4899"
|
background="#ec4899"
|
||||||
className="w-20"
|
className="w-20"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -233,7 +233,7 @@ const DealerTeam: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="bg-gray-50 min-h-screen">
|
<View className="bg-gray-50 min-h-screen">
|
||||||
<Tabs value={activeTab} onChange={setActiveTab}>
|
<Tabs value={activeTab} onChange={() => setActiveTab}>
|
||||||
<Tabs.TabPane title="团队总览" value="0">
|
<Tabs.TabPane title="团队总览" value="0">
|
||||||
{renderOverview()}
|
{renderOverview()}
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import {
|
|||||||
Tag,
|
Tag,
|
||||||
Empty
|
Empty
|
||||||
} from '@nutui/nutui-react-taro'
|
} from '@nutui/nutui-react-taro'
|
||||||
import { Money, ArrowRight } from '@nutui/icons-react-taro'
|
import { Wallet } from '@nutui/icons-react-taro'
|
||||||
import { businessGradients, cardGradients } from '@/styles/gradients'
|
import { businessGradients } from '@/styles/gradients'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
|
|
||||||
const DealerWithdraw: React.FC = () => {
|
const DealerWithdraw: React.FC = () => {
|
||||||
@@ -84,21 +84,22 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
<View className="rounded-xl p-6 mb-6 text-white relative overflow-hidden" style={{
|
<View className="rounded-xl p-6 mb-6 text-white relative overflow-hidden" style={{
|
||||||
background: businessGradients.dealer.header
|
background: businessGradients.dealer.header
|
||||||
}}>
|
}}>
|
||||||
{/* 装饰背景 */}
|
{/* 装饰背景 - 小程序兼容版本 */}
|
||||||
<View className="absolute top-0 right-0 w-24 h-24 rounded-full opacity-10" style={{
|
<View className="absolute top-0 right-0 w-24 h-24 rounded-full" style={{
|
||||||
background: 'radial-gradient(circle, rgba(255, 255, 255, 0.3) 0%, transparent 70%)',
|
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||||
transform: 'translate(50%, -50%)'
|
right: '-12px',
|
||||||
|
top: '-12px'
|
||||||
}}></View>
|
}}></View>
|
||||||
|
|
||||||
<View className="flex items-center justify-between relative z-10">
|
<View className="flex items-center justify-between relative z-10">
|
||||||
<View>
|
<View>
|
||||||
<Text className="text-white text-opacity-80 text-sm mb-1">可提现余额</Text>
|
<Text className="text-white text-opacity-80 text-sm mb-1">可提现余额</Text>
|
||||||
<Text className="text-2xl font-bold text-white drop-shadow-sm">¥{availableAmount}</Text>
|
<Text className="text-2xl font-bold text-white">¥{availableAmount}</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className="p-3 rounded-full" style={{
|
<View className="p-3 rounded-full" style={{
|
||||||
background: 'rgba(255, 255, 255, 0.2)'
|
background: 'rgba(255, 255, 255, 0.2)'
|
||||||
}}>
|
}}>
|
||||||
<Money color="white" size="32" />
|
<Wallet color="white" size="32" />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<View className="mt-4 pt-4 relative z-10" style={{
|
<View className="mt-4 pt-4 relative z-10" style={{
|
||||||
@@ -149,7 +150,7 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
<Form.Item name="accountType" label="提现方式" required>
|
<Form.Item name="accountType" label="提现方式" required>
|
||||||
<Radio.Group value={selectedAccount} onChange={setSelectedAccount}>
|
<Radio.Group value={selectedAccount} onChange={() => setSelectedAccount}>
|
||||||
<Cell.Group>
|
<Cell.Group>
|
||||||
<Cell>
|
<Cell>
|
||||||
<Radio value="wechat">微信钱包</Radio>
|
<Radio value="wechat">微信钱包</Radio>
|
||||||
@@ -200,7 +201,7 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
提现账户:{record.account}
|
提现账户:{record.account}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Tag type={getStatusColor(record.status)} size="small">
|
<Tag type={getStatusColor(record.status)}>
|
||||||
{getStatusText(record.status)}
|
{getStatusText(record.status)}
|
||||||
</Tag>
|
</Tag>
|
||||||
</View>
|
</View>
|
||||||
@@ -223,7 +224,7 @@ const DealerWithdraw: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View className="bg-gray-50 min-h-screen">
|
<View className="bg-gray-50 min-h-screen">
|
||||||
<Tabs value={activeTab} onChange={setActiveTab}>
|
<Tabs value={activeTab} onChange={() => setActiveTab}>
|
||||||
<Tabs.TabPane title="申请提现" value="0">
|
<Tabs.TabPane title="申请提现" value="0">
|
||||||
{renderWithdrawForm()}
|
{renderWithdrawForm()}
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
|
|||||||
95
src/hooks/useTheme.ts
Normal file
95
src/hooks/useTheme.ts
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
Divider,
|
Divider,
|
||||||
ConfigProvider
|
ConfigProvider
|
||||||
} from '@nutui/nutui-react-taro';
|
} from '@nutui/nutui-react-taro';
|
||||||
import {ArrowLeft, Del, Shopping} from '@nutui/icons-react-taro';
|
import {ArrowLeft, Del} from '@nutui/icons-react-taro';
|
||||||
import {View} from '@tarojs/components';
|
import {View} from '@tarojs/components';
|
||||||
import {CartItem, useCart} from "@/hooks/useCart";
|
import {CartItem, useCart} from "@/hooks/useCart";
|
||||||
import './cart.scss';
|
import './cart.scss';
|
||||||
@@ -48,11 +48,11 @@ function Cart() {
|
|||||||
useShareAppMessage(() => {
|
useShareAppMessage(() => {
|
||||||
return {
|
return {
|
||||||
title: '购物车 - 网宿小店',
|
title: '购物车 - 网宿小店',
|
||||||
success: function (res) {
|
success: function () {
|
||||||
console.log('分享成功', res);
|
console.log('分享成功');
|
||||||
},
|
},
|
||||||
fail: function (res) {
|
fail: function () {
|
||||||
console.log('分享失败', res);
|
console.log('分享失败');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@@ -203,15 +203,25 @@ function Cart() {
|
|||||||
>
|
>
|
||||||
<span className="text-lg">购物车({cartCount})</span>
|
<span className="text-lg">购物车({cartCount})</span>
|
||||||
</NavBar>
|
</NavBar>
|
||||||
<Empty
|
|
||||||
description="购物车空空如也"
|
{/* 垂直居中的空状态容器 */}
|
||||||
actions={[{ text: '去逛逛' }]}
|
<View
|
||||||
|
className="flex items-center justify-center"
|
||||||
style={{
|
style={{
|
||||||
marginTop: `${statusBarHeight + 50}px`,
|
height: `calc(100vh - ${statusBarHeight + 150}px)`,
|
||||||
|
paddingTop: `${statusBarHeight + 50}px`,
|
||||||
backgroundColor: 'transparent'
|
backgroundColor: 'transparent'
|
||||||
}}
|
}}
|
||||||
onClick={() => Taro.switchTab({ url: '/pages/index/index' })}
|
>
|
||||||
/>
|
<Empty
|
||||||
|
description="购物车空空如也"
|
||||||
|
actions={[{ text: '去逛逛' }]}
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'transparent'
|
||||||
|
}}
|
||||||
|
onClick={() => Taro.switchTab({ url: '/pages/index/index' })}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -251,33 +261,8 @@ function Cart() {
|
|||||||
</NavBar>
|
</NavBar>
|
||||||
|
|
||||||
{/* 购物车内容 */}
|
{/* 购物车内容 */}
|
||||||
<View
|
<View className="pt-24">
|
||||||
className="pt-24"
|
{/* 商品列表 */}
|
||||||
style={{ backgroundColor: cartItems.length === 0 ? 'transparent' : undefined }}
|
|
||||||
>
|
|
||||||
{cartItems.length === 0 ? (
|
|
||||||
// 空购物车
|
|
||||||
<View
|
|
||||||
className="cart-empty-container flex flex-col items-center justify-center h-96"
|
|
||||||
style={{ backgroundColor: 'transparent' }}
|
|
||||||
>
|
|
||||||
<Empty
|
|
||||||
image={<Shopping size={80}/>}
|
|
||||||
description="购物车空空如也"
|
|
||||||
style={{ backgroundColor: 'transparent' }}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
type="primary"
|
|
||||||
size="small"
|
|
||||||
onClick={() => Taro.switchTab({url: '/pages/index/index'})}
|
|
||||||
>
|
|
||||||
去逛逛
|
|
||||||
</Button>
|
|
||||||
</Empty>
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{/* 商品列表 */}
|
|
||||||
<View className="bg-white">
|
<View className="bg-white">
|
||||||
{cartItems.map((item: CartItem, index: number) => (
|
{cartItems.map((item: CartItem, index: number) => (
|
||||||
<View key={item.goodsId}>
|
<View key={item.goodsId}>
|
||||||
@@ -361,10 +346,8 @@ function Cart() {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 底部安全区域占位 */}
|
{/* 底部安全区域占位 */}
|
||||||
<View className="h-20"></View>
|
<View className="h-20"></View>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -27,12 +27,13 @@ const UserCell = () => {
|
|||||||
backgroundImage: 'linear-gradient(to right bottom, #e53e3e, #c53030)',
|
backgroundImage: 'linear-gradient(to right bottom, #e53e3e, #c53030)',
|
||||||
}}
|
}}
|
||||||
title={
|
title={
|
||||||
<View style={{display: 'inline-flex', alignItems: 'center'}} onClick={() => navTo('/admin/index', true)}>
|
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
<Setting className={'text-white '} size={16}/>
|
<Setting className={'text-white '} size={16}/>
|
||||||
<Text style={{fontSize: '16px'}} className={'pl-3 text-white font-medium'}>管理中心</Text>
|
<Text style={{fontSize: '16px'}} className={'pl-3 text-white font-medium'}>管理中心</Text>
|
||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
extra={<ArrowRight color="#ffffff" size={18}/>}
|
extra={<ArrowRight color="#ffffff" size={18}/>}
|
||||||
|
onClick={() => navTo('/admin/index', true)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
@@ -52,7 +53,7 @@ const UserCell = () => {
|
|||||||
backgroundImage: 'linear-gradient(to right bottom, #54a799, #177b73)',
|
backgroundImage: 'linear-gradient(to right bottom, #54a799, #177b73)',
|
||||||
}}
|
}}
|
||||||
title={
|
title={
|
||||||
<View style={{display: 'inline-flex', alignItems: 'center'}} onClick={() => navTo('/dealer/index', true)}>
|
<View style={{display: 'inline-flex', alignItems: 'center'}}>
|
||||||
<Reward className={'text-orange-100 '} size={16}/>
|
<Reward className={'text-orange-100 '} size={16}/>
|
||||||
<Text style={{fontSize: '16px'}}
|
<Text style={{fontSize: '16px'}}
|
||||||
className={'pl-3 text-orange-100 font-medium'}>分销中心</Text>
|
className={'pl-3 text-orange-100 font-medium'}>分销中心</Text>
|
||||||
@@ -60,6 +61,7 @@ const UserCell = () => {
|
|||||||
</View>
|
</View>
|
||||||
}
|
}
|
||||||
extra={<ArrowRight color="#cccccc" size={18}/>}
|
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||||
|
onClick={() => navTo('/dealer/index', true)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -122,6 +122,13 @@ const UserCell = () => {
|
|||||||
extra={<ArrowRight color="#cccccc" size={18}/>}
|
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||||
onClick={() => navTo('/user/profile/profile', true)}
|
onClick={() => navTo('/user/profile/profile', true)}
|
||||||
/>
|
/>
|
||||||
|
<Cell
|
||||||
|
className="nutui-cell-clickable"
|
||||||
|
title="切换主题"
|
||||||
|
align="center"
|
||||||
|
extra={<ArrowRight color="#cccccc" size={18}/>}
|
||||||
|
onClick={() => navTo('/user/theme/index', true)}
|
||||||
|
/>
|
||||||
<Cell
|
<Cell
|
||||||
className="nutui-cell-clickable"
|
className="nutui-cell-clickable"
|
||||||
title="退出登录"
|
title="退出登录"
|
||||||
|
|||||||
@@ -582,9 +582,7 @@ const OrderConfirm = () => {
|
|||||||
|
|
||||||
<Gap height={50}/>
|
<Gap height={50}/>
|
||||||
|
|
||||||
<div className={'fixed z-50 bg-white w-full bottom-0 left-0 pt-4 pb-10'} style={{
|
<div className={'fixed z-50 bg-white w-full bottom-0 left-0 pt-4 pb-10 border-t border-gray-200'}>
|
||||||
boxShadow: '0 -2px 4px 0 rgba(0,0,0,0.10)'
|
|
||||||
}}>
|
|
||||||
<View className={'btn-bar flex justify-between items-center'}>
|
<View className={'btn-bar flex justify-between items-center'}>
|
||||||
<div className={'flex flex-col justify-center items-start mx-4'}>
|
<div className={'flex flex-col justify-center items-start mx-4'}>
|
||||||
<View className={'flex items-center gap-2'}>
|
<View className={'flex items-center gap-2'}>
|
||||||
|
|||||||
@@ -195,9 +195,7 @@ const OrderConfirm = () => {
|
|||||||
|
|
||||||
<Gap height={50} />
|
<Gap height={50} />
|
||||||
|
|
||||||
<div className={'fixed z-50 bg-white w-full bottom-0 left-0 pt-4 pb-10'} style={{
|
<div className={'fixed z-50 bg-white w-full bottom-0 left-0 pt-4 pb-10 border-t border-gray-200'}>
|
||||||
boxShadow: '0 -2px 4px 0 rgba(0,0,0,0.10)'
|
|
||||||
}}>
|
|
||||||
<View className={'btn-bar flex justify-between items-center'}>
|
<View className={'btn-bar flex justify-between items-center'}>
|
||||||
<div className={'flex justify-center items-center mx-4'}>
|
<div className={'flex justify-center items-center mx-4'}>
|
||||||
<span className={'total-price text-sm text-gray-500'}>实付金额:</span>
|
<span className={'total-price text-sm text-gray-500'}>实付金额:</span>
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export const businessGradients = {
|
|||||||
danger: 'linear-gradient(135deg, #ef4444 0%, #f87171 100%)',
|
danger: 'linear-gradient(135deg, #ef4444 0%, #f87171 100%)',
|
||||||
info: 'linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%)'
|
info: 'linear-gradient(135deg, #3b82f6 0%, #60a5fa 100%)'
|
||||||
},
|
},
|
||||||
|
|
||||||
// 订单相关
|
// 订单相关
|
||||||
order: {
|
order: {
|
||||||
pending: 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%)',
|
pending: 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%)',
|
||||||
@@ -99,7 +99,7 @@ export const businessGradients = {
|
|||||||
cancelled: 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)',
|
cancelled: 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)',
|
||||||
processing: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)'
|
processing: 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)'
|
||||||
},
|
},
|
||||||
|
|
||||||
// 金额相关
|
// 金额相关
|
||||||
money: {
|
money: {
|
||||||
available: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
|
available: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
|
||||||
@@ -108,51 +108,47 @@ export const businessGradients = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 卡片渐变样式
|
// 卡片渐变样式(小程序兼容版本)
|
||||||
export const cardGradients = {
|
export const cardGradients = {
|
||||||
glass: {
|
glass: {
|
||||||
background: 'linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%)',
|
background: 'linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.05) 100%)',
|
||||||
border: '1px solid rgba(255, 255, 255, 0.2)',
|
border: '1px solid rgba(255, 255, 255, 0.2)'
|
||||||
backdropFilter: 'blur(10px)'
|
// 注意:小程序不支持 backdropFilter
|
||||||
},
|
},
|
||||||
|
|
||||||
subtle: {
|
subtle: {
|
||||||
background: 'linear-gradient(135deg, #ffffff 0%, #f8fafc 100%)',
|
background: 'linear-gradient(135deg, #ffffff 0%, #f8fafc 100%)',
|
||||||
border: '1px solid rgba(255, 255, 255, 0.8)',
|
border: '1px solid rgba(255, 255, 255, 0.8)'
|
||||||
boxShadow: '0 10px 25px rgba(0, 0, 0, 0.1), 0 4px 10px rgba(0, 0, 0, 0.05)'
|
// 注意:小程序不支持 boxShadow,使用边框和背景替代
|
||||||
},
|
},
|
||||||
|
|
||||||
elevated: {
|
elevated: {
|
||||||
background: 'linear-gradient(135deg, #ffffff 0%, #f1f5f9 100%)',
|
background: 'linear-gradient(135deg, #ffffff 0%, #f1f5f9 100%)',
|
||||||
border: '1px solid rgba(255, 255, 255, 0.9)',
|
border: '1px solid rgba(255, 255, 255, 0.9)'
|
||||||
boxShadow: '0 20px 40px rgba(0, 0, 0, 0.1), 0 8px 16px rgba(0, 0, 0, 0.06)'
|
// 注意:小程序不支持 boxShadow,使用边框和背景替代
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 文字渐变样式
|
// 文字渐变样式(小程序兼容版本 - 使用纯色替代)
|
||||||
export const textGradients = {
|
export const textGradients = {
|
||||||
primary: {
|
primary: {
|
||||||
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
color: '#667eea'
|
||||||
WebkitBackgroundClip: 'text',
|
// 注意:小程序不支持 WebkitBackgroundClip 和 WebkitTextFillColor,使用纯色替代
|
||||||
WebkitTextFillColor: 'transparent'
|
|
||||||
},
|
},
|
||||||
|
|
||||||
success: {
|
success: {
|
||||||
background: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
|
color: '#10b981'
|
||||||
WebkitBackgroundClip: 'text',
|
// 注意:小程序不支持文字渐变,使用纯色替代
|
||||||
WebkitTextFillColor: 'transparent'
|
|
||||||
},
|
},
|
||||||
|
|
||||||
warning: {
|
warning: {
|
||||||
background: 'linear-gradient(135deg, #f59e0b 0%, #d97706 100%)',
|
color: '#f59e0b'
|
||||||
WebkitBackgroundClip: 'text',
|
// 注意:小程序不支持文字渐变,使用纯色替代
|
||||||
WebkitTextFillColor: 'transparent'
|
|
||||||
},
|
},
|
||||||
|
|
||||||
danger: {
|
danger: {
|
||||||
background: 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)',
|
color: '#ef4444'
|
||||||
WebkitBackgroundClip: 'text',
|
// 注意:小程序不支持文字渐变,使用纯色替代
|
||||||
WebkitTextFillColor: 'transparent'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,12 +160,12 @@ export const gradientUtils = {
|
|||||||
const index = userId % gradientThemes.length
|
const index = userId % gradientThemes.length
|
||||||
return gradientThemes[index]
|
return gradientThemes[index]
|
||||||
},
|
},
|
||||||
|
|
||||||
// 根据主题名获取主题
|
// 根据主题名获取主题
|
||||||
getThemeByName: (name: string): GradientTheme | undefined => {
|
getThemeByName: (name: string): GradientTheme | undefined => {
|
||||||
return gradientThemes.find(theme => theme.name === name)
|
return gradientThemes.find(theme => theme.name === name)
|
||||||
},
|
},
|
||||||
|
|
||||||
// 调整颜色亮度
|
// 调整颜色亮度
|
||||||
adjustColorBrightness: (color: string, percent: number): string => {
|
adjustColorBrightness: (color: string, percent: number): string => {
|
||||||
const num = parseInt(color.replace("#", ""), 16)
|
const num = parseInt(color.replace("#", ""), 16)
|
||||||
@@ -181,12 +177,12 @@ export const gradientUtils = {
|
|||||||
(G < 255 ? G < 1 ? 0 : G : 255) * 0x100 +
|
(G < 255 ? G < 1 ? 0 : G : 255) * 0x100 +
|
||||||
(B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1)
|
(B < 255 ? B < 1 ? 0 : B : 255)).toString(16).slice(1)
|
||||||
},
|
},
|
||||||
|
|
||||||
// 生成自定义渐变
|
// 生成自定义渐变
|
||||||
createGradient: (color1: string, color2: string, direction = '135deg'): string => {
|
createGradient: (color1: string, color2: string, direction = '135deg'): string => {
|
||||||
return `linear-gradient(${direction}, ${color1} 0%, ${color2} 100%)`
|
return `linear-gradient(${direction}, ${color1} 0%, ${color2} 100%)`
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取渐变的主色调
|
// 获取渐变的主色调
|
||||||
getPrimaryColor: (gradient: string): string => {
|
getPrimaryColor: (gradient: string): string => {
|
||||||
const match = gradient.match(/#[a-fA-F0-9]{6}/)
|
const match = gradient.match(/#[a-fA-F0-9]{6}/)
|
||||||
@@ -201,7 +197,7 @@ export const animatedGradients = {
|
|||||||
backgroundSize: '400% 400%',
|
backgroundSize: '400% 400%',
|
||||||
animation: 'gradientFlow 15s ease infinite'
|
animation: 'gradientFlow 15s ease infinite'
|
||||||
},
|
},
|
||||||
|
|
||||||
pulse: {
|
pulse: {
|
||||||
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
||||||
animation: 'gradientPulse 3s ease-in-out infinite'
|
animation: 'gradientPulse 3s ease-in-out infinite'
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ const getInfiniteUlStyle = (showSearch: boolean = false): CSSProperties => ({
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
padding: '0',
|
padding: '0',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
overflowX: 'hidden',
|
overflowX: 'hidden'
|
||||||
boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)',
|
// 注意:小程序不支持 boxShadow
|
||||||
})
|
})
|
||||||
|
|
||||||
// 统一的订单状态标签配置,与后端 statusFilter 保持一致
|
// 统一的订单状态标签配置,与后端 statusFilter 保持一致
|
||||||
@@ -362,8 +362,8 @@ function OrderList(props: OrderListProps) {
|
|||||||
borderBottom: '1px solid #e5e5e5'
|
borderBottom: '1px solid #e5e5e5'
|
||||||
}}
|
}}
|
||||||
tabStyle={{
|
tabStyle={{
|
||||||
backgroundColor: '#ffffff',
|
backgroundColor: '#ffffff'
|
||||||
boxShadow: '0 2px 4px rgba(0,0,0,0.1)'
|
// 注意:小程序不支持 boxShadow
|
||||||
}}
|
}}
|
||||||
value={tapIndex}
|
value={tapIndex}
|
||||||
onChange={(paneKey) => {
|
onChange={(paneKey) => {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ const StoreVerification: React.FC = () => {
|
|||||||
const json = JSON.parse(res.result)
|
const json = JSON.parse(res.result)
|
||||||
console.log(json, 'json')
|
console.log(json, 'json')
|
||||||
if (json.businessType === 'gift') {
|
if (json.businessType === 'gift') {
|
||||||
|
// 调用解密接口
|
||||||
handleDecryptAndVerify(json.token, json.data).then()
|
handleDecryptAndVerify(json.token, json.data).then()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,20 +49,35 @@ const StoreVerification: React.FC = () => {
|
|||||||
|
|
||||||
// 调用解密接口
|
// 调用解密接口
|
||||||
const handleDecryptAndVerify = async (token: string, encryptedData: string) => {
|
const handleDecryptAndVerify = async (token: string, encryptedData: string) => {
|
||||||
const decryptedData = await decryptQrData({token, encryptedData})
|
decryptQrData({token, encryptedData}).then(res => {
|
||||||
console.log('解密成功:', decryptedData)
|
const decryptedData = res;
|
||||||
setScanResult(`${decryptedData}`)
|
console.log('解密结果:', decryptedData)
|
||||||
setVerificationCode(`${decryptedData}`)
|
console.log('解密成功:', decryptedData)
|
||||||
await handleVerification(`${decryptedData}`)
|
setScanResult(`${decryptedData}`)
|
||||||
setLoading(false)
|
setVerificationCode(`${decryptedData}`)
|
||||||
|
handleVerification(`${decryptedData}`)
|
||||||
|
}).catch(() => {
|
||||||
|
console.error('解密失败:')
|
||||||
|
Taro.showToast({
|
||||||
|
title: `token失效,请刷新二维码重试`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}).finally(() => {
|
||||||
|
setLoading(false)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证商品信息
|
// 验证商品信息
|
||||||
const handleVerification = async (code?: string) => {
|
const handleVerification = async (code?: string) => {
|
||||||
|
setGiftInfo(null)
|
||||||
|
setVerificationCode(`${code}`)
|
||||||
// 这里应该调用后端API验证核销码
|
// 这里应该调用后端API验证核销码
|
||||||
const gift = await getShopGiftByCode(`${code}`)
|
const gift = await getShopGiftByCode(`${code}`)
|
||||||
// 设置礼品信息用于显示
|
if(gift){
|
||||||
setGiftInfo(gift)
|
// 设置礼品信息用于显示
|
||||||
|
setGiftInfo(gift)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 手动输入核销码验证
|
// 手动输入核销码验证
|
||||||
@@ -264,10 +280,8 @@ const StoreVerification: React.FC = () => {
|
|||||||
{giftInfo.description && (
|
{giftInfo.description && (
|
||||||
<>
|
<>
|
||||||
<View className="text-sm text-gray-600 mb-2" style={{
|
<View className="text-sm text-gray-600 mb-2" style={{
|
||||||
display: '-webkit-box',
|
|
||||||
WebkitLineClamp: 2,
|
|
||||||
WebkitBoxOrient: 'vertical',
|
|
||||||
overflow: 'hidden'
|
overflow: 'hidden'
|
||||||
|
// 注意:小程序不支持 WebKit 文本截断属性
|
||||||
}}>
|
}}>
|
||||||
{giftInfo.description}
|
{giftInfo.description}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
4
src/user/theme/index.config.ts
Normal file
4
src/user/theme/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '主题设置',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
})
|
||||||
179
src/user/theme/index.tsx
Normal file
179
src/user/theme/index.tsx
Normal 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
|
||||||
Reference in New Issue
Block a user