feat: 本次提交更新内容如下
梳理了icon ,增加了layout的loading
This commit is contained in:
274
nkebao/AI_ICON_GUIDE.md
Normal file
274
nkebao/AI_ICON_GUIDE.md
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
# AI图标使用指南
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本文档为AI助手提供标准化的图标选择和使用流程,确保在代码生成时使用正确的图标包和图标名称。
|
||||||
|
|
||||||
|
## AI使用流程
|
||||||
|
|
||||||
|
### 1. 项目类型判断
|
||||||
|
首先判断项目类型:
|
||||||
|
- **PC端项目**: 使用 `@ant-design/icons`
|
||||||
|
- **移动端项目**: 使用 `antd-mobile-icons`
|
||||||
|
|
||||||
|
### 2. 图标查找流程
|
||||||
|
1. 根据功能需求确定图标类型
|
||||||
|
2. 在对应图标包中查找合适的图标
|
||||||
|
3. 如果图标不存在,查找替代方案
|
||||||
|
4. 使用正确的导入语法
|
||||||
|
|
||||||
|
### 3. 代码生成模板
|
||||||
|
|
||||||
|
#### PC端项目模板
|
||||||
|
```typescript
|
||||||
|
import {
|
||||||
|
// 导航类
|
||||||
|
HomeOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
SettingOutlined,
|
||||||
|
|
||||||
|
// 操作类
|
||||||
|
PlusOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
CopyOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
|
||||||
|
// 状态类
|
||||||
|
CheckOutlined,
|
||||||
|
CloseOutlined,
|
||||||
|
CheckCircleOutlined,
|
||||||
|
CloseCircleOutlined,
|
||||||
|
ExclamationCircleOutlined,
|
||||||
|
InfoCircleOutlined,
|
||||||
|
LoadingOutlined,
|
||||||
|
|
||||||
|
// 方向类
|
||||||
|
UpOutlined,
|
||||||
|
DownOutlined,
|
||||||
|
LeftOutlined,
|
||||||
|
RightOutlined,
|
||||||
|
ArrowLeftOutlined,
|
||||||
|
|
||||||
|
// 其他
|
||||||
|
MessageOutlined,
|
||||||
|
CalendarOutlined,
|
||||||
|
ClockCircleOutlined,
|
||||||
|
PictureOutlined,
|
||||||
|
FileOutlined,
|
||||||
|
CameraOutlined,
|
||||||
|
QrcodeOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 移动端项目模板
|
||||||
|
```typescript
|
||||||
|
import {
|
||||||
|
// 导航类
|
||||||
|
HomeOutline,
|
||||||
|
UserOutline,
|
||||||
|
SettingOutline,
|
||||||
|
|
||||||
|
// 操作类
|
||||||
|
AddOutline,
|
||||||
|
EditSOutline,
|
||||||
|
DeleteOutline,
|
||||||
|
CopyOutline,
|
||||||
|
SearchOutline,
|
||||||
|
RefreshOutline,
|
||||||
|
|
||||||
|
// 状态类
|
||||||
|
CheckOutline,
|
||||||
|
CloseOutline,
|
||||||
|
CheckCircleOutline,
|
||||||
|
CloseCircleOutline,
|
||||||
|
ExclamationCircleOutline,
|
||||||
|
InfoCircleOutline,
|
||||||
|
LoadingOutline,
|
||||||
|
|
||||||
|
// 方向类
|
||||||
|
UpOutline,
|
||||||
|
DownOutline,
|
||||||
|
LeftOutline,
|
||||||
|
RightOutline,
|
||||||
|
|
||||||
|
// 其他
|
||||||
|
MessageOutline,
|
||||||
|
CalendarOutline,
|
||||||
|
ClockCircleOutline,
|
||||||
|
PictureOutline,
|
||||||
|
FileOutline,
|
||||||
|
CameraOutline,
|
||||||
|
QrCodeOutline,
|
||||||
|
} from 'antd-mobile-icons';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 功能到图标映射
|
||||||
|
|
||||||
|
### 基础功能映射
|
||||||
|
| 功能需求 | PC端图标 | 移动端图标 | 说明 |
|
||||||
|
|---------|---------|-----------|------|
|
||||||
|
| 添加/新建 | PlusOutlined | AddOutline | 通用添加功能 |
|
||||||
|
| 编辑/修改 | EditOutlined | EditSOutline | 编辑功能 |
|
||||||
|
| 删除/移除 | DeleteOutlined | DeleteOutline | 删除功能 |
|
||||||
|
| 复制/克隆 | CopyOutlined | CopyOutline | 复制功能 |
|
||||||
|
| 搜索/查找 | SearchOutlined | SearchOutline | 搜索功能 |
|
||||||
|
| 刷新/重新加载 | ReloadOutlined | RefreshOutline | 刷新功能 |
|
||||||
|
| 设置/配置 | SettingOutlined | SettingOutline | 设置功能 |
|
||||||
|
| 用户/个人 | UserOutlined | UserOutline | 用户相关 |
|
||||||
|
| 首页/主页 | HomeOutlined | HomeOutline | 首页导航 |
|
||||||
|
| 返回/后退 | ArrowLeftOutlined | LeftOutline | 返回功能 |
|
||||||
|
| 关闭/取消 | CloseOutlined | CloseOutline | 关闭功能 |
|
||||||
|
| 确认/确定 | CheckOutlined | CheckOutline | 确认功能 |
|
||||||
|
|
||||||
|
### 状态指示映射
|
||||||
|
| 状态需求 | PC端图标 | 移动端图标 | 说明 |
|
||||||
|
|---------|---------|-----------|------|
|
||||||
|
| 成功/完成 | CheckCircleOutlined | CheckCircleOutline | 成功状态 |
|
||||||
|
| 错误/失败 | CloseCircleOutlined | CloseCircleOutline | 错误状态 |
|
||||||
|
| 警告/注意 | ExclamationCircleOutlined | ExclamationCircleOutline | 警告状态 |
|
||||||
|
| 信息/提示 | InfoCircleOutlined | InfoCircleOutline | 信息提示 |
|
||||||
|
| 加载/等待 | LoadingOutlined | LoadingOutline | 加载状态 |
|
||||||
|
| 时间/等待 | ClockCircleOutlined | ClockCircleOutline | 时间相关 |
|
||||||
|
|
||||||
|
### 方向导航映射
|
||||||
|
| 方向需求 | PC端图标 | 移动端图标 | 说明 |
|
||||||
|
|---------|---------|-----------|------|
|
||||||
|
| 向上/上升 | UpOutlined | UpOutline | 向上方向 |
|
||||||
|
| 向下/下降 | DownOutlined | DownOutline | 向下方向 |
|
||||||
|
| 向左/后退 | LeftOutlined | LeftOutline | 向左方向 |
|
||||||
|
| 向右/前进 | RightOutlined | RightOutline | 向右方向 |
|
||||||
|
|
||||||
|
### 业务功能映射
|
||||||
|
| 业务需求 | PC端图标 | 移动端图标 | 说明 |
|
||||||
|
|---------|---------|-----------|------|
|
||||||
|
| 消息/通知 | MessageOutlined | MessageOutline | 消息功能 |
|
||||||
|
| 日历/日期 | CalendarOutlined | CalendarOutline | 日历功能 |
|
||||||
|
| 图片/照片 | PictureOutlined | PictureOutline | 图片功能 |
|
||||||
|
| 文件/文档 | FileOutlined | FileOutline | 文件功能 |
|
||||||
|
| 相机/拍照 | CameraOutlined | CameraOutline | 相机功能 |
|
||||||
|
| 二维码 | QrcodeOutlined | QrCodeOutline | 二维码功能 |
|
||||||
|
| 微信/社交 | WechatOutlined | WechatOutline | 微信功能 |
|
||||||
|
| 设备/手机 | MobileOutlined | MobileOutline | 设备功能 |
|
||||||
|
| 团队/群组 | TeamOutlined | TeamOutline | 团队功能 |
|
||||||
|
| 订单/购物 | ShoppingOutlined | ShoppingOutline | 订单功能 |
|
||||||
|
| 支付/钱包 | PayCircleOutlined | PayCircleOutline | 支付功能 |
|
||||||
|
|
||||||
|
## 特殊替换规则
|
||||||
|
|
||||||
|
### 移动端不存在的图标替换
|
||||||
|
| 原需求 | 替换方案 | 说明 |
|
||||||
|
|--------|----------|------|
|
||||||
|
| RiseOutlined | UpOutline | 上升趋势 |
|
||||||
|
| ThumbsUpOutlined | LikeOutline | 点赞功能 |
|
||||||
|
| ShareAltOutlined | LinkOutline | 分享功能 |
|
||||||
|
| BarChartOutlined | PieOutline | 图表功能 |
|
||||||
|
| LineChartOutlined | PieOutline | 图表功能 |
|
||||||
|
| UserAddOutlined | UserOutline | 用户添加 |
|
||||||
|
| Progress | 自定义div | 进度条组件 |
|
||||||
|
|
||||||
|
### PC端不存在的图标替换
|
||||||
|
| 原需求 | 替换方案 | 说明 |
|
||||||
|
|--------|----------|------|
|
||||||
|
| AntOutline | HomeOutlined | 蚂蚁图标 |
|
||||||
|
| AppOutline | AppstoreOutlined | 应用图标 |
|
||||||
|
|
||||||
|
## AI代码生成示例
|
||||||
|
|
||||||
|
### 场景1: 移动端列表页面
|
||||||
|
```typescript
|
||||||
|
// AI应该生成的代码
|
||||||
|
import {
|
||||||
|
AddOutline, // 添加按钮
|
||||||
|
EditSOutline, // 编辑按钮
|
||||||
|
DeleteOutline, // 删除按钮
|
||||||
|
CopyOutline, // 复制按钮
|
||||||
|
SearchOutline, // 搜索框
|
||||||
|
RefreshOutline, // 刷新按钮
|
||||||
|
UserOutline, // 用户信息
|
||||||
|
CalendarOutline, // 时间信息
|
||||||
|
UpOutline, // 上升趋势(替换RiseOutlined)
|
||||||
|
} from 'antd-mobile-icons';
|
||||||
|
|
||||||
|
// 使用示例
|
||||||
|
<Button>
|
||||||
|
<AddOutline /> 新建
|
||||||
|
</Button>
|
||||||
|
<Button>
|
||||||
|
<EditSOutline /> 编辑
|
||||||
|
</Button>
|
||||||
|
<Button color="danger">
|
||||||
|
<DeleteOutline /> 删除
|
||||||
|
</Button>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 场景2: PC端管理页面
|
||||||
|
```typescript
|
||||||
|
// AI应该生成的代码
|
||||||
|
import {
|
||||||
|
PlusOutlined, // 添加按钮
|
||||||
|
EditOutlined, // 编辑按钮
|
||||||
|
DeleteOutlined, // 删除按钮
|
||||||
|
CopyOutlined, // 复制按钮
|
||||||
|
SearchOutlined, // 搜索框
|
||||||
|
ReloadOutlined, // 刷新按钮
|
||||||
|
UserOutlined, // 用户信息
|
||||||
|
CalendarOutlined, // 时间信息
|
||||||
|
RiseOutlined, // 上升趋势(PC端存在)
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
|
||||||
|
// 使用示例
|
||||||
|
<Button icon={<PlusOutlined />}>新建</Button>
|
||||||
|
<Button icon={<EditOutlined />}>编辑</Button>
|
||||||
|
<Button icon={<DeleteOutlined />} danger>删除</Button>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 错误检测和修正
|
||||||
|
|
||||||
|
### 常见错误模式
|
||||||
|
1. **混用图标包**: 同时导入PC端和移动端图标
|
||||||
|
2. **使用不存在的图标**: 在移动端使用PC端特有的图标
|
||||||
|
3. **命名错误**: 图标名称大小写错误
|
||||||
|
4. **导入路径错误**: 使用错误的包名
|
||||||
|
|
||||||
|
### 修正策略
|
||||||
|
1. **统一图标包**: 根据项目类型选择单一图标包
|
||||||
|
2. **查找替代**: 使用对照表查找替代图标
|
||||||
|
3. **验证存在**: 确保图标在目标包中存在
|
||||||
|
4. **测试验证**: 在代码中测试图标是否正常显示
|
||||||
|
|
||||||
|
## AI使用检查清单
|
||||||
|
|
||||||
|
### 代码生成前
|
||||||
|
- [ ] 确认项目类型(PC端/移动端)
|
||||||
|
- [ ] 选择对应的图标包
|
||||||
|
- [ ] 根据功能需求选择合适图标
|
||||||
|
- [ ] 检查图标是否存在
|
||||||
|
|
||||||
|
### 代码生成中
|
||||||
|
- [ ] 使用正确的导入语法
|
||||||
|
- [ ] 图标名称大小写正确
|
||||||
|
- [ ] 避免混用不同包的图标
|
||||||
|
- [ ] 为不存在的图标提供替代方案
|
||||||
|
|
||||||
|
### 代码生成后
|
||||||
|
- [ ] 验证图标导入正确
|
||||||
|
- [ ] 检查图标使用语法
|
||||||
|
- [ ] 确保样式设置合理
|
||||||
|
- [ ] 提供使用示例
|
||||||
|
|
||||||
|
## 更新和维护
|
||||||
|
|
||||||
|
- 定期更新图标对照表
|
||||||
|
- 记录新发现的图标差异
|
||||||
|
- 更新替换规则
|
||||||
|
- 优化AI使用流程
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **优先使用语义化图标**: 选择最能表达功能的图标
|
||||||
|
2. **保持一致性**: 在同一项目中保持图标风格一致
|
||||||
|
3. **考虑可访问性**: 为图标添加适当的aria-label
|
||||||
|
4. **性能优化**: 按需导入图标,避免全量导入
|
||||||
|
5. **版本兼容**: 注意图标包版本与UI框架版本的兼容性
|
||||||
@@ -1,269 +0,0 @@
|
|||||||
# Ant Design Mobile 图标完整列表
|
|
||||||
|
|
||||||
## 📋 图标分类
|
|
||||||
|
|
||||||
### 🔗 基础图标 (Outline)
|
|
||||||
- `AddOutline` - 添加
|
|
||||||
- `AppOutline` - 应用
|
|
||||||
- `ArrowDownOutline` - 向下箭头
|
|
||||||
- `ArrowLeftOutline` - 向左箭头
|
|
||||||
- `ArrowRightOutline` - 向右箭头
|
|
||||||
- `ArrowUpOutline` - 向上箭头
|
|
||||||
- `AudioOutline` - 音频
|
|
||||||
- `BarChartOutline` - 柱状图
|
|
||||||
- `BellOutline` - 铃铛
|
|
||||||
- `BookOutline` - 书本
|
|
||||||
- `CalendarOutline` - 日历
|
|
||||||
- `CameraOutline` - 相机
|
|
||||||
- `CheckOutline` - 勾选
|
|
||||||
- `ClockCircleOutline` - 时钟
|
|
||||||
- `CloseOutline` - 关闭
|
|
||||||
- `DeleteOutline` - 删除
|
|
||||||
- `DownlandOutline` - 下载
|
|
||||||
- `EditSOutline` - 编辑
|
|
||||||
- `EllipsisOutline` - 省略号
|
|
||||||
- `EyeOutline` - 眼睛
|
|
||||||
- `FileOutline` - 文件
|
|
||||||
- `FilterOutline` - 筛选
|
|
||||||
- `FireOutline` - 火焰
|
|
||||||
- `FlagOutline` - 旗帜
|
|
||||||
- `FolderOutline` - 文件夹
|
|
||||||
- `GiftOutline` - 礼物
|
|
||||||
- `HeartOutline` - 心形
|
|
||||||
- `HomeOutline` - 首页
|
|
||||||
- `ImageOutline` - 图片
|
|
||||||
- `LeftOutline` - 左箭头
|
|
||||||
- `LikeOutline` - 点赞
|
|
||||||
- `LinkOutline` - 链接
|
|
||||||
- `LocationOutline` - 位置
|
|
||||||
- `LockOutline` - 锁定
|
|
||||||
- `MailOutline` - 邮件
|
|
||||||
- `MessageOutline` - 消息
|
|
||||||
- `MoreOutline` - 更多
|
|
||||||
- `PayCircleOutline` - 支付
|
|
||||||
- `PhoneOutline` - 电话
|
|
||||||
- `PictureOutline` - 图片
|
|
||||||
- `PlayOutline` - 播放
|
|
||||||
- `PlusOutline` - 加号
|
|
||||||
- `RightOutline` - 右箭头
|
|
||||||
- `ScanOutline` - 扫描
|
|
||||||
- `SearchOutline` - 搜索
|
|
||||||
- `SendOutline` - 发送
|
|
||||||
- `SettingOutline` - 设置
|
|
||||||
- `ShareOutline` - 分享
|
|
||||||
- `ShopbagOutline` - 购物袋
|
|
||||||
- `StarOutline` - 星星
|
|
||||||
- `TeamOutline` - 团队
|
|
||||||
- `ThumbsUpOutline` - 点赞
|
|
||||||
- `TimeOutline` - 时间
|
|
||||||
- `UnlockOutline` - 解锁
|
|
||||||
- `UserOutline` - 用户
|
|
||||||
- `VideoOutline` - 视频
|
|
||||||
- `VoiceOutline` - 语音
|
|
||||||
|
|
||||||
### 🎨 填充图标 (Fill)
|
|
||||||
- `AddCircleFill` - 添加圆形
|
|
||||||
- `AppFill` - 应用填充
|
|
||||||
- `AudioFill` - 音频填充
|
|
||||||
- `BellFill` - 铃铛填充
|
|
||||||
- `BookFill` - 书本填充
|
|
||||||
- `CalendarFill` - 日历填充
|
|
||||||
- `CameraFill` - 相机填充
|
|
||||||
- `CheckCircleFill` - 勾选圆形
|
|
||||||
- `ClockCircleFill` - 时钟圆形
|
|
||||||
- `CloseCircleFill` - 关闭圆形
|
|
||||||
- `DeleteFill` - 删除填充
|
|
||||||
- `DownlandFill` - 下载填充
|
|
||||||
- `EditSFill` - 编辑填充
|
|
||||||
- `EyeFill` - 眼睛填充
|
|
||||||
- `FileFill` - 文件填充
|
|
||||||
- `FilterFill` - 筛选填充
|
|
||||||
- `FireFill` - 火焰填充
|
|
||||||
- `FlagFill` - 旗帜填充
|
|
||||||
- `FolderFill` - 文件夹填充
|
|
||||||
- `GiftFill` - 礼物填充
|
|
||||||
- `HeartFill` - 心形填充
|
|
||||||
- `HomeFill` - 首页填充
|
|
||||||
- `ImageFill` - 图片填充
|
|
||||||
- `LeftFill` - 左箭头填充
|
|
||||||
- `LikeFill` - 点赞填充
|
|
||||||
- `LinkFill` - 链接填充
|
|
||||||
- `LocationFill` - 位置填充
|
|
||||||
- `LockFill` - 锁定填充
|
|
||||||
- `MailFill` - 邮件填充
|
|
||||||
- `MessageFill` - 消息填充
|
|
||||||
- `MoreFill` - 更多填充
|
|
||||||
- `PayCircleFill` - 支付圆形
|
|
||||||
- `PhoneFill` - 电话填充
|
|
||||||
- `PictureFill` - 图片填充
|
|
||||||
- `PlayFill` - 播放填充
|
|
||||||
- `PlusCircleFill` - 加号圆形
|
|
||||||
- `RightFill` - 右箭头填充
|
|
||||||
- `ScanFill` - 扫描填充
|
|
||||||
- `SearchFill` - 搜索填充
|
|
||||||
- `SendFill` - 发送填充
|
|
||||||
- `SettingFill` - 设置填充
|
|
||||||
- `ShareFill` - 分享填充
|
|
||||||
- `ShopbagFill` - 购物袋填充
|
|
||||||
- `StarFill` - 星星填充
|
|
||||||
- `TeamFill` - 团队填充
|
|
||||||
- `ThumbsUpFill` - 点赞填充
|
|
||||||
- `TimeFill` - 时间填充
|
|
||||||
- `UnlockFill` - 解锁填充
|
|
||||||
- `UserFill` - 用户填充
|
|
||||||
- `VideoFill` - 视频填充
|
|
||||||
- `VoiceFill` - 语音填充
|
|
||||||
|
|
||||||
### 📊 图表图标
|
|
||||||
- `BarChartOutline` - 柱状图
|
|
||||||
- `LineChartOutline` - 折线图
|
|
||||||
- `PieOutline` - 饼图
|
|
||||||
- `AreaOutline` - 面积图
|
|
||||||
- `RadarOutline` - 雷达图
|
|
||||||
- `ScatterOutline` - 散点图
|
|
||||||
|
|
||||||
### 🔧 功能图标
|
|
||||||
- `AddOutline` - 添加
|
|
||||||
- `DeleteOutline` - 删除
|
|
||||||
- `EditSOutline` - 编辑
|
|
||||||
- `SearchOutline` - 搜索
|
|
||||||
- `FilterOutline` - 筛选
|
|
||||||
- `SettingOutline` - 设置
|
|
||||||
- `MoreOutline` - 更多
|
|
||||||
- `CloseOutline` - 关闭
|
|
||||||
- `CheckOutline` - 勾选
|
|
||||||
|
|
||||||
### 📱 通信图标
|
|
||||||
- `MessageOutline` - 消息
|
|
||||||
- `PhoneOutline` - 电话
|
|
||||||
- `MailOutline` - 邮件
|
|
||||||
- `SendOutline` - 发送
|
|
||||||
- `VoiceOutline` - 语音
|
|
||||||
- `VideoOutline` - 视频
|
|
||||||
|
|
||||||
### 👥 用户图标
|
|
||||||
- `UserOutline` - 用户
|
|
||||||
- `TeamOutline` - 团队
|
|
||||||
- `HeartOutline` - 心形
|
|
||||||
- `LikeOutline` - 点赞
|
|
||||||
- `ThumbsUpOutline` - 点赞
|
|
||||||
|
|
||||||
### 🛍️ 商业图标
|
|
||||||
- `ShopbagOutline` - 购物袋
|
|
||||||
- `PayCircleOutline` - 支付
|
|
||||||
- `GiftOutline` - 礼物
|
|
||||||
- `StarOutline` - 星星
|
|
||||||
|
|
||||||
### 📁 文件图标
|
|
||||||
- `FileOutline` - 文件
|
|
||||||
- `FolderOutline` - 文件夹
|
|
||||||
- `ImageOutline` - 图片
|
|
||||||
- `PictureOutline` - 图片
|
|
||||||
- `CameraOutline` - 相机
|
|
||||||
|
|
||||||
### 🎵 媒体图标
|
|
||||||
- `AudioOutline` - 音频
|
|
||||||
- `VideoOutline` - 视频
|
|
||||||
- `PlayOutline` - 播放
|
|
||||||
- `VoiceOutline` - 语音
|
|
||||||
|
|
||||||
### 📅 时间图标
|
|
||||||
- `CalendarOutline` - 日历
|
|
||||||
- `ClockCircleOutline` - 时钟
|
|
||||||
- `TimeOutline` - 时间
|
|
||||||
|
|
||||||
### 🔒 安全图标
|
|
||||||
- `LockOutline` - 锁定
|
|
||||||
- `UnlockOutline` - 解锁
|
|
||||||
- `EyeOutline` - 眼睛
|
|
||||||
|
|
||||||
### 📍 位置图标
|
|
||||||
- `LocationOutline` - 位置
|
|
||||||
- `FlagOutline` - 旗帜
|
|
||||||
|
|
||||||
### 🔗 导航图标
|
|
||||||
- `ArrowUpOutline` - 向上箭头
|
|
||||||
- `ArrowDownOutline` - 向下箭头
|
|
||||||
- `ArrowLeftOutline` - 向左箭头
|
|
||||||
- `ArrowRightOutline` - 向右箭头
|
|
||||||
- `LeftOutline` - 左箭头
|
|
||||||
- `RightOutline` - 右箭头
|
|
||||||
|
|
||||||
## 🎨 使用示例
|
|
||||||
|
|
||||||
### 基础用法
|
|
||||||
```tsx
|
|
||||||
import { AddOutline, UserOutline, SettingOutline } from 'antd-mobile-icons';
|
|
||||||
|
|
||||||
// 在组件中使用
|
|
||||||
<AddOutline />
|
|
||||||
<UserOutline />
|
|
||||||
<SettingOutline />
|
|
||||||
```
|
|
||||||
|
|
||||||
### 自定义样式
|
|
||||||
```tsx
|
|
||||||
import { HeartOutline } from 'antd-mobile-icons';
|
|
||||||
|
|
||||||
// 自定义颜色和大小
|
|
||||||
<HeartOutline style={{ color: '#ff4d4f', fontSize: 24 }} />
|
|
||||||
```
|
|
||||||
|
|
||||||
### 在项目中的使用
|
|
||||||
```tsx
|
|
||||||
// 工作台页面示例
|
|
||||||
const features = [
|
|
||||||
{
|
|
||||||
icon: <ThumbsUpOutline style={{ color: "#ff4d4f" }} />,
|
|
||||||
name: "自动点赞"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <MessageOutline style={{ color: "#1890ff" }} />,
|
|
||||||
name: "AI对话助手"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: <BarChartOutline style={{ color: "#531dab" }} />,
|
|
||||||
name: "AI数据分析"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📝 注意事项
|
|
||||||
|
|
||||||
1. **图标命名规则**: 所有图标都以 `Outline` 或 `Fill` 结尾
|
|
||||||
2. **导入方式**: 从 `antd-mobile-icons` 包中导入
|
|
||||||
3. **样式定制**: 可以通过 `style` 属性自定义颜色、大小等
|
|
||||||
4. **响应式**: 图标支持响应式设计,可以配合 CSS 媒体查询使用
|
|
||||||
5. **可访问性**: 图标支持 `aria-label` 等无障碍属性
|
|
||||||
|
|
||||||
## 🔗 相关资源
|
|
||||||
|
|
||||||
- [Ant Design Mobile 官方文档](https://mobile.ant.design/)
|
|
||||||
- [图标组件文档](https://mobile.ant.design/zh/components/icon)
|
|
||||||
- [图标库 GitHub](https://github.com/ant-design/ant-design-mobile)
|
|
||||||
|
|
||||||
## 📊 项目中已使用的图标
|
|
||||||
|
|
||||||
### 工作台页面
|
|
||||||
- `ThumbsUpOutline` - 自动点赞
|
|
||||||
- `MessageOutline` - AI对话助手
|
|
||||||
- `SendOutline` - 群消息推送
|
|
||||||
- `TeamOutline` - 自动建群
|
|
||||||
- `ShareOutline` - 流量分发
|
|
||||||
- `AppOutline` - AI策略优化
|
|
||||||
- `BarChartOutline` - AI数据分析
|
|
||||||
- `LineChartOutline` - AI销售预测
|
|
||||||
- `ClockCircleOutline` - 朋友圈同步
|
|
||||||
|
|
||||||
### 底部导航
|
|
||||||
- `AppOutline` - 首页
|
|
||||||
- `ShopbagOutline` - 场景获客
|
|
||||||
- `PieOutline` - 工作台
|
|
||||||
- `UserOutline` - 我的
|
|
||||||
|
|
||||||
### 其他页面
|
|
||||||
- `AddOutline` - 添加按钮
|
|
||||||
- `UserAddOutline` - 用户添加
|
|
||||||
- `BellOutline` - 通知
|
|
||||||
- `LoopOutline` - 循环/同步
|
|
||||||
- `TravelOutline` - 旅行/任务
|
|
||||||
205
nkebao/ICON_DETAILED_MAPPING.md
Normal file
205
nkebao/ICON_DETAILED_MAPPING.md
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
# 详细图标对照表
|
||||||
|
|
||||||
|
## 基础图标对照
|
||||||
|
|
||||||
|
### 导航类
|
||||||
|
| 功能 | PC端 | 移动端 | 状态 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| 首页 | HomeOutlined | HomeOutline | ✅ |
|
||||||
|
| 返回 | ArrowLeftOutlined | LeftOutline | ✅ |
|
||||||
|
| 菜单 | MenuOutlined | MenuOutline | ✅ |
|
||||||
|
| 设置 | SettingOutlined | SettingOutline | ✅ |
|
||||||
|
| 用户 | UserOutlined | UserOutline | ✅ |
|
||||||
|
| 个人中心 | UserOutlined | UserOutline | ✅ |
|
||||||
|
|
||||||
|
### 操作类
|
||||||
|
| 功能 | PC端 | 移动端 | 状态 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| 添加 | PlusOutlined | AddOutline | ✅ |
|
||||||
|
| 编辑 | EditOutlined | EditSOutline | ✅ |
|
||||||
|
| 删除 | DeleteOutlined | DeleteOutline | ✅ |
|
||||||
|
| 复制 | CopyOutlined | CopyOutline | ✅ |
|
||||||
|
| 保存 | SaveOutlined | SaveOutline | ✅ |
|
||||||
|
| 刷新 | ReloadOutlined | RefreshOutline | ✅ |
|
||||||
|
| 搜索 | SearchOutlined | SearchOutline | ✅ |
|
||||||
|
| 关闭 | CloseOutlined | CloseOutline | ✅ |
|
||||||
|
| 确认 | CheckOutlined | CheckOutline | ✅ |
|
||||||
|
| 取消 | CloseOutlined | CloseOutline | ✅ |
|
||||||
|
|
||||||
|
### 状态类
|
||||||
|
| 功能 | PC端 | 移动端 | 状态 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| 成功 | CheckCircleOutlined | CheckCircleOutline | ✅ |
|
||||||
|
| 错误 | CloseCircleOutlined | CloseCircleOutline | ✅ |
|
||||||
|
| 警告 | ExclamationCircleOutlined | ExclamationCircleOutline | ✅ |
|
||||||
|
| 信息 | InfoCircleOutlined | InfoCircleOutline | ✅ |
|
||||||
|
| 加载 | LoadingOutlined | LoadingOutline | ✅ |
|
||||||
|
| 等待 | ClockCircleOutlined | ClockCircleOutline | ✅ |
|
||||||
|
|
||||||
|
### 方向类
|
||||||
|
| 功能 | PC端 | 移动端 | 状态 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| 向上 | UpOutlined | UpOutline | ✅ |
|
||||||
|
| 向下 | DownOutlined | DownOutline | ✅ |
|
||||||
|
| 向左 | LeftOutlined | LeftOutline | ✅ |
|
||||||
|
| 向右 | RightOutlined | RightOutline | ✅ |
|
||||||
|
| 向上圆形 | UpCircleOutlined | UpCircleOutline | ✅ |
|
||||||
|
| 向下圆形 | DownCircleOutlined | DownCircleOutline | ✅ |
|
||||||
|
|
||||||
|
### 通信类
|
||||||
|
| 功能 | PC端 | 移动端 | 状态 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| 消息 | MessageOutlined | MessageOutline | ✅ |
|
||||||
|
| 邮件 | MailOutlined | MailOutline | ✅ |
|
||||||
|
| 电话 | PhoneOutlined | PhoneOutline | ✅ |
|
||||||
|
| 视频 | VideoCameraOutlined | VideoCameraOutline | ✅ |
|
||||||
|
| 语音 | AudioOutlined | AudioOutline | ✅ |
|
||||||
|
|
||||||
|
### 媒体类
|
||||||
|
| 功能 | PC端 | 移动端 | 状态 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| 图片 | PictureOutlined | PictureOutline | ✅ |
|
||||||
|
| 文件 | FileOutlined | FileOutline | ✅ |
|
||||||
|
| 文件夹 | FolderOutlined | FolderOutline | ✅ |
|
||||||
|
| 相机 | CameraOutlined | CameraOutline | ✅ |
|
||||||
|
| 二维码 | QrcodeOutlined | QrCodeOutline | ✅ |
|
||||||
|
|
||||||
|
### 时间类
|
||||||
|
| 功能 | PC端 | 移动端 | 状态 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| 日历 | CalendarOutlined | CalendarOutline | ✅ |
|
||||||
|
| 时钟 | ClockCircleOutlined | ClockCircleOutline | ✅ |
|
||||||
|
| 历史 | HistoryOutlined | HistoryOutline | ✅ |
|
||||||
|
|
||||||
|
### 数据类
|
||||||
|
| 功能 | PC端 | 移动端 | 状态 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| 统计 | BarChartOutlined | BarChartOutline | ✅ |
|
||||||
|
| 饼图 | PieChartOutlined | PieChartOutline | ✅ |
|
||||||
|
| 折线图 | LineChartOutlined | LineChartOutline | ✅ |
|
||||||
|
| 表格 | TableOutlined | TableOutline | ✅ |
|
||||||
|
| 列表 | UnorderedListOutlined | UnorderedListOutline | ✅ |
|
||||||
|
|
||||||
|
## 特殊图标对照
|
||||||
|
|
||||||
|
### 业务相关
|
||||||
|
| 功能 | PC端 | 移动端 | 状态 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| 设备 | MobileOutlined | MobileOutline | ✅ |
|
||||||
|
| 微信 | WechatOutlined | WechatOutline | ✅ |
|
||||||
|
| 群组 | TeamOutlined | TeamOutline | ✅ |
|
||||||
|
| 客户 | UserAddOutlined | UserAddOutline | ❌ |
|
||||||
|
| 订单 | ShoppingOutlined | ShoppingOutline | ✅ |
|
||||||
|
| 支付 | PayCircleOutlined | PayCircleOutline | ✅ |
|
||||||
|
|
||||||
|
### 工具类
|
||||||
|
| 功能 | PC端 | 移动端 | 状态 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| 工具 | ToolOutlined | ToolOutline | ✅ |
|
||||||
|
| 配置 | SettingOutlined | SettingOutline | ✅ |
|
||||||
|
| 帮助 | QuestionCircleOutlined | QuestionCircleOutline | ✅ |
|
||||||
|
| 反馈 | MessageOutlined | MessageOutline | ✅ |
|
||||||
|
| 分享 | ShareAltOutlined | ShareOutline | ❌ |
|
||||||
|
|
||||||
|
## 不存在的图标替换方案
|
||||||
|
|
||||||
|
### PC端存在但移动端不存在的图标
|
||||||
|
| PC端图标 | 推荐替换 | 说明 |
|
||||||
|
|----------|----------|------|
|
||||||
|
| UserAddOutlined | UserOutline | 用户添加功能 |
|
||||||
|
| ShareAltOutlined | LinkOutline | 分享功能 |
|
||||||
|
| RiseOutlined | UpOutline | 上升趋势 |
|
||||||
|
| ThumbsUpOutlined | LikeOutline | 点赞功能 |
|
||||||
|
| BarChartOutlined | PieOutline | 图表功能 |
|
||||||
|
| LineChartOutlined | PieOutline | 图表功能 |
|
||||||
|
|
||||||
|
### 移动端存在但PC端不存在的图标
|
||||||
|
| 移动端图标 | 推荐替换 | 说明 |
|
||||||
|
|------------|----------|------|
|
||||||
|
| AntOutline | HomeOutlined | 蚂蚁图标 |
|
||||||
|
| AppOutline | AppstoreOutlined | 应用图标 |
|
||||||
|
|
||||||
|
## 使用规范
|
||||||
|
|
||||||
|
### 1. 导入规范
|
||||||
|
```typescript
|
||||||
|
// PC端项目
|
||||||
|
import {
|
||||||
|
HomeOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
SettingOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
|
||||||
|
// 移动端项目
|
||||||
|
import {
|
||||||
|
HomeOutline,
|
||||||
|
UserOutline,
|
||||||
|
SettingOutline,
|
||||||
|
} from 'antd-mobile-icons';
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 命名规范
|
||||||
|
- PC端:使用 `Outlined` 后缀
|
||||||
|
- 移动端:使用 `Outline` 后缀
|
||||||
|
- 保持语义化命名
|
||||||
|
|
||||||
|
### 3. 使用建议
|
||||||
|
- 优先使用语义明确的图标
|
||||||
|
- 保持图标风格一致性
|
||||||
|
- 考虑图标在不同尺寸下的清晰度
|
||||||
|
- 为图标添加适当的aria-label
|
||||||
|
|
||||||
|
### 4. 错误处理
|
||||||
|
当图标不存在时:
|
||||||
|
1. 查找语义相近的图标
|
||||||
|
2. 使用通用图标(如QuestionOutlined)
|
||||||
|
3. 考虑使用文字替代
|
||||||
|
4. 创建自定义图标组件
|
||||||
|
|
||||||
|
## 项目中的实际应用
|
||||||
|
|
||||||
|
### 场景获客模块使用的图标
|
||||||
|
```typescript
|
||||||
|
// 移动端项目中的图标使用
|
||||||
|
import {
|
||||||
|
AddOutline, // 添加
|
||||||
|
UpOutline, // 上升趋势(替换RiseOutline)
|
||||||
|
UserOutline, // 用户
|
||||||
|
CalendarOutline, // 日历
|
||||||
|
CopyOutline, // 复制
|
||||||
|
DeleteOutline, // 删除
|
||||||
|
EditSOutline, // 编辑
|
||||||
|
SettingOutline, // 设置
|
||||||
|
SearchOutline, // 搜索
|
||||||
|
RefreshOutline, // 刷新
|
||||||
|
QrCodeOutline, // 二维码
|
||||||
|
} from 'antd-mobile-icons';
|
||||||
|
```
|
||||||
|
|
||||||
|
### 工作台模块使用的图标
|
||||||
|
```typescript
|
||||||
|
// 移动端项目中的图标使用
|
||||||
|
import {
|
||||||
|
LikeOutline, // 点赞(替换ThumbsUpOutline)
|
||||||
|
LinkOutline, // 链接(替换ShareOutline)
|
||||||
|
PieOutline, // 饼图(替换BarChartOutline/LineChartOutline)
|
||||||
|
UserOutline, // 用户
|
||||||
|
TeamOutline, // 团队
|
||||||
|
MessageOutline, // 消息
|
||||||
|
} from 'antd-mobile-icons';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 更新和维护
|
||||||
|
|
||||||
|
1. **定期检查**: 定期检查新版本中新增的图标
|
||||||
|
2. **文档更新**: 及时更新图标对照表
|
||||||
|
3. **团队协作**: 团队成员共享图标使用规范
|
||||||
|
4. **代码审查**: 在代码审查中检查图标使用是否正确
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **包版本**: 确保图标包版本与UI框架版本兼容
|
||||||
|
2. **按需导入**: 避免全量导入图标,影响打包体积
|
||||||
|
3. **样式覆盖**: 可以通过CSS自定义图标样式
|
||||||
|
4. **无障碍**: 为图标添加适当的无障碍属性
|
||||||
|
5. **性能**: 大量使用图标时注意性能优化
|
||||||
230
nkebao/ICON_MAPPING_GUIDE.md
Normal file
230
nkebao/ICON_MAPPING_GUIDE.md
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
# PC端与移动端图标对照文档
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本文档记录了PC端(@ant-design/icons)和移动端(antd-mobile-icons)的图标名称对照,以及正确的导入方式。
|
||||||
|
|
||||||
|
## 导入方式
|
||||||
|
|
||||||
|
### PC端图标 (@ant-design/icons)
|
||||||
|
```typescript
|
||||||
|
import {
|
||||||
|
HomeOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
SettingOutlined,
|
||||||
|
// ... 其他图标
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
```
|
||||||
|
|
||||||
|
### 移动端图标 (antd-mobile-icons)
|
||||||
|
```typescript
|
||||||
|
import {
|
||||||
|
AntOutline,
|
||||||
|
ArrowDownCircleOutline,
|
||||||
|
UserOutline,
|
||||||
|
// ... 其他图标
|
||||||
|
} from 'antd-mobile-icons';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 图标对照表
|
||||||
|
|
||||||
|
### 常用图标对照
|
||||||
|
|
||||||
|
| 功能描述 | PC端图标 | 移动端图标 | 备注 |
|
||||||
|
|---------|---------|-----------|------|
|
||||||
|
| 首页 | HomeOutlined | HomeOutline | 完全对应 |
|
||||||
|
| 用户 | UserOutlined | UserOutline | 完全对应 |
|
||||||
|
| 设置 | SettingOutlined | SettingOutline | 完全对应 |
|
||||||
|
| 搜索 | SearchOutlined | SearchOutline | 完全对应 |
|
||||||
|
| 添加 | PlusOutlined | AddOutline | 完全对应 |
|
||||||
|
| 编辑 | EditOutlined | EditSOutline | 移动端略有不同 |
|
||||||
|
| 删除 | DeleteOutlined | DeleteOutline | 完全对应 |
|
||||||
|
| 复制 | CopyOutlined | CopyOutline | 完全对应 |
|
||||||
|
| 刷新 | ReloadOutlined | RefreshOutline | 完全对应 |
|
||||||
|
| 二维码 | QrcodeOutlined | QrCodeOutline | 完全对应 |
|
||||||
|
| 日历 | CalendarOutlined | CalendarOutline | 完全对应 |
|
||||||
|
| 时钟 | ClockCircleOutlined | ClockCircleOutline | 完全对应 |
|
||||||
|
| 箭头向上 | UpOutlined | UpOutline | 完全对应 |
|
||||||
|
| 箭头向下 | DownOutlined | DownOutline | 完全对应 |
|
||||||
|
| 箭头向左 | LeftOutlined | LeftOutline | 完全对应 |
|
||||||
|
| 箭头向右 | RightOutlined | RightOutline | 完全对应 |
|
||||||
|
| 返回 | ArrowLeftOutlined | LeftOutline | 移动端使用LeftOutline |
|
||||||
|
| 关闭 | CloseOutlined | CloseOutline | 完全对应 |
|
||||||
|
| 检查 | CheckOutlined | CheckOutline | 完全对应 |
|
||||||
|
| 警告 | ExclamationCircleOutlined | ExclamationCircleOutline | 完全对应 |
|
||||||
|
| 信息 | InfoCircleOutlined | InfoCircleOutline | 完全对应 |
|
||||||
|
| 成功 | CheckCircleOutlined | CheckCircleOutline | 完全对应 |
|
||||||
|
| 错误 | CloseCircleOutlined | CloseCircleOutline | 完全对应 |
|
||||||
|
|
||||||
|
### 方向性图标
|
||||||
|
|
||||||
|
| 功能描述 | PC端图标 | 移动端图标 |
|
||||||
|
|---------|---------|-----------|
|
||||||
|
| 向上 | UpOutlined | UpOutline |
|
||||||
|
| 向下 | DownOutlined | DownOutline |
|
||||||
|
| 向左 | LeftOutlined | LeftOutline |
|
||||||
|
| 向右 | RightOutlined | RightOutline |
|
||||||
|
| 向上圆形 | UpCircleOutlined | UpCircleOutline |
|
||||||
|
| 向下圆形 | DownCircleOutlined | DownCircleOutline |
|
||||||
|
| 向左圆形 | LeftCircleOutlined | LeftCircleOutline |
|
||||||
|
| 向右圆形 | RightCircleOutlined | RightCircleOutline |
|
||||||
|
|
||||||
|
### 编辑类图标
|
||||||
|
|
||||||
|
| 功能描述 | PC端图标 | 移动端图标 |
|
||||||
|
|---------|---------|-----------|
|
||||||
|
| 编辑 | EditOutlined | EditSOutline |
|
||||||
|
| 删除 | DeleteOutlined | DeleteOutline |
|
||||||
|
| 复制 | CopyOutlined | CopyOutline |
|
||||||
|
| 剪切 | ScissorOutlined | ScissorOutline |
|
||||||
|
| 撤销 | UndoOutlined | UndoOutline |
|
||||||
|
| 重做 | RedoOutlined | RedoOutline |
|
||||||
|
| 保存 | SaveOutlined | SaveOutline |
|
||||||
|
|
||||||
|
### 通信类图标
|
||||||
|
|
||||||
|
| 功能描述 | PC端图标 | 移动端图标 |
|
||||||
|
|---------|---------|-----------|
|
||||||
|
| 消息 | MessageOutlined | MessageOutline |
|
||||||
|
| 邮件 | MailOutlined | MailOutline |
|
||||||
|
| 电话 | PhoneOutlined | PhoneOutline |
|
||||||
|
| 视频通话 | VideoCameraOutlined | VideoCameraOutline |
|
||||||
|
| 语音 | AudioOutlined | AudioOutline |
|
||||||
|
|
||||||
|
### 媒体类图标
|
||||||
|
|
||||||
|
| 功能描述 | PC端图标 | 移动端图标 |
|
||||||
|
|---------|---------|-----------|
|
||||||
|
| 图片 | PictureOutlined | PictureOutline |
|
||||||
|
| 视频 | VideoCameraOutlined | VideoCameraOutline |
|
||||||
|
| 音频 | AudioOutlined | AudioOutline |
|
||||||
|
| 文件 | FileOutlined | FileOutline |
|
||||||
|
| 文件夹 | FolderOutlined | FolderOutline |
|
||||||
|
| 相机 | CameraOutlined | CameraOutline |
|
||||||
|
|
||||||
|
### 导航类图标
|
||||||
|
|
||||||
|
| 功能描述 | PC端图标 | 移动端图标 |
|
||||||
|
|---------|---------|-----------|
|
||||||
|
| 菜单 | MenuOutlined | MenuOutline |
|
||||||
|
| 汉堡菜单 | MenuFoldOutlined | MenuOutline |
|
||||||
|
| 展开菜单 | MenuUnfoldOutlined | MenuOutline |
|
||||||
|
| 面包屑 | BreadcrumbOutlined | BreadcrumbOutline |
|
||||||
|
| 分页 | PaginationOutlined | PaginationOutline |
|
||||||
|
|
||||||
|
### 数据展示类图标
|
||||||
|
|
||||||
|
| 功能描述 | PC端图标 | 移动端图标 |
|
||||||
|
|---------|---------|-----------|
|
||||||
|
| 表格 | TableOutlined | TableOutline |
|
||||||
|
| 列表 | UnorderedListOutlined | UnorderedListOutline |
|
||||||
|
| 卡片 | CreditCardOutlined | CreditCardOutline |
|
||||||
|
| 统计 | BarChartOutlined | BarChartOutline |
|
||||||
|
| 饼图 | PieChartOutlined | PieChartOutline |
|
||||||
|
| 折线图 | LineChartOutlined | LineChartOutline |
|
||||||
|
| 仪表盘 | DashboardOutlined | DashboardOutline |
|
||||||
|
|
||||||
|
### 反馈类图标
|
||||||
|
|
||||||
|
| 功能描述 | PC端图标 | 移动端图标 |
|
||||||
|
|---------|---------|-----------|
|
||||||
|
| 成功 | CheckCircleOutlined | CheckCircleOutline |
|
||||||
|
| 错误 | CloseCircleOutlined | CloseCircleOutline |
|
||||||
|
| 警告 | ExclamationCircleOutlined | ExclamationCircleOutline |
|
||||||
|
| 信息 | InfoCircleOutlined | InfoCircleOutline |
|
||||||
|
| 加载 | LoadingOutlined | LoadingOutline |
|
||||||
|
| 等待 | ClockCircleOutlined | ClockCircleOutline |
|
||||||
|
|
||||||
|
## 使用建议
|
||||||
|
|
||||||
|
### 1. 项目类型判断
|
||||||
|
- **PC端项目**: 使用 `@ant-design/icons`
|
||||||
|
- **移动端项目**: 使用 `antd-mobile-icons`
|
||||||
|
|
||||||
|
### 2. 图标选择原则
|
||||||
|
- 优先选择语义化图标
|
||||||
|
- 保持图标风格一致性
|
||||||
|
- 考虑图标在不同尺寸下的清晰度
|
||||||
|
|
||||||
|
### 3. 常见错误避免
|
||||||
|
- 不要混用PC端和移动端图标
|
||||||
|
- 注意图标名称的大小写
|
||||||
|
- 确保图标在对应包中存在
|
||||||
|
|
||||||
|
### 4. 图标替换策略
|
||||||
|
当某个图标在目标包中不存在时:
|
||||||
|
1. 查找语义相近的图标
|
||||||
|
2. 使用通用图标(如QuestionOutlined)
|
||||||
|
3. 考虑使用文字替代
|
||||||
|
4. 创建自定义图标组件
|
||||||
|
|
||||||
|
## 实际项目中的使用示例
|
||||||
|
|
||||||
|
### PC端项目示例
|
||||||
|
```typescript
|
||||||
|
import {
|
||||||
|
HomeOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
SettingOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
PlusOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
CopyOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
QrcodeOutlined,
|
||||||
|
CalendarOutlined,
|
||||||
|
ClockCircleOutlined,
|
||||||
|
UpOutlined,
|
||||||
|
DownOutlined,
|
||||||
|
LeftOutlined,
|
||||||
|
RightOutlined,
|
||||||
|
CloseOutlined,
|
||||||
|
CheckOutlined,
|
||||||
|
ExclamationCircleOutlined,
|
||||||
|
InfoCircleOutlined,
|
||||||
|
CheckCircleOutlined,
|
||||||
|
CloseCircleOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
```
|
||||||
|
|
||||||
|
### 移动端项目示例
|
||||||
|
```typescript
|
||||||
|
import {
|
||||||
|
HomeOutline,
|
||||||
|
UserOutline,
|
||||||
|
SettingOutline,
|
||||||
|
SearchOutline,
|
||||||
|
AddOutline,
|
||||||
|
EditSOutline,
|
||||||
|
DeleteOutline,
|
||||||
|
CopyOutline,
|
||||||
|
RefreshOutline,
|
||||||
|
QrCodeOutline,
|
||||||
|
CalendarOutline,
|
||||||
|
ClockCircleOutline,
|
||||||
|
UpOutline,
|
||||||
|
DownOutline,
|
||||||
|
LeftOutline,
|
||||||
|
RightOutline,
|
||||||
|
CloseOutline,
|
||||||
|
CheckOutline,
|
||||||
|
ExclamationCircleOutline,
|
||||||
|
InfoCircleOutline,
|
||||||
|
CheckCircleOutline,
|
||||||
|
CloseCircleOutline,
|
||||||
|
} from 'antd-mobile-icons';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **包依赖**: 确保项目中已安装对应的图标包
|
||||||
|
2. **版本兼容**: 注意图标包版本与UI框架版本的兼容性
|
||||||
|
3. **性能考虑**: 按需导入图标,避免全量导入
|
||||||
|
4. **样式覆盖**: 可以通过CSS自定义图标颜色和大小
|
||||||
|
5. **无障碍**: 为图标添加适当的aria-label属性
|
||||||
|
|
||||||
|
## 更新记录
|
||||||
|
|
||||||
|
- 2024-01-XX: 初始版本,包含常用图标对照
|
||||||
|
- 后续根据实际使用情况持续更新
|
||||||
271
nkebao/ICON_QUICK_REFERENCE.md
Normal file
271
nkebao/ICON_QUICK_REFERENCE.md
Normal file
@@ -0,0 +1,271 @@
|
|||||||
|
# 图标快速参考表
|
||||||
|
|
||||||
|
## 快速查找
|
||||||
|
|
||||||
|
### 🔍 按功能查找
|
||||||
|
|
||||||
|
| 功能 | PC端 | 移动端 | 导入方式 |
|
||||||
|
|------|------|--------|----------|
|
||||||
|
| **添加** | PlusOutlined | AddOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **编辑** | EditOutlined | EditSOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **删除** | DeleteOutlined | DeleteOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **复制** | CopyOutlined | CopyOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **搜索** | SearchOutlined | SearchOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **刷新** | ReloadOutlined | RefreshOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **设置** | SettingOutlined | SettingOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **用户** | UserOutlined | UserOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **首页** | HomeOutlined | HomeOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **返回** | ArrowLeftOutlined | LeftOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **关闭** | CloseOutlined | CloseOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **确认** | CheckOutlined | CheckOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **成功** | CheckCircleOutlined | CheckCircleOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **错误** | CloseCircleOutlined | CloseCircleOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **警告** | ExclamationCircleOutlined | ExclamationCircleOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **信息** | InfoCircleOutlined | InfoCircleOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **加载** | LoadingOutlined | LoadingOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **向上** | UpOutlined | UpOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **向下** | DownOutlined | DownOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **向左** | LeftOutlined | LeftOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **向右** | RightOutlined | RightOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **消息** | MessageOutlined | MessageOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **电话** | PhoneOutlined | PhoneOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **日历** | CalendarOutlined | CalendarOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **时钟** | ClockCircleOutlined | ClockCircleOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **图片** | PictureOutlined | PictureOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **文件** | FileOutlined | FileOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **相机** | CameraOutlined | CameraOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **二维码** | QrcodeOutlined | QrCodeOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **微信** | WechatOutlined | WechatOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **设备** | MobileOutlined | MobileOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **团队** | TeamOutlined | TeamOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **订单** | ShoppingOutlined | ShoppingOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
| **支付** | PayCircleOutlined | PayCircleOutline | `@ant-design/icons` / `antd-mobile-icons` |
|
||||||
|
|
||||||
|
### 🔄 常见替换
|
||||||
|
|
||||||
|
| 原图标 | 替换为 | 说明 |
|
||||||
|
|--------|--------|------|
|
||||||
|
| RiseOutlined | UpOutline | 上升趋势 |
|
||||||
|
| ThumbsUpOutlined | LikeOutline | 点赞功能 |
|
||||||
|
| ShareAltOutlined | LinkOutline | 分享功能 |
|
||||||
|
| BarChartOutlined | PieOutline | 图表功能 |
|
||||||
|
| LineChartOutlined | PieOutline | 图表功能 |
|
||||||
|
| UserAddOutlined | UserOutline | 用户添加 |
|
||||||
|
| SettingOutline | SettingOutline | 设置(移动端) |
|
||||||
|
|
||||||
|
## 导入模板
|
||||||
|
|
||||||
|
### PC端项目模板
|
||||||
|
```typescript
|
||||||
|
import {
|
||||||
|
HomeOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
SettingOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
PlusOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
CopyOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
CloseOutlined,
|
||||||
|
CheckOutlined,
|
||||||
|
UpOutlined,
|
||||||
|
DownOutlined,
|
||||||
|
LeftOutlined,
|
||||||
|
RightOutlined,
|
||||||
|
MessageOutlined,
|
||||||
|
CalendarOutlined,
|
||||||
|
ClockCircleOutlined,
|
||||||
|
PictureOutlined,
|
||||||
|
FileOutlined,
|
||||||
|
CameraOutlined,
|
||||||
|
QrcodeOutlined,
|
||||||
|
WechatOutlined,
|
||||||
|
MobileOutlined,
|
||||||
|
TeamOutlined,
|
||||||
|
ShoppingOutlined,
|
||||||
|
PayCircleOutlined,
|
||||||
|
CheckCircleOutlined,
|
||||||
|
CloseCircleOutlined,
|
||||||
|
ExclamationCircleOutlined,
|
||||||
|
InfoCircleOutlined,
|
||||||
|
LoadingOutlined,
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
```
|
||||||
|
|
||||||
|
### 移动端项目模板
|
||||||
|
```typescript
|
||||||
|
import {
|
||||||
|
HomeOutline,
|
||||||
|
UserOutline,
|
||||||
|
SettingOutline,
|
||||||
|
SearchOutline,
|
||||||
|
AddOutline,
|
||||||
|
EditSOutline,
|
||||||
|
DeleteOutline,
|
||||||
|
CopyOutline,
|
||||||
|
RefreshOutline,
|
||||||
|
CloseOutline,
|
||||||
|
CheckOutline,
|
||||||
|
UpOutline,
|
||||||
|
DownOutline,
|
||||||
|
LeftOutline,
|
||||||
|
RightOutline,
|
||||||
|
MessageOutline,
|
||||||
|
CalendarOutline,
|
||||||
|
ClockCircleOutline,
|
||||||
|
PictureOutline,
|
||||||
|
FileOutline,
|
||||||
|
CameraOutline,
|
||||||
|
QrCodeOutline,
|
||||||
|
WechatOutline,
|
||||||
|
MobileOutline,
|
||||||
|
TeamOutline,
|
||||||
|
ShoppingOutline,
|
||||||
|
PayCircleOutline,
|
||||||
|
CheckCircleOutline,
|
||||||
|
CloseCircleOutline,
|
||||||
|
ExclamationCircleOutline,
|
||||||
|
InfoCircleOutline,
|
||||||
|
LoadingOutline,
|
||||||
|
} from 'antd-mobile-icons';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### 基础使用
|
||||||
|
```typescript
|
||||||
|
// PC端
|
||||||
|
import { HomeOutlined, UserOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
<HomeOutlined style={{ fontSize: 16, color: '#1890ff' }} />
|
||||||
|
<UserOutlined style={{ fontSize: 16, color: '#52c41a' }} />
|
||||||
|
|
||||||
|
// 移动端
|
||||||
|
import { HomeOutline, UserOutline } from 'antd-mobile-icons';
|
||||||
|
|
||||||
|
<HomeOutline style={{ fontSize: 16, color: '#1890ff' }} />
|
||||||
|
<UserOutline style={{ fontSize: 16, color: '#52c41a' }} />
|
||||||
|
```
|
||||||
|
|
||||||
|
### 按钮中使用
|
||||||
|
```typescript
|
||||||
|
// PC端
|
||||||
|
import { PlusOutlined, EditOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
<Button icon={<PlusOutlined />}>添加</Button>
|
||||||
|
<Button icon={<EditOutlined />}>编辑</Button>
|
||||||
|
|
||||||
|
// 移动端
|
||||||
|
import { AddOutline, EditSOutline } from 'antd-mobile-icons';
|
||||||
|
|
||||||
|
<Button>
|
||||||
|
<AddOutline /> 添加
|
||||||
|
</Button>
|
||||||
|
<Button>
|
||||||
|
<EditSOutline /> 编辑
|
||||||
|
</Button>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 列表中使用
|
||||||
|
```typescript
|
||||||
|
// PC端
|
||||||
|
import { DeleteOutlined, CopyOutlined } from '@ant-design/icons';
|
||||||
|
|
||||||
|
<Button icon={<DeleteOutlined />} danger>删除</Button>
|
||||||
|
<Button icon={<CopyOutlined />}>复制</Button>
|
||||||
|
|
||||||
|
// 移动端
|
||||||
|
import { DeleteOutline, CopyOutline } from 'antd-mobile-icons';
|
||||||
|
|
||||||
|
<Button color="danger">
|
||||||
|
<DeleteOutline /> 删除
|
||||||
|
</Button>
|
||||||
|
<Button>
|
||||||
|
<CopyOutline /> 复制
|
||||||
|
</Button>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常见错误
|
||||||
|
|
||||||
|
### ❌ 错误示例
|
||||||
|
```typescript
|
||||||
|
// 错误:混用PC端和移动端图标
|
||||||
|
import { HomeOutlined } from '@ant-design/icons'; // PC端
|
||||||
|
import { UserOutline } from 'antd-mobile-icons'; // 移动端
|
||||||
|
|
||||||
|
// 错误:使用不存在的图标
|
||||||
|
import { RiseOutlined } from 'antd-mobile-icons'; // 不存在
|
||||||
|
import { UserAddOutline } from 'antd-mobile-icons'; // 不存在
|
||||||
|
```
|
||||||
|
|
||||||
|
### ✅ 正确示例
|
||||||
|
```typescript
|
||||||
|
// 正确:统一使用移动端图标
|
||||||
|
import {
|
||||||
|
HomeOutline,
|
||||||
|
UserOutline,
|
||||||
|
UpOutline, // 替换RiseOutlined
|
||||||
|
UserOutline // 替换UserAddOutline
|
||||||
|
} from 'antd-mobile-icons';
|
||||||
|
|
||||||
|
// 正确:统一使用PC端图标
|
||||||
|
import {
|
||||||
|
HomeOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
RiseOutlined, // PC端存在
|
||||||
|
UserAddOutlined // PC端存在
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 快速检查清单
|
||||||
|
|
||||||
|
### 开发前检查
|
||||||
|
- [ ] 确认项目类型(PC端/移动端)
|
||||||
|
- [ ] 选择对应的图标包
|
||||||
|
- [ ] 检查图标是否存在
|
||||||
|
- [ ] 准备替换方案
|
||||||
|
|
||||||
|
### 开发中检查
|
||||||
|
- [ ] 使用正确的导入方式
|
||||||
|
- [ ] 图标名称大小写正确
|
||||||
|
- [ ] 避免混用不同包的图标
|
||||||
|
- [ ] 为图标添加适当的样式
|
||||||
|
|
||||||
|
### 开发后检查
|
||||||
|
- [ ] 图标显示正常
|
||||||
|
- [ ] 样式符合设计要求
|
||||||
|
- [ ] 无障碍属性完整
|
||||||
|
- [ ] 性能影响最小
|
||||||
|
|
||||||
|
## 紧急替换方案
|
||||||
|
|
||||||
|
当遇到图标不存在时,使用以下通用图标:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 移动端通用图标
|
||||||
|
import {
|
||||||
|
QuestionCircleOutline, // 通用问号
|
||||||
|
AppOutline, // 通用应用
|
||||||
|
ToolOutline, // 通用工具
|
||||||
|
SettingOutline, // 通用设置
|
||||||
|
UserOutline, // 通用用户
|
||||||
|
} from 'antd-mobile-icons';
|
||||||
|
|
||||||
|
// PC端通用图标
|
||||||
|
import {
|
||||||
|
QuestionCircleOutlined, // 通用问号
|
||||||
|
AppstoreOutlined, // 通用应用
|
||||||
|
ToolOutlined, // 通用工具
|
||||||
|
SettingOutlined, // 通用设置
|
||||||
|
UserOutlined, // 通用用户
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 更新日志
|
||||||
|
|
||||||
|
- 2024-01-XX: 初始版本
|
||||||
|
- 添加常用图标对照
|
||||||
|
- 添加错误示例和正确示例
|
||||||
|
- 添加快速检查清单
|
||||||
|
- 添加紧急替换方案
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# Nkebao Base
|
|
||||||
|
|
||||||
一个现代化的 React + TypeScript 前端项目模板,适用于中后台管理系统、移动端 H5 等场景。
|
|
||||||
集成了 Vite 极速构建、Zustand 轻量状态管理、模块化路由与权限拦截、axios 请求封装、移动端 rem 适配等主流工程能力。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 启动方式
|
|
||||||
|
|
||||||
```bash
|
|
||||||
yarn install
|
|
||||||
yarn dev
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 技术栈
|
|
||||||
|
|
||||||
- **React 18**:主流前端框架
|
|
||||||
- **TypeScript**:类型安全开发
|
|
||||||
- **Vite**:极速开发与构建工具
|
|
||||||
- **axios**:请求库,统一封装
|
|
||||||
- **sass**:CSS 预处理
|
|
||||||
- **React Router v6**:路由管理,支持模块化与权限拦截
|
|
||||||
- **antd / antd-mobile**:桌面端与移动端 UI 组件库
|
|
||||||
- **postcss-pxtorem**:rem 适配,移动端友好
|
|
||||||
- **Zustand**:轻量状态管理,支持持久化
|
|
||||||
- **ESLint + Prettier**:代码规范与自动格式化
|
|
||||||
- **路径别名 @**:简化 import
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 目录结构
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
api/ # axios 封装与接口模块
|
|
||||||
pages/ # 页面组件
|
|
||||||
styles/ # 全局样式
|
|
||||||
App.tsx # 入口组件
|
|
||||||
main.tsx # 入口文件
|
|
||||||
public/
|
|
||||||
index.html # HTML 模板
|
|
||||||
```
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
# 路由迁移总结
|
|
||||||
|
|
||||||
## 📋 迁移概述
|
|
||||||
|
|
||||||
已成功将旧项目 @Cunkebao 的路由结构迁移到新项目 @nkebao,并进行了合理的文件拆分和归类。
|
|
||||||
|
|
||||||
## 🗂️ 路由模块结构
|
|
||||||
|
|
||||||
### 1. 基础路由模块 (`auth.tsx`)
|
|
||||||
- `/login` - 登录页面
|
|
||||||
|
|
||||||
### 2. 设备管理模块 (`devices.tsx`)
|
|
||||||
- `/devices` - 设备列表
|
|
||||||
- `/devices/:id` - 设备详情
|
|
||||||
|
|
||||||
### 3. 微信号管理模块 (`wechat-accounts.tsx`)
|
|
||||||
- `/wechat-accounts` - 微信号列表
|
|
||||||
- `/wechat-accounts/:id` - 微信号详情
|
|
||||||
|
|
||||||
### 4. 工作台模块 (`workspace.tsx`)
|
|
||||||
- `/workspace` - 工作台主页
|
|
||||||
- `/workspace/auto-like` - 自动点赞
|
|
||||||
- `/workspace/auto-like/new` - 新建自动点赞
|
|
||||||
- `/workspace/auto-like/:id` - 自动点赞详情
|
|
||||||
- `/workspace/auto-like/:id/edit` - 编辑自动点赞
|
|
||||||
- `/workspace/auto-group` - 自动分组
|
|
||||||
- `/workspace/auto-group/:id` - 自动分组详情
|
|
||||||
- `/workspace/group-push` - 群发推送
|
|
||||||
- `/workspace/group-push/new` - 新建群发推送
|
|
||||||
- `/workspace/group-push/:id` - 群发推送详情
|
|
||||||
- `/workspace/group-push/:id/edit` - 编辑群发推送
|
|
||||||
- `/workspace/moments-sync` - 朋友圈同步
|
|
||||||
- `/workspace/moments-sync/new` - 新建朋友圈同步
|
|
||||||
- `/workspace/moments-sync/:id` - 朋友圈同步详情
|
|
||||||
- `/workspace/moments-sync/edit/:id` - 编辑朋友圈同步
|
|
||||||
- `/workspace/ai-assistant` - AI助手
|
|
||||||
- `/workspace/traffic-distribution` - 流量分发
|
|
||||||
- `/workspace/traffic-distribution/new` - 新建流量分发
|
|
||||||
- `/workspace/traffic-distribution/edit/:id` - 编辑流量分发
|
|
||||||
- `/workspace/traffic-distribution/:id` - 流量分发详情
|
|
||||||
|
|
||||||
### 5. 场景管理模块 (`scenarios.tsx`)
|
|
||||||
- `/scenarios` - 场景列表
|
|
||||||
- `/scenarios/new` - 新建场景
|
|
||||||
- `/scenarios/new/:scenarioId` - 新建场景(带场景ID)
|
|
||||||
- `/scenarios/edit/:planId` - 编辑场景
|
|
||||||
- `/scenarios/list/:scenarioId/:scenarioName` - 场景列表详情
|
|
||||||
|
|
||||||
### 6. 内容管理模块 (`content.tsx`)
|
|
||||||
- `/content` - 内容管理
|
|
||||||
- `/content/new` - 新建内容
|
|
||||||
- `/content/edit/:id` - 编辑内容
|
|
||||||
- `/content/materials/:id` - 素材管理
|
|
||||||
- `/content/materials/new/:id` - 新建素材
|
|
||||||
- `/content/materials/edit/:id/:materialId` - 编辑素材
|
|
||||||
|
|
||||||
### 7. 流量池模块 (`traffic-pool.tsx`)
|
|
||||||
- `/traffic-pool` - 流量池列表
|
|
||||||
- `/traffic-pool/:id` - 流量池详情
|
|
||||||
|
|
||||||
### 8. 其他功能模块 (`other.tsx`)
|
|
||||||
- `/profile` - 个人中心
|
|
||||||
- `/plans` - 计划管理
|
|
||||||
- `/plans/:planId` - 计划详情
|
|
||||||
- `/orders` - 订单管理
|
|
||||||
- `/contact-import` - 联系人导入
|
|
||||||
|
|
||||||
## 🔧 技术实现
|
|
||||||
|
|
||||||
### 路由配置
|
|
||||||
- 使用 React Router DOM v6
|
|
||||||
- 支持动态路由参数
|
|
||||||
- 统一的权限控制
|
|
||||||
- 模块化路由管理
|
|
||||||
|
|
||||||
### 权限控制
|
|
||||||
- 每个路由都配置了 `auth` 属性
|
|
||||||
- 支持角色权限控制
|
|
||||||
- 自动权限检查
|
|
||||||
|
|
||||||
### 组件结构
|
|
||||||
- 使用 Layout 组件统一布局
|
|
||||||
- 使用 MeauMobile 组件作为底部导航
|
|
||||||
- 创建了 PlaceholderPage 通用占位组件
|
|
||||||
|
|
||||||
## 📁 文件结构
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
├── router/
|
|
||||||
│ ├── module/
|
|
||||||
│ │ ├── index.tsx # 主路由文件
|
|
||||||
│ │ ├── auth.tsx # 认证路由
|
|
||||||
│ │ ├── devices.tsx # 设备管理路由
|
|
||||||
│ │ ├── wechat-accounts.tsx # 微信号管理路由
|
|
||||||
│ │ ├── workspace.tsx # 工作台路由
|
|
||||||
│ │ ├── scenarios.tsx # 场景管理路由
|
|
||||||
│ │ ├── content.tsx # 内容管理路由
|
|
||||||
│ │ ├── traffic-pool.tsx # 流量池路由
|
|
||||||
│ │ └── other.tsx # 其他功能路由
|
|
||||||
│ ├── index.tsx # 路由入口
|
|
||||||
│ ├── permissionRoute.tsx # 权限路由组件
|
|
||||||
│ └── config.ts # 路由配置
|
|
||||||
├── pages/
|
|
||||||
│ ├── login/ # 登录页面
|
|
||||||
│ ├── devices/ # 设备管理页面
|
|
||||||
│ ├── wechat-accounts/ # 微信号管理页面
|
|
||||||
│ ├── workspace/ # 工作台页面
|
|
||||||
│ ├── scenarios/ # 场景管理页面
|
|
||||||
│ ├── content/ # 内容管理页面
|
|
||||||
│ ├── traffic-pool/ # 流量池页面
|
|
||||||
│ ├── profile/ # 个人中心页面
|
|
||||||
│ ├── plans/ # 计划管理页面
|
|
||||||
│ ├── orders/ # 订单管理页面
|
|
||||||
│ └── contact-import/ # 联系人导入页面
|
|
||||||
└── components/
|
|
||||||
└── PlaceholderPage.tsx # 通用占位页面组件
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎯 主要特性
|
|
||||||
|
|
||||||
1. **模块化设计**: 按功能模块拆分路由,便于维护
|
|
||||||
2. **权限控制**: 统一的权限管理机制
|
|
||||||
3. **类型安全**: 完整的 TypeScript 类型定义
|
|
||||||
4. **响应式设计**: 适配移动端和桌面端
|
|
||||||
5. **代码复用**: 使用通用组件减少重复代码
|
|
||||||
|
|
||||||
## 🚀 下一步计划
|
|
||||||
|
|
||||||
1. 实现具体的页面功能
|
|
||||||
2. 完善权限控制逻辑
|
|
||||||
3. 添加路由懒加载
|
|
||||||
4. 优化页面性能
|
|
||||||
5. 添加路由守卫
|
|
||||||
|
|
||||||
## 📝 注意事项
|
|
||||||
|
|
||||||
- 所有页面目前都是占位页面,需要根据业务需求逐步实现
|
|
||||||
- 路由权限控制需要根据实际业务逻辑调整
|
|
||||||
- 建议按照模块优先级逐步开发页面功能
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
# 工作台页面迁移总结
|
|
||||||
|
|
||||||
## 📋 迁移概述
|
|
||||||
|
|
||||||
已成功将旧项目 @Cunkebao 的工作台页面迁移到新项目 @nkebao,并进行了技术栈转换和样式优化。
|
|
||||||
|
|
||||||
## 🔄 技术栈转换
|
|
||||||
|
|
||||||
### 旧项目技术栈
|
|
||||||
- React + TypeScript
|
|
||||||
- Lucide React 图标库
|
|
||||||
- Tailwind CSS
|
|
||||||
- React Router DOM v6
|
|
||||||
- 自定义UI组件库
|
|
||||||
|
|
||||||
### 新项目技术栈
|
|
||||||
- React + TypeScript
|
|
||||||
- Ant Design Mobile
|
|
||||||
- SCSS Modules
|
|
||||||
- React Router DOM v6
|
|
||||||
- 统一的Layout组件
|
|
||||||
|
|
||||||
## 📱 页面结构
|
|
||||||
|
|
||||||
### 主要功能模块
|
|
||||||
|
|
||||||
1. **任务统计卡片**
|
|
||||||
- 总任务数(带进度条)
|
|
||||||
- 今日任务(带活跃度指标)
|
|
||||||
|
|
||||||
2. **常用功能**
|
|
||||||
- 自动点赞(New标签)
|
|
||||||
- 朋友圈同步
|
|
||||||
- 群消息推送
|
|
||||||
- 自动建群
|
|
||||||
- 流量分发
|
|
||||||
- AI对话助手(New标签)
|
|
||||||
|
|
||||||
3. **AI智能助手**
|
|
||||||
- AI数据分析(New标签)
|
|
||||||
- AI策略优化(New标签)
|
|
||||||
- AI销售预测
|
|
||||||
|
|
||||||
## 🎨 设计优化
|
|
||||||
|
|
||||||
### 视觉改进
|
|
||||||
- 使用Ant Design Mobile组件,保持移动端一致性
|
|
||||||
- 采用SCSS Modules进行样式管理
|
|
||||||
- 统一的颜色主题和间距规范
|
|
||||||
- 响应式设计,适配不同屏幕尺寸
|
|
||||||
|
|
||||||
### 交互优化
|
|
||||||
- 卡片悬停效果
|
|
||||||
- 统一的图标样式和颜色
|
|
||||||
- 清晰的层级结构
|
|
||||||
- 流畅的页面跳转
|
|
||||||
|
|
||||||
## 📁 文件结构
|
|
||||||
|
|
||||||
```
|
|
||||||
src/pages/workspace/
|
|
||||||
├── Workspace.tsx # 工作台主页
|
|
||||||
├── Workspace.module.scss # 工作台样式
|
|
||||||
├── auto-like/ # 自动点赞模块
|
|
||||||
├── auto-group/ # 自动分组模块
|
|
||||||
├── group-push/ # 群发推送模块
|
|
||||||
├── moments-sync/ # 朋友圈同步模块
|
|
||||||
├── ai-assistant/ # AI助手模块
|
|
||||||
└── traffic-distribution/ # 流量分发模块
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔗 路由配置
|
|
||||||
|
|
||||||
### 主要路由
|
|
||||||
- `/workspace` - 工作台主页
|
|
||||||
- `/workspace/auto-like` - 自动点赞
|
|
||||||
- `/workspace/group-push` - 群发推送
|
|
||||||
- `/workspace/moments-sync` - 朋友圈同步
|
|
||||||
- `/workspace/auto-group` - 自动分组
|
|
||||||
- `/workspace/traffic-distribution` - 流量分发
|
|
||||||
- `/workspace/ai-assistant` - AI助手
|
|
||||||
|
|
||||||
### AI功能路由
|
|
||||||
- `/workspace/ai-analyzer` - AI数据分析
|
|
||||||
- `/workspace/ai-strategy` - AI策略优化
|
|
||||||
- `/workspace/ai-forecast` - AI销售预测
|
|
||||||
|
|
||||||
## 🎯 功能特性
|
|
||||||
|
|
||||||
### 数据展示
|
|
||||||
- 实时任务统计
|
|
||||||
- 进度条可视化
|
|
||||||
- 活跃度指标
|
|
||||||
- 功能分类展示
|
|
||||||
|
|
||||||
### 导航优化
|
|
||||||
- 底部导航集成
|
|
||||||
- 路由自动激活
|
|
||||||
- 页面跳转优化
|
|
||||||
- 返回按钮处理
|
|
||||||
|
|
||||||
### 用户体验
|
|
||||||
- 加载状态处理
|
|
||||||
- 错误边界处理
|
|
||||||
- 响应式布局
|
|
||||||
- 触摸友好设计
|
|
||||||
|
|
||||||
## 🔧 技术实现
|
|
||||||
|
|
||||||
### 组件化设计
|
|
||||||
- 使用Layout组件统一布局
|
|
||||||
- 复用PlaceholderPage组件
|
|
||||||
- 模块化的功能卡片
|
|
||||||
- 统一的样式规范
|
|
||||||
|
|
||||||
### 状态管理
|
|
||||||
- 模拟数据展示
|
|
||||||
- 路由状态同步
|
|
||||||
- 组件生命周期管理
|
|
||||||
- 性能优化
|
|
||||||
|
|
||||||
### 样式系统
|
|
||||||
- SCSS Modules避免样式冲突
|
|
||||||
- CSS变量统一主题
|
|
||||||
- 响应式断点设计
|
|
||||||
- 移动端优化
|
|
||||||
|
|
||||||
## 🚀 下一步计划
|
|
||||||
|
|
||||||
1. **数据集成**
|
|
||||||
- 连接真实API接口
|
|
||||||
- 实现数据获取和更新
|
|
||||||
- 添加加载状态
|
|
||||||
- 错误处理机制
|
|
||||||
|
|
||||||
2. **功能完善**
|
|
||||||
- 实现具体功能页面
|
|
||||||
- 添加表单验证
|
|
||||||
- 完善交互逻辑
|
|
||||||
- 优化用户体验
|
|
||||||
|
|
||||||
3. **性能优化**
|
|
||||||
- 路由懒加载
|
|
||||||
- 组件代码分割
|
|
||||||
- 图片资源优化
|
|
||||||
- 缓存策略
|
|
||||||
|
|
||||||
4. **测试完善**
|
|
||||||
- 单元测试
|
|
||||||
- 集成测试
|
|
||||||
- 端到端测试
|
|
||||||
- 性能测试
|
|
||||||
|
|
||||||
## 📝 注意事项
|
|
||||||
|
|
||||||
- 所有功能页面目前使用占位组件
|
|
||||||
- 需要根据实际业务需求调整数据展示
|
|
||||||
- 建议按照用户使用频率优先开发核心功能
|
|
||||||
- 注意保持与整体设计风格的一致性
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { SpinLoading } from "antd-mobile";
|
||||||
import styles from "./layout.module.scss";
|
import styles from "./layout.module.scss";
|
||||||
|
|
||||||
interface LayoutProps {
|
interface LayoutProps {
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
@@ -7,11 +9,25 @@ interface LayoutProps {
|
|||||||
footer?: React.ReactNode;
|
footer?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Layout: React.FC<LayoutProps> = ({ children, header, footer }) => {
|
const Layout: React.FC<LayoutProps> = ({
|
||||||
|
children,
|
||||||
|
header,
|
||||||
|
footer,
|
||||||
|
loading = false,
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
{header && <header>{header}</header>}
|
{header && <header>{header}</header>}
|
||||||
<main>{children}</main>
|
<main>
|
||||||
|
{loading ? (
|
||||||
|
<div className={styles.loadingContainer}>
|
||||||
|
<SpinLoading color="primary" style={{ fontSize: 32 }} />
|
||||||
|
<div className={styles.loadingText}>加载中...</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
)}
|
||||||
|
</main>
|
||||||
{footer && <footer>{footer}</footer>}
|
{footer && <footer>{footer}</footer>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,3 +9,20 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loadingContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 300px;
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loadingText {
|
||||||
|
margin-top: 16px;
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { NavBar, Button } from "antd-mobile";
|
import { NavBar, Button } from "antd-mobile";
|
||||||
import { AddOutline } from "antd-mobile-icons";
|
import { PlusOutlined } from "@ant-design/icons";
|
||||||
import Layout from "@/components/Layout/Layout";
|
import Layout from "@/components/Layout/Layout";
|
||||||
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ const PlaceholderPage: React.FC<PlaceholderPageProps> = ({
|
|||||||
right={
|
right={
|
||||||
showAddButton ? (
|
showAddButton ? (
|
||||||
<Button size="small" color="primary">
|
<Button size="small" color="primary">
|
||||||
<AddOutline />
|
<PlusOutlined />
|
||||||
<span style={{ marginLeft: 4, fontSize: 12 }}>
|
<span style={{ marginLeft: 4, fontSize: 12 }}>
|
||||||
{addButtonText}
|
{addButtonText}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
250
nkebao/src/pages/scenarios/ScenarioList.module.scss
Normal file
250
nkebao/src/pages/scenarios/ScenarioList.module.scss
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
.scenario-list-page {
|
||||||
|
padding: 16px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-plan-btn {
|
||||||
|
font-size: 14px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 60vh;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-card {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-info {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-name {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-desc {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-stats {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-bar {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.adm-input {
|
||||||
|
flex: 1;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-btn {
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-list {
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-item {
|
||||||
|
background: white;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-meta {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.plan-actions {
|
||||||
|
margin-top: 12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 60px 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
color: #999;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-first-btn {
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 24px;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-dialog {
|
||||||
|
background: white;
|
||||||
|
border-radius: 16px 16px 0 0;
|
||||||
|
padding: 20px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-content {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.api-item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adm-input {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-dialog {
|
||||||
|
background: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 20px;
|
||||||
|
width: 300px;
|
||||||
|
max-width: 90vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-loading {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 40px 20px;
|
||||||
|
gap: 16px;
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-image {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 200px;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-error {
|
||||||
|
text-align: center;
|
||||||
|
color: #ff4d4f;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 40px 20px;
|
||||||
|
}
|
||||||
@@ -1,8 +1,618 @@
|
|||||||
import React from "react";
|
import React, { useEffect, useState, useCallback } from "react";
|
||||||
import PlaceholderPage from "@/components/PlaceholderPage";
|
import { useParams, useNavigate, useSearchParams } from "react-router-dom";
|
||||||
|
import {
|
||||||
|
NavBar,
|
||||||
|
List,
|
||||||
|
Button,
|
||||||
|
Toast,
|
||||||
|
SpinLoading,
|
||||||
|
Dialog,
|
||||||
|
Input,
|
||||||
|
Popup,
|
||||||
|
Card,
|
||||||
|
Tag,
|
||||||
|
Space,
|
||||||
|
Divider,
|
||||||
|
} from "antd-mobile";
|
||||||
|
import {
|
||||||
|
PlusOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
CalendarOutlined,
|
||||||
|
CopyOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
EditOutlined,
|
||||||
|
SettingOutlined,
|
||||||
|
SearchOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
QrcodeOutlined,
|
||||||
|
UpOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
|
||||||
|
import Layout from "@/components/Layout/Layout";
|
||||||
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
|
import {
|
||||||
|
getPlanList,
|
||||||
|
getPlanDetail,
|
||||||
|
copyPlan,
|
||||||
|
deletePlan,
|
||||||
|
getWxMinAppCode,
|
||||||
|
} from "./api";
|
||||||
|
import style from "./ScenarioList.module.scss";
|
||||||
|
|
||||||
|
interface Task {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
status: number;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
enabled: boolean;
|
||||||
|
total_customers?: number;
|
||||||
|
today_customers?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ScenarioData {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
image: string;
|
||||||
|
description: string;
|
||||||
|
totalPlans: number;
|
||||||
|
totalCustomers: number;
|
||||||
|
todayCustomers: number;
|
||||||
|
growth: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiSettings {
|
||||||
|
apiKey: string;
|
||||||
|
webhookUrl: string;
|
||||||
|
taskId: string;
|
||||||
|
}
|
||||||
|
|
||||||
const ScenarioList: React.FC = () => {
|
const ScenarioList: React.FC = () => {
|
||||||
return <PlaceholderPage title="场景列表" />;
|
const { scenarioId, scenarioName } = useParams<{
|
||||||
|
scenarioId: string;
|
||||||
|
scenarioName: string;
|
||||||
|
}>();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const [scenario, setScenario] = useState<ScenarioData | null>(null);
|
||||||
|
const [tasks, setTasks] = useState<Task[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
const [showApiDialog, setShowApiDialog] = useState(false);
|
||||||
|
const [currentApiSettings, setCurrentApiSettings] = useState<ApiSettings>({
|
||||||
|
apiKey: "",
|
||||||
|
webhookUrl: "",
|
||||||
|
taskId: "",
|
||||||
|
});
|
||||||
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
|
const [loadingTasks, setLoadingTasks] = useState(false);
|
||||||
|
const [showQrDialog, setShowQrDialog] = useState(false);
|
||||||
|
const [qrLoading, setQrLoading] = useState(false);
|
||||||
|
const [qrImg, setQrImg] = useState("");
|
||||||
|
|
||||||
|
// 获取渠道中文名称
|
||||||
|
const getChannelName = (channel: string) => {
|
||||||
|
const channelMap: Record<string, string> = {
|
||||||
|
douyin: "抖音直播获客",
|
||||||
|
kuaishou: "快手直播获客",
|
||||||
|
xiaohongshu: "小红书种草获客",
|
||||||
|
weibo: "微博话题获客",
|
||||||
|
haibao: "海报扫码获客",
|
||||||
|
phone: "电话号码获客",
|
||||||
|
gongzhonghao: "公众号引流获客",
|
||||||
|
weixinqun: "微信群裂变获客",
|
||||||
|
payment: "付款码获客",
|
||||||
|
api: "API接口获客",
|
||||||
|
};
|
||||||
|
return channelMap[channel] || `${channel}获客`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取场景描述
|
||||||
|
const getScenarioDescription = (channel: string) => {
|
||||||
|
const descriptions: Record<string, string> = {
|
||||||
|
douyin: "通过抖音平台进行精准获客,利用短视频内容吸引目标用户",
|
||||||
|
xiaohongshu: "利用小红书平台进行内容营销获客,通过优质内容建立品牌形象",
|
||||||
|
gongzhonghao: "通过微信公众号进行获客,建立私域流量池",
|
||||||
|
haibao: "通过海报分享进行获客,快速传播品牌信息",
|
||||||
|
phone: "通过电话营销进行获客,直接与客户沟通",
|
||||||
|
weixinqun: "通过微信群进行获客,利用社交裂变效应",
|
||||||
|
payment: "通过付款码进行获客,便捷的支付方式",
|
||||||
|
api: "通过API接口进行获客,支持第三方系统集成",
|
||||||
|
};
|
||||||
|
return descriptions[channel] || "通过该平台进行获客";
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchScenarioData = async () => {
|
||||||
|
if (!scenarioId) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
setError("");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取计划列表
|
||||||
|
const response = await getPlanList(scenarioId, 1, 20);
|
||||||
|
|
||||||
|
// 设置计划列表
|
||||||
|
if (response && response.data && response.data.list) {
|
||||||
|
setTasks(response.data.list);
|
||||||
|
} else {
|
||||||
|
setTasks([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建场景数据
|
||||||
|
const scenarioData: ScenarioData = {
|
||||||
|
id: scenarioId,
|
||||||
|
name: scenarioName || "",
|
||||||
|
image: "",
|
||||||
|
description: getScenarioDescription(scenarioId),
|
||||||
|
totalPlans: response?.data?.list?.length || 0,
|
||||||
|
totalCustomers: 0,
|
||||||
|
todayCustomers: 0,
|
||||||
|
growth: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
setScenario(scenarioData);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取场景数据失败:", error);
|
||||||
|
// 即使API失败也要创建基本的场景数据
|
||||||
|
const scenarioData: ScenarioData = {
|
||||||
|
id: scenarioId,
|
||||||
|
name: getScenarioName(),
|
||||||
|
image: "",
|
||||||
|
description: getScenarioDescription(scenarioId),
|
||||||
|
totalPlans: 0,
|
||||||
|
totalCustomers: 0,
|
||||||
|
todayCustomers: 0,
|
||||||
|
growth: "",
|
||||||
|
};
|
||||||
|
setScenario(scenarioData);
|
||||||
|
setTasks([]);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchScenarioData();
|
||||||
|
}, [scenarioId]);
|
||||||
|
|
||||||
|
// 获取场景名称
|
||||||
|
const getScenarioName = useCallback(() => {
|
||||||
|
const urlName = searchParams.get("name");
|
||||||
|
if (urlName) {
|
||||||
|
return urlName;
|
||||||
|
}
|
||||||
|
return getChannelName(scenarioId || "");
|
||||||
|
}, [searchParams, scenarioId]);
|
||||||
|
|
||||||
|
// 更新场景数据中的名称
|
||||||
|
useEffect(() => {
|
||||||
|
setScenario((prev) =>
|
||||||
|
prev
|
||||||
|
? {
|
||||||
|
...prev,
|
||||||
|
name: (() => {
|
||||||
|
const urlName = searchParams.get("name");
|
||||||
|
if (urlName) return urlName;
|
||||||
|
return getChannelName(scenarioId || "");
|
||||||
|
})(),
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
}, [searchParams, scenarioId]);
|
||||||
|
|
||||||
|
const handleCopyPlan = async (taskId: string) => {
|
||||||
|
const taskToCopy = tasks.find((task) => task.id === taskId);
|
||||||
|
if (!taskToCopy) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await copyPlan(taskId);
|
||||||
|
if (response && response.code === 200) {
|
||||||
|
Toast.show({
|
||||||
|
content: `已成功复制"${taskToCopy.name}"`,
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
// 刷新列表
|
||||||
|
handleRefresh();
|
||||||
|
} else {
|
||||||
|
Toast.show({
|
||||||
|
content: response?.msg || "复制失败",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Toast.show({
|
||||||
|
content: "复制失败,请重试",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeletePlan = async (taskId: string) => {
|
||||||
|
const taskToDelete = tasks.find((task) => task.id === taskId);
|
||||||
|
if (!taskToDelete) return;
|
||||||
|
|
||||||
|
const result = await Dialog.confirm({
|
||||||
|
content: `确定要删除"${taskToDelete.name}"吗?`,
|
||||||
|
confirmText: "删除",
|
||||||
|
cancelText: "取消",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
try {
|
||||||
|
const response = await deletePlan(taskId);
|
||||||
|
if (response && response.code === 200) {
|
||||||
|
Toast.show({
|
||||||
|
content: "计划已删除",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
// 刷新列表
|
||||||
|
handleRefresh();
|
||||||
|
} else {
|
||||||
|
Toast.show({
|
||||||
|
content: response?.msg || "删除失败",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Toast.show({
|
||||||
|
content: "删除失败,请重试",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpenApiSettings = async (taskId: string) => {
|
||||||
|
try {
|
||||||
|
const response = await getPlanDetail(taskId);
|
||||||
|
if (response && response.code === 200 && response.data) {
|
||||||
|
const detail = response.data;
|
||||||
|
setCurrentApiSettings({
|
||||||
|
apiKey: detail.apiKey || "",
|
||||||
|
webhookUrl: detail.webhookUrl || "",
|
||||||
|
taskId: taskId,
|
||||||
|
});
|
||||||
|
setShowApiDialog(true);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Toast.show({
|
||||||
|
content: "获取API设置失败",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCopyApiUrl = (url: string) => {
|
||||||
|
navigator.clipboard.writeText(url);
|
||||||
|
Toast.show({
|
||||||
|
content: "API地址已复制到剪贴板",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCreateNewPlan = () => {
|
||||||
|
navigate(`/scenarios/new/${scenarioId}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleShowQrCode = async (taskId: string) => {
|
||||||
|
setQrLoading(true);
|
||||||
|
setShowQrDialog(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await getWxMinAppCode(taskId);
|
||||||
|
if (response && response.code === 200 && response.data) {
|
||||||
|
setQrImg(response.data.qrCode || "");
|
||||||
|
} else {
|
||||||
|
Toast.show({
|
||||||
|
content: "获取二维码失败",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Toast.show({
|
||||||
|
content: "获取二维码失败",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setQrLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusColor = (status: number) => {
|
||||||
|
switch (status) {
|
||||||
|
case 1:
|
||||||
|
return "success";
|
||||||
|
case 0:
|
||||||
|
return "default";
|
||||||
|
case -1:
|
||||||
|
return "danger";
|
||||||
|
default:
|
||||||
|
return "default";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusText = (status: number) => {
|
||||||
|
switch (status) {
|
||||||
|
case 1:
|
||||||
|
return "运行中";
|
||||||
|
case 0:
|
||||||
|
return "已暂停";
|
||||||
|
case -1:
|
||||||
|
return "已停止";
|
||||||
|
default:
|
||||||
|
return "未知";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRefresh = async () => {
|
||||||
|
setLoadingTasks(true);
|
||||||
|
try {
|
||||||
|
const response = await getPlanList(scenarioId!, 1, 20);
|
||||||
|
if (response && response.data && response.data.list) {
|
||||||
|
setTasks(response.data.list);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Toast.show({
|
||||||
|
content: "刷新失败",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setLoadingTasks(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const filteredTasks = tasks.filter((task) =>
|
||||||
|
task.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<Layout
|
||||||
|
header={
|
||||||
|
<NavBar back={null} style={{ background: "#fff" }}>
|
||||||
|
<div className={style["nav-title"]}>场景详情</div>
|
||||||
|
</NavBar>
|
||||||
|
}
|
||||||
|
footer={<MeauMobile />}
|
||||||
|
>
|
||||||
|
<div className={style["loading"]}>
|
||||||
|
<SpinLoading color="primary" style={{ fontSize: 32 }} />
|
||||||
|
<div className={style["loading-text"]}>加载场景数据中...</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout
|
||||||
|
header={
|
||||||
|
<NavBar
|
||||||
|
back={null}
|
||||||
|
style={{ background: "#fff" }}
|
||||||
|
left={<div className={style["nav-title"]}>{scenario?.name}</div>}
|
||||||
|
right={
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleCreateNewPlan}
|
||||||
|
className={style["new-plan-btn"]}
|
||||||
|
>
|
||||||
|
<PlusOutlined /> 新建计划
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
footer={<MeauMobile />}
|
||||||
|
>
|
||||||
|
<div className={style["scenario-list-page"]}>
|
||||||
|
{/* 场景信息卡片 */}
|
||||||
|
{scenario && (
|
||||||
|
<Card className={style["scenario-card"]}>
|
||||||
|
<div className={style["scenario-info"]}>
|
||||||
|
<div className={style["scenario-name"]}>{scenario.name}</div>
|
||||||
|
<div className={style["scenario-desc"]}>
|
||||||
|
{scenario.description}
|
||||||
|
</div>
|
||||||
|
<div className={style["scenario-stats"]}>
|
||||||
|
<div className={style["stat-item"]}>
|
||||||
|
<UserOutlined />
|
||||||
|
<span>计划数: {scenario.totalPlans}</span>
|
||||||
|
</div>
|
||||||
|
<div className={style["stat-item"]}>
|
||||||
|
<CalendarOutlined />
|
||||||
|
<span>今日获客: {scenario.todayCustomers}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 搜索栏 */}
|
||||||
|
<div className={style["search-bar"]}>
|
||||||
|
<div className={style["search-input-wrapper"]}>
|
||||||
|
<SearchOutlined className={style["search-icon"]} />
|
||||||
|
<Input
|
||||||
|
placeholder="搜索计划名称"
|
||||||
|
value={searchTerm}
|
||||||
|
onChange={setSearchTerm}
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
onClick={handleRefresh}
|
||||||
|
loading={loadingTasks}
|
||||||
|
className={style["refresh-btn"]}
|
||||||
|
>
|
||||||
|
<ReloadOutlined />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 计划列表 */}
|
||||||
|
<List className={style["plan-list"]}>
|
||||||
|
{filteredTasks.length === 0 ? (
|
||||||
|
<div className={style["empty-state"]}>
|
||||||
|
<div className={style["empty-text"]}>
|
||||||
|
{searchTerm ? "没有找到匹配的计划" : "暂无计划"}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
onClick={handleCreateNewPlan}
|
||||||
|
className={style["create-first-btn"]}
|
||||||
|
>
|
||||||
|
<PlusOutlined /> 创建第一个计划
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
filteredTasks.map((task) => (
|
||||||
|
<List.Item
|
||||||
|
key={task.id}
|
||||||
|
className={style["plan-item"]}
|
||||||
|
onClick={() => navigate(`/scenarios/edit/${task.id}`)}
|
||||||
|
>
|
||||||
|
<div className={style["plan-content"]}>
|
||||||
|
<div className={style["plan-header"]}>
|
||||||
|
<div className={style["plan-name"]}>{task.name}</div>
|
||||||
|
<Tag color={getStatusColor(task.status)}>
|
||||||
|
{getStatusText(task.status)}
|
||||||
|
</Tag>
|
||||||
|
</div>
|
||||||
|
<div className={style["plan-meta"]}>
|
||||||
|
<span>创建时间: {task.created_at}</span>
|
||||||
|
{task.total_customers !== undefined && (
|
||||||
|
<span>总获客: {task.total_customers}</span>
|
||||||
|
)}
|
||||||
|
{task.today_customers !== undefined && (
|
||||||
|
<span>今日获客: {task.today_customers}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={style["plan-actions"]}>
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
size="mini"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleCopyPlan(task.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CopyOutlined />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="mini"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleShowQrCode(task.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<QrcodeOutlined />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="mini"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleOpenApiSettings(task.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SettingOutlined />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="mini"
|
||||||
|
color="danger"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleDeletePlan(task.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DeleteOutlined />
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</List.Item>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</List>
|
||||||
|
|
||||||
|
{/* API设置弹窗 */}
|
||||||
|
<Popup
|
||||||
|
visible={showApiDialog}
|
||||||
|
onMaskClick={() => setShowApiDialog(false)}
|
||||||
|
position="bottom"
|
||||||
|
bodyStyle={{ height: "60vh" }}
|
||||||
|
>
|
||||||
|
<div className={style["api-dialog"]}>
|
||||||
|
<div className={style["dialog-header"]}>
|
||||||
|
<h3>API设置</h3>
|
||||||
|
<Button size="small" onClick={() => setShowApiDialog(false)}>
|
||||||
|
关闭
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className={style["dialog-content"]}>
|
||||||
|
<div className={style["api-item"]}>
|
||||||
|
<label>API Key:</label>
|
||||||
|
<div className={style["input-with-button"]}>
|
||||||
|
<Input value={currentApiSettings.apiKey} readOnly />
|
||||||
|
<Button
|
||||||
|
size="mini"
|
||||||
|
onClick={() => handleCopyApiUrl(currentApiSettings.apiKey)}
|
||||||
|
>
|
||||||
|
复制
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={style["api-item"]}>
|
||||||
|
<label>Webhook URL:</label>
|
||||||
|
<div className={style["input-with-button"]}>
|
||||||
|
<Input value={currentApiSettings.webhookUrl} readOnly />
|
||||||
|
<Button
|
||||||
|
size="mini"
|
||||||
|
onClick={() =>
|
||||||
|
handleCopyApiUrl(currentApiSettings.webhookUrl)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
复制
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Popup>
|
||||||
|
|
||||||
|
{/* 二维码弹窗 */}
|
||||||
|
<Popup
|
||||||
|
visible={showQrDialog}
|
||||||
|
onMaskClick={() => setShowQrDialog(false)}
|
||||||
|
position="bottom"
|
||||||
|
>
|
||||||
|
<div className={style["qr-dialog"]}>
|
||||||
|
<div className={style["dialog-header"]}>
|
||||||
|
<h3>小程序二维码</h3>
|
||||||
|
<Button size="small" onClick={() => setShowQrDialog(false)}>
|
||||||
|
关闭
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className={style["dialog-content"]}>
|
||||||
|
{qrLoading ? (
|
||||||
|
<div className={style["qr-loading"]}>
|
||||||
|
<SpinLoading color="primary" />
|
||||||
|
<div>生成二维码中...</div>
|
||||||
|
</div>
|
||||||
|
) : qrImg ? (
|
||||||
|
<img
|
||||||
|
src={qrImg}
|
||||||
|
alt="小程序二维码"
|
||||||
|
className={style["qr-image"]}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className={style["qr-error"]}>二维码生成失败</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Popup>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ScenarioList;
|
export default ScenarioList;
|
||||||
|
|||||||
51
nkebao/src/pages/scenarios/api.ts
Normal file
51
nkebao/src/pages/scenarios/api.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import request from "@/api/request";
|
||||||
|
|
||||||
|
// 获取计划列表
|
||||||
|
export const getPlanList = async (scenarioId: string, page: number = 1, limit: number = 20) => {
|
||||||
|
return request(`/api/scenarios/${scenarioId}/plans`, { page, limit }, 'GET');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取计划详情
|
||||||
|
export const getPlanDetail = async (planId: string) => {
|
||||||
|
return request(`/api/scenarios/plans/${planId}`, undefined, 'GET');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 复制计划
|
||||||
|
export const copyPlan = async (planId: string) => {
|
||||||
|
return request(`/api/scenarios/plans/${planId}/copy`, undefined, 'POST');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 删除计划
|
||||||
|
export const deletePlan = async (planId: string) => {
|
||||||
|
return request(`/api/scenarios/plans/${planId}`, undefined, 'DELETE');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 创建计划
|
||||||
|
export const createPlan = async (data: any) => {
|
||||||
|
return request('/api/scenarios/plans', data, 'POST');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新计划
|
||||||
|
export const updatePlan = async (planId: string, data: any) => {
|
||||||
|
return request(`/api/scenarios/plans/${planId}`, data, 'PUT');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取小程序二维码
|
||||||
|
export const getWxMinAppCode = async (planId: string) => {
|
||||||
|
return request(`/api/scenarios/plans/${planId}/qrcode`, undefined, 'GET');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取场景类型列表
|
||||||
|
export const getScenarioTypes = async () => {
|
||||||
|
return request('/api/scenarios/types', undefined, 'GET');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取设备列表
|
||||||
|
export const getDevices = async () => {
|
||||||
|
return request('/api/devices', undefined, 'GET');
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取海报列表
|
||||||
|
export const getPosters = async () => {
|
||||||
|
return request('/api/posters', undefined, 'GET');
|
||||||
|
};
|
||||||
43
nkebao/src/pages/scenarios/new/page.module.scss
Normal file
43
nkebao/src/pages/scenarios/new/page.module.scss
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
.new-plan-page {
|
||||||
|
background: #f5f5f5;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
height: 32px;
|
||||||
|
width: 32px;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 60vh;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.steps-container {
|
||||||
|
background: white;
|
||||||
|
padding: 20px 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
@@ -1,8 +1,293 @@
|
|||||||
import React from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import PlaceholderPage from "@/components/PlaceholderPage";
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
import { NavBar, Button, Toast, SpinLoading, Steps, Popup } from "antd-mobile";
|
||||||
|
import { LeftOutline } from "antd-mobile-icons";
|
||||||
|
import Layout from "@/components/Layout/Layout";
|
||||||
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
|
import BasicSettings from "./steps/BasicSettings";
|
||||||
|
import FriendRequestSettings from "./steps/FriendRequestSettings";
|
||||||
|
import MessageSettings from "./steps/MessageSettings";
|
||||||
|
import {
|
||||||
|
getScenarioTypes,
|
||||||
|
createPlan,
|
||||||
|
updatePlan,
|
||||||
|
getPlanDetail,
|
||||||
|
} from "../api";
|
||||||
|
import style from "./page.module.scss";
|
||||||
|
|
||||||
|
// 步骤定义
|
||||||
|
const steps = [
|
||||||
|
{ id: 1, title: "步骤一", subtitle: "基础设置" },
|
||||||
|
{ id: 2, title: "步骤二", subtitle: "好友申请设置" },
|
||||||
|
{ id: 3, title: "步骤三", subtitle: "消息设置" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 类型定义
|
||||||
|
interface FormData {
|
||||||
|
name: string;
|
||||||
|
scenario: number;
|
||||||
|
posters: any[];
|
||||||
|
device: string[];
|
||||||
|
remarkType: string;
|
||||||
|
greeting: string;
|
||||||
|
addInterval: number;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
enabled: boolean;
|
||||||
|
sceneId: string | number;
|
||||||
|
remarkFormat: string;
|
||||||
|
addFriendInterval: number;
|
||||||
|
}
|
||||||
|
|
||||||
const NewPlan: React.FC = () => {
|
const NewPlan: React.FC = () => {
|
||||||
return <PlaceholderPage title="新建场景" />;
|
const navigate = useNavigate();
|
||||||
|
const [currentStep, setCurrentStep] = useState(1);
|
||||||
|
const [formData, setFormData] = useState<FormData>({
|
||||||
|
name: "",
|
||||||
|
scenario: 1,
|
||||||
|
posters: [],
|
||||||
|
device: [],
|
||||||
|
remarkType: "phone",
|
||||||
|
greeting: "你好,请通过",
|
||||||
|
addInterval: 1,
|
||||||
|
startTime: "09:00",
|
||||||
|
endTime: "18:00",
|
||||||
|
enabled: true,
|
||||||
|
sceneId: "",
|
||||||
|
remarkFormat: "",
|
||||||
|
addFriendInterval: 1,
|
||||||
|
});
|
||||||
|
const [sceneList, setSceneList] = useState<any[]>([]);
|
||||||
|
const [sceneLoading, setSceneLoading] = useState(true);
|
||||||
|
const { scenarioId, planId } = useParams<{
|
||||||
|
scenarioId: string;
|
||||||
|
planId: string;
|
||||||
|
}>();
|
||||||
|
const [isEdit, setIsEdit] = useState(false);
|
||||||
|
const [saving, setSaving] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
|
setSceneLoading(true);
|
||||||
|
try {
|
||||||
|
// 获取场景类型
|
||||||
|
const res = await getScenarioTypes();
|
||||||
|
if (res?.data) {
|
||||||
|
setSceneList(res.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (planId) {
|
||||||
|
setIsEdit(true);
|
||||||
|
// 获取计划详情
|
||||||
|
const detailRes = await getPlanDetail(planId);
|
||||||
|
if (detailRes.code === 200 && detailRes.data) {
|
||||||
|
const detail = detailRes.data;
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
name: detail.name ?? "",
|
||||||
|
scenario: Number(detail.scenario) || 1,
|
||||||
|
posters: detail.posters ?? [],
|
||||||
|
device: detail.device ?? [],
|
||||||
|
remarkType: detail.remarkType ?? "phone",
|
||||||
|
greeting: detail.greeting ?? "",
|
||||||
|
addInterval: detail.addInterval ?? 1,
|
||||||
|
startTime: detail.startTime ?? "09:00",
|
||||||
|
endTime: detail.endTime ?? "18:00",
|
||||||
|
enabled: detail.enabled ?? true,
|
||||||
|
sceneId: Number(detail.scenario) || 1,
|
||||||
|
remarkFormat: detail.remarkFormat ?? "",
|
||||||
|
addFriendInterval: detail.addFriendInterval ?? 1,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} else if (scenarioId) {
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
scenario: Number(scenarioId) || 1,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Toast.show({
|
||||||
|
content: "加载数据失败",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setSceneLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 更新表单数据
|
||||||
|
const onChange = (data: any) => {
|
||||||
|
setFormData((prev) => ({ ...prev, ...data }));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理保存
|
||||||
|
const handleSave = async () => {
|
||||||
|
if (!formData.name.trim()) {
|
||||||
|
Toast.show({
|
||||||
|
content: "请输入计划名称",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSaving(true);
|
||||||
|
try {
|
||||||
|
let result;
|
||||||
|
if (isEdit && planId) {
|
||||||
|
// 编辑
|
||||||
|
const editData = {
|
||||||
|
...formData,
|
||||||
|
id: Number(planId),
|
||||||
|
planId: Number(planId),
|
||||||
|
};
|
||||||
|
result = await updatePlan(planId, editData);
|
||||||
|
} else {
|
||||||
|
// 新建
|
||||||
|
result = await createPlan(formData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.code === 200) {
|
||||||
|
Toast.show({
|
||||||
|
content: isEdit ? "计划已更新" : "获客计划已创建",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
const sceneItem = sceneList.find((v) => formData.scenario === v.id);
|
||||||
|
navigate(
|
||||||
|
`/scenarios/list/${formData.sceneId}/${sceneItem?.name || ""}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Toast.show({
|
||||||
|
content: result.msg || "操作失败",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Toast.show({
|
||||||
|
content: isEdit ? "更新计划失败,请重试" : "创建计划失败,请重试",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 下一步
|
||||||
|
const handleNext = () => {
|
||||||
|
if (currentStep === steps.length) {
|
||||||
|
handleSave();
|
||||||
|
} else {
|
||||||
|
setCurrentStep((prev) => prev + 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 上一步
|
||||||
|
const handlePrev = () => {
|
||||||
|
setCurrentStep((prev) => Math.max(prev - 1, 1));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 渲染当前步骤内容
|
||||||
|
const renderStepContent = () => {
|
||||||
|
switch (currentStep) {
|
||||||
|
case 1:
|
||||||
|
return (
|
||||||
|
<BasicSettings
|
||||||
|
formData={formData}
|
||||||
|
onChange={onChange}
|
||||||
|
onNext={handleNext}
|
||||||
|
sceneList={sceneList}
|
||||||
|
sceneLoading={sceneLoading}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 2:
|
||||||
|
return (
|
||||||
|
<FriendRequestSettings
|
||||||
|
formData={formData}
|
||||||
|
onChange={onChange}
|
||||||
|
onNext={handleNext}
|
||||||
|
onPrev={handlePrev}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 3:
|
||||||
|
return (
|
||||||
|
<MessageSettings
|
||||||
|
formData={formData}
|
||||||
|
onChange={onChange}
|
||||||
|
onNext={handleNext}
|
||||||
|
onPrev={handlePrev}
|
||||||
|
saving={saving}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (sceneLoading) {
|
||||||
|
return (
|
||||||
|
<Layout
|
||||||
|
header={
|
||||||
|
<NavBar back={null} style={{ background: "#fff" }}>
|
||||||
|
<div className={style["nav-title"]}>
|
||||||
|
{isEdit ? "编辑计划" : "新建计划"}
|
||||||
|
</div>
|
||||||
|
</NavBar>
|
||||||
|
}
|
||||||
|
footer={<MeauMobile />}
|
||||||
|
>
|
||||||
|
<div className={style["loading"]}>
|
||||||
|
<SpinLoading color="primary" style={{ fontSize: 32 }} />
|
||||||
|
<div className={style["loading-text"]}>加载数据中...</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout
|
||||||
|
header={
|
||||||
|
<NavBar
|
||||||
|
back={null}
|
||||||
|
style={{ background: "#fff" }}
|
||||||
|
left={
|
||||||
|
<div className={style["nav-title"]}>
|
||||||
|
{isEdit ? "编辑计划" : "新建计划"}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
right={
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
onClick={() => navigate(-1)}
|
||||||
|
className={style["back-btn"]}
|
||||||
|
>
|
||||||
|
<LeftOutline />
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
footer={<MeauMobile />}
|
||||||
|
>
|
||||||
|
<div className={style["new-plan-page"]}>
|
||||||
|
{/* 步骤指示器 */}
|
||||||
|
<div className={style["steps-container"]}>
|
||||||
|
<Steps current={currentStep - 1}>
|
||||||
|
{steps.map((step) => (
|
||||||
|
<Steps.Step
|
||||||
|
key={step.id}
|
||||||
|
title={step.title}
|
||||||
|
description={step.subtitle}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Steps>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 步骤内容 */}
|
||||||
|
<div className={style["step-content"]}>{renderStepContent()}</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NewPlan;
|
export default NewPlan;
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
.basic-settings {
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adm-form-item-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adm-input {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adm-selector {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-input {
|
||||||
|
width: 120px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 40vh;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.next-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
210
nkebao/src/pages/scenarios/new/steps/BasicSettings.tsx
Normal file
210
nkebao/src/pages/scenarios/new/steps/BasicSettings.tsx
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
Input,
|
||||||
|
Selector,
|
||||||
|
Button,
|
||||||
|
SpinLoading,
|
||||||
|
Toast,
|
||||||
|
Card,
|
||||||
|
Space,
|
||||||
|
} from "antd-mobile";
|
||||||
|
import { getDevices, getPosters } from "../../api";
|
||||||
|
import style from "./BasicSettings.module.scss";
|
||||||
|
|
||||||
|
interface BasicSettingsProps {
|
||||||
|
formData: any;
|
||||||
|
onChange: (data: any) => void;
|
||||||
|
onNext: () => void;
|
||||||
|
sceneList: any[];
|
||||||
|
sceneLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BasicSettings: React.FC<BasicSettingsProps> = ({
|
||||||
|
formData,
|
||||||
|
onChange,
|
||||||
|
onNext,
|
||||||
|
sceneList,
|
||||||
|
sceneLoading,
|
||||||
|
}) => {
|
||||||
|
const [devices, setDevices] = useState<any[]>([]);
|
||||||
|
const [posters, setPosters] = useState<any[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
// 获取设备列表
|
||||||
|
const devicesRes = await getDevices();
|
||||||
|
if (devicesRes?.data) {
|
||||||
|
setDevices(devicesRes.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取海报列表
|
||||||
|
const postersRes = await getPosters();
|
||||||
|
if (postersRes?.data) {
|
||||||
|
setPosters(postersRes.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Toast.show({
|
||||||
|
content: "加载数据失败",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNext = () => {
|
||||||
|
if (!formData.name.trim()) {
|
||||||
|
Toast.show({
|
||||||
|
content: "请输入计划名称",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!formData.scenario) {
|
||||||
|
Toast.show({
|
||||||
|
content: "请选择场景类型",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formData.device.length === 0) {
|
||||||
|
Toast.show({
|
||||||
|
content: "请选择设备",
|
||||||
|
position: "top",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onNext();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (loading || sceneLoading) {
|
||||||
|
return (
|
||||||
|
<div className={style["loading"]}>
|
||||||
|
<SpinLoading color="primary" style={{ fontSize: 32 }} />
|
||||||
|
<div className={style["loading-text"]}>加载数据中...</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={style["basic-settings"]}>
|
||||||
|
<Card className={style["form-card"]}>
|
||||||
|
<Form layout="vertical">
|
||||||
|
{/* 计划名称 */}
|
||||||
|
<Form.Item label="计划名称" required className={style["form-item"]}>
|
||||||
|
<Input
|
||||||
|
placeholder="请输入计划名称"
|
||||||
|
value={formData.name}
|
||||||
|
onChange={(value) => onChange({ name: value })}
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{/* 场景类型 */}
|
||||||
|
<Form.Item label="场景类型" required className={style["form-item"]}>
|
||||||
|
<Selector
|
||||||
|
options={sceneList.map((scene) => ({
|
||||||
|
label: scene.name,
|
||||||
|
value: scene.id,
|
||||||
|
}))}
|
||||||
|
value={[formData.scenario]}
|
||||||
|
onChange={(value) => {
|
||||||
|
const selectedScene = sceneList.find(
|
||||||
|
(scene) => scene.id === value[0]
|
||||||
|
);
|
||||||
|
onChange({
|
||||||
|
scenario: value[0],
|
||||||
|
sceneId: value[0],
|
||||||
|
name: selectedScene?.name || "",
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{/* 选择设备 */}
|
||||||
|
<Form.Item label="选择设备" required className={style["form-item"]}>
|
||||||
|
<Selector
|
||||||
|
options={devices.map((device) => ({
|
||||||
|
label: device.name,
|
||||||
|
value: device.id,
|
||||||
|
}))}
|
||||||
|
value={formData.device}
|
||||||
|
onChange={(value) => onChange({ device: value })}
|
||||||
|
multiple
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{/* 选择海报 */}
|
||||||
|
<Form.Item label="选择海报" className={style["form-item"]}>
|
||||||
|
<Selector
|
||||||
|
options={posters.map((poster) => ({
|
||||||
|
label: poster.name,
|
||||||
|
value: poster.id,
|
||||||
|
}))}
|
||||||
|
value={formData.posters}
|
||||||
|
onChange={(value) => onChange({ posters: value })}
|
||||||
|
multiple
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{/* 工作时间 */}
|
||||||
|
<Form.Item label="工作时间" className={style["form-item"]}>
|
||||||
|
<Space>
|
||||||
|
<Input
|
||||||
|
type="time"
|
||||||
|
value={formData.startTime}
|
||||||
|
onChange={(value) => onChange({ startTime: value })}
|
||||||
|
className={style["time-input"]}
|
||||||
|
/>
|
||||||
|
<span>至</span>
|
||||||
|
<Input
|
||||||
|
type="time"
|
||||||
|
value={formData.endTime}
|
||||||
|
onChange={(value) => onChange({ endTime: value })}
|
||||||
|
className={style["time-input"]}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{/* 添加间隔 */}
|
||||||
|
<Form.Item label="添加间隔(分钟)" className={style["form-item"]}>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入添加间隔"
|
||||||
|
value={formData.addInterval.toString()}
|
||||||
|
onChange={(value) =>
|
||||||
|
onChange({ addInterval: Number(value) || 1 })
|
||||||
|
}
|
||||||
|
min={1}
|
||||||
|
max={60}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 操作按钮 */}
|
||||||
|
<div className={style["actions"]}>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
size="large"
|
||||||
|
onClick={handleNext}
|
||||||
|
className={style["next-btn"]}
|
||||||
|
>
|
||||||
|
下一步
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BasicSettings;
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
.friend-request-settings {
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adm-form-item-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adm-input {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adm-selector {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adm-text-area {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prev-btn {
|
||||||
|
flex: 1;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.next-btn {
|
||||||
|
flex: 1;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
113
nkebao/src/pages/scenarios/new/steps/FriendRequestSettings.tsx
Normal file
113
nkebao/src/pages/scenarios/new/steps/FriendRequestSettings.tsx
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
Input,
|
||||||
|
Selector,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Space,
|
||||||
|
TextArea,
|
||||||
|
} from "antd-mobile";
|
||||||
|
import style from "./FriendRequestSettings.module.scss";
|
||||||
|
|
||||||
|
interface FriendRequestSettingsProps {
|
||||||
|
formData: any;
|
||||||
|
onChange: (data: any) => void;
|
||||||
|
onNext: () => void;
|
||||||
|
onPrev: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FriendRequestSettings: React.FC<FriendRequestSettingsProps> = ({
|
||||||
|
formData,
|
||||||
|
onChange,
|
||||||
|
onNext,
|
||||||
|
onPrev,
|
||||||
|
}) => {
|
||||||
|
const remarkTypeOptions = [
|
||||||
|
{ label: "手机号", value: "phone" },
|
||||||
|
{ label: "微信号", value: "wechat" },
|
||||||
|
{ label: "QQ号", value: "qq" },
|
||||||
|
{ label: "自定义", value: "custom" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const handleNext = () => {
|
||||||
|
if (!formData.greeting.trim()) {
|
||||||
|
// 可以添加验证逻辑
|
||||||
|
}
|
||||||
|
onNext();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={style["friend-request-settings"]}>
|
||||||
|
<Card className={style["form-card"]}>
|
||||||
|
<Form layout="vertical">
|
||||||
|
{/* 备注类型 */}
|
||||||
|
<Form.Item label="备注类型" required className={style["form-item"]}>
|
||||||
|
<Selector
|
||||||
|
options={remarkTypeOptions}
|
||||||
|
value={[formData.remarkType]}
|
||||||
|
onChange={(value) => onChange({ remarkType: value[0] })}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{/* 备注格式 */}
|
||||||
|
{formData.remarkType === "custom" && (
|
||||||
|
<Form.Item label="备注格式" required className={style["form-item"]}>
|
||||||
|
<Input
|
||||||
|
placeholder="请输入备注格式,如:{name}-{phone}"
|
||||||
|
value={formData.remarkFormat}
|
||||||
|
onChange={(value) => onChange({ remarkFormat: value })}
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 打招呼消息 */}
|
||||||
|
<Form.Item label="打招呼消息" required className={style["form-item"]}>
|
||||||
|
<TextArea
|
||||||
|
placeholder="请输入打招呼消息"
|
||||||
|
value={formData.greeting}
|
||||||
|
onChange={(value) => onChange({ greeting: value })}
|
||||||
|
rows={4}
|
||||||
|
maxLength={200}
|
||||||
|
showCount
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{/* 好友申请间隔 */}
|
||||||
|
<Form.Item label="好友申请间隔(分钟)" className={style["form-item"]}>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入好友申请间隔"
|
||||||
|
value={formData.addFriendInterval.toString()}
|
||||||
|
onChange={(value) =>
|
||||||
|
onChange({ addFriendInterval: Number(value) || 1 })
|
||||||
|
}
|
||||||
|
min={1}
|
||||||
|
max={60}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 操作按钮 */}
|
||||||
|
<div className={style["actions"]}>
|
||||||
|
<Space style={{ width: "100%" }}>
|
||||||
|
<Button size="large" onClick={onPrev} className={style["prev-btn"]}>
|
||||||
|
上一步
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
size="large"
|
||||||
|
onClick={handleNext}
|
||||||
|
className={style["next-btn"]}
|
||||||
|
>
|
||||||
|
下一步
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FriendRequestSettings;
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
.message-settings {
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adm-form-item-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adm-input {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.adm-text-area {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 0;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prev-btn {
|
||||||
|
flex: 1;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-btn {
|
||||||
|
flex: 1;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
140
nkebao/src/pages/scenarios/new/steps/MessageSettings.tsx
Normal file
140
nkebao/src/pages/scenarios/new/steps/MessageSettings.tsx
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
Input,
|
||||||
|
Selector,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
Space,
|
||||||
|
TextArea,
|
||||||
|
Switch,
|
||||||
|
} from "antd-mobile";
|
||||||
|
import style from "./MessageSettings.module.scss";
|
||||||
|
|
||||||
|
interface MessageSettingsProps {
|
||||||
|
formData: any;
|
||||||
|
onChange: (data: any) => void;
|
||||||
|
onNext: () => void;
|
||||||
|
onPrev: () => void;
|
||||||
|
saving: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MessageSettings: React.FC<MessageSettingsProps> = ({
|
||||||
|
formData,
|
||||||
|
onChange,
|
||||||
|
onNext,
|
||||||
|
onPrev,
|
||||||
|
saving,
|
||||||
|
}) => {
|
||||||
|
const handleSave = () => {
|
||||||
|
onNext();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={style["message-settings"]}>
|
||||||
|
<Card className={style["form-card"]}>
|
||||||
|
<Form layout="vertical">
|
||||||
|
{/* 启用状态 */}
|
||||||
|
<Form.Item label="启用状态" className={style["form-item"]}>
|
||||||
|
<div className={style["switch-item"]}>
|
||||||
|
<span>启用计划</span>
|
||||||
|
<Switch
|
||||||
|
checked={formData.enabled}
|
||||||
|
onChange={(checked) => onChange({ enabled: checked })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{/* 自动回复消息 */}
|
||||||
|
<Form.Item label="自动回复消息" className={style["form-item"]}>
|
||||||
|
<TextArea
|
||||||
|
placeholder="请输入自动回复消息"
|
||||||
|
value={formData.autoReply || ""}
|
||||||
|
onChange={(value) => onChange({ autoReply: value })}
|
||||||
|
rows={4}
|
||||||
|
maxLength={500}
|
||||||
|
showCount
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{/* 关键词回复 */}
|
||||||
|
<Form.Item label="关键词回复" className={style["form-item"]}>
|
||||||
|
<TextArea
|
||||||
|
placeholder="请输入关键词回复规则,格式:关键词=回复内容"
|
||||||
|
value={formData.keywordReply || ""}
|
||||||
|
onChange={(value) => onChange({ keywordReply: value })}
|
||||||
|
rows={4}
|
||||||
|
maxLength={1000}
|
||||||
|
showCount
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{/* 群发消息 */}
|
||||||
|
<Form.Item label="群发消息" className={style["form-item"]}>
|
||||||
|
<TextArea
|
||||||
|
placeholder="请输入群发消息内容"
|
||||||
|
value={formData.groupMessage || ""}
|
||||||
|
onChange={(value) => onChange({ groupMessage: value })}
|
||||||
|
rows={4}
|
||||||
|
maxLength={500}
|
||||||
|
showCount
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{/* 消息发送间隔 */}
|
||||||
|
<Form.Item label="消息发送间隔(秒)" className={style["form-item"]}>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入消息发送间隔"
|
||||||
|
value={formData.messageInterval?.toString() || "5"}
|
||||||
|
onChange={(value) =>
|
||||||
|
onChange({ messageInterval: Number(value) || 5 })
|
||||||
|
}
|
||||||
|
min={1}
|
||||||
|
max={300}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
{/* 每日发送限制 */}
|
||||||
|
<Form.Item label="每日发送限制" className={style["form-item"]}>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder="请输入每日发送限制数量"
|
||||||
|
value={formData.dailyLimit?.toString() || "100"}
|
||||||
|
onChange={(value) =>
|
||||||
|
onChange({ dailyLimit: Number(value) || 100 })
|
||||||
|
}
|
||||||
|
min={1}
|
||||||
|
max={1000}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* 操作按钮 */}
|
||||||
|
<div className={style["actions"]}>
|
||||||
|
<Space style={{ width: "100%" }}>
|
||||||
|
<Button
|
||||||
|
size="large"
|
||||||
|
onClick={onPrev}
|
||||||
|
className={style["prev-btn"]}
|
||||||
|
disabled={saving}
|
||||||
|
>
|
||||||
|
上一步
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
size="large"
|
||||||
|
onClick={handleSave}
|
||||||
|
className={style["save-btn"]}
|
||||||
|
loading={saving}
|
||||||
|
>
|
||||||
|
{saving ? "保存中..." : "保存计划"}
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MessageSettings;
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { NavBar, Button, Toast, SpinLoading } from "antd-mobile";
|
import { NavBar, Button, Toast, SpinLoading } from "antd-mobile";
|
||||||
import { AddOutline } from "antd-mobile-icons";
|
import { PlusOutlined, UpOutlined } from "@ant-design/icons";
|
||||||
import { RiseOutlined } from "@ant-design/icons";
|
|
||||||
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
import Layout from "@/components/Layout/Layout";
|
import Layout from "@/components/Layout/Layout";
|
||||||
import { getScenarios } from "./api";
|
import { getScenarios } from "./api";
|
||||||
@@ -91,7 +90,7 @@ const Scene: React.FC = () => {
|
|||||||
className={style["new-plan-btn"]}
|
className={style["new-plan-btn"]}
|
||||||
style={{ marginLeft: "auto" }}
|
style={{ marginLeft: "auto" }}
|
||||||
>
|
>
|
||||||
<AddOutline /> 新建计划
|
<PlusOutlined /> 新建计划
|
||||||
</Button>
|
</Button>
|
||||||
</NavBar>
|
</NavBar>
|
||||||
}
|
}
|
||||||
@@ -118,7 +117,7 @@ const Scene: React.FC = () => {
|
|||||||
className={style["new-plan-btn"]}
|
className={style["new-plan-btn"]}
|
||||||
style={{ marginLeft: "auto" }}
|
style={{ marginLeft: "auto" }}
|
||||||
>
|
>
|
||||||
<AddOutline /> 新建计划
|
<PlusOutlined /> 新建计划
|
||||||
</Button>
|
</Button>
|
||||||
</NavBar>
|
</NavBar>
|
||||||
}
|
}
|
||||||
@@ -149,7 +148,7 @@ const Scene: React.FC = () => {
|
|||||||
className={style["new-plan-btn"]}
|
className={style["new-plan-btn"]}
|
||||||
style={{ marginLeft: "auto" }}
|
style={{ marginLeft: "auto" }}
|
||||||
>
|
>
|
||||||
<AddOutline /> 新建计划
|
<PlusOutlined /> 新建计划
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
></NavBar>
|
></NavBar>
|
||||||
@@ -189,7 +188,7 @@ const Scene: React.FC = () => {
|
|||||||
今日: {scenario.count}
|
今日: {scenario.count}
|
||||||
</span>
|
</span>
|
||||||
<span className={style["card-growth"]}>
|
<span className={style["card-growth"]}>
|
||||||
<RiseOutlined
|
<UpOutlined
|
||||||
style={{ fontSize: 14, color: "#52c41a", marginRight: 2 }}
|
style={{ fontSize: 14, color: "#52c41a", marginRight: 2 }}
|
||||||
/>
|
/>
|
||||||
{scenario.growth}
|
{scenario.growth}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { NavBar, Button } from "antd-mobile";
|
import { NavBar, Button } from "antd-mobile";
|
||||||
import { AddOutline } from "antd-mobile-icons";
|
import { PlusOutlined } from "@ant-design/icons";
|
||||||
import Layout from "@/components/Layout/Layout";
|
import Layout from "@/components/Layout/Layout";
|
||||||
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ const WechatAccounts: React.FC = () => {
|
|||||||
}
|
}
|
||||||
right={
|
right={
|
||||||
<Button size="small" color="primary">
|
<Button size="small" color="primary">
|
||||||
<AddOutline />
|
<PlusOutlined />
|
||||||
<span style={{ marginLeft: 4, fontSize: 12 }}>添加微信号</span>
|
<span style={{ marginLeft: 4, fontSize: 12 }}>添加微信号</span>
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,15 @@ import React from "react";
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { Card, NavBar, Badge } from "antd-mobile";
|
import { Card, NavBar, Badge } from "antd-mobile";
|
||||||
import {
|
import {
|
||||||
LikeOutline,
|
LikeOutlined,
|
||||||
MessageOutline,
|
MessageOutlined,
|
||||||
SendOutline,
|
SendOutlined,
|
||||||
TeamOutline,
|
TeamOutlined,
|
||||||
LinkOutline,
|
LinkOutlined,
|
||||||
AppOutline,
|
AppstoreOutlined,
|
||||||
PieOutline,
|
PieChartOutlined,
|
||||||
ClockCircleOutline,
|
ClockCircleOutlined,
|
||||||
} from "antd-mobile-icons";
|
} from "@ant-design/icons";
|
||||||
import Layout from "@/components/Layout/Layout";
|
import Layout from "@/components/Layout/Layout";
|
||||||
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
import styles from "./Workspace.module.scss";
|
import styles from "./Workspace.module.scss";
|
||||||
@@ -32,7 +32,7 @@ const Workspace: React.FC = () => {
|
|||||||
name: "自动点赞",
|
name: "自动点赞",
|
||||||
description: "智能自动点赞互动",
|
description: "智能自动点赞互动",
|
||||||
icon: (
|
icon: (
|
||||||
<LikeOutline className={styles.icon} style={{ color: "#ff4d4f" }} />
|
<LikeOutlined className={styles.icon} style={{ color: "#ff4d4f" }} />
|
||||||
),
|
),
|
||||||
path: "/workspace/auto-like",
|
path: "/workspace/auto-like",
|
||||||
bgColor: "#fff2f0",
|
bgColor: "#fff2f0",
|
||||||
@@ -43,7 +43,7 @@ const Workspace: React.FC = () => {
|
|||||||
name: "朋友圈同步",
|
name: "朋友圈同步",
|
||||||
description: "自动同步朋友圈内容",
|
description: "自动同步朋友圈内容",
|
||||||
icon: (
|
icon: (
|
||||||
<ClockCircleOutline
|
<ClockCircleOutlined
|
||||||
className={styles.icon}
|
className={styles.icon}
|
||||||
style={{ color: "#722ed1" }}
|
style={{ color: "#722ed1" }}
|
||||||
/>
|
/>
|
||||||
@@ -56,7 +56,7 @@ const Workspace: React.FC = () => {
|
|||||||
name: "群消息推送",
|
name: "群消息推送",
|
||||||
description: "智能群发助手",
|
description: "智能群发助手",
|
||||||
icon: (
|
icon: (
|
||||||
<SendOutline className={styles.icon} style={{ color: "#fa8c16" }} />
|
<SendOutlined className={styles.icon} style={{ color: "#fa8c16" }} />
|
||||||
),
|
),
|
||||||
path: "/workspace/group-push",
|
path: "/workspace/group-push",
|
||||||
bgColor: "#fff7e6",
|
bgColor: "#fff7e6",
|
||||||
@@ -66,7 +66,7 @@ const Workspace: React.FC = () => {
|
|||||||
name: "自动建群",
|
name: "自动建群",
|
||||||
description: "智能拉好友建群",
|
description: "智能拉好友建群",
|
||||||
icon: (
|
icon: (
|
||||||
<TeamOutline className={styles.icon} style={{ color: "#52c41a" }} />
|
<TeamOutlined className={styles.icon} style={{ color: "#52c41a" }} />
|
||||||
),
|
),
|
||||||
path: "/workspace/auto-group",
|
path: "/workspace/auto-group",
|
||||||
bgColor: "#f6ffed",
|
bgColor: "#f6ffed",
|
||||||
@@ -76,7 +76,7 @@ const Workspace: React.FC = () => {
|
|||||||
name: "流量分发",
|
name: "流量分发",
|
||||||
description: "管理流量分发和分配",
|
description: "管理流量分发和分配",
|
||||||
icon: (
|
icon: (
|
||||||
<LinkOutline className={styles.icon} style={{ color: "#1890ff" }} />
|
<LinkOutlined className={styles.icon} style={{ color: "#1890ff" }} />
|
||||||
),
|
),
|
||||||
path: "/workspace/traffic-distribution",
|
path: "/workspace/traffic-distribution",
|
||||||
bgColor: "#e6f7ff",
|
bgColor: "#e6f7ff",
|
||||||
@@ -86,7 +86,7 @@ const Workspace: React.FC = () => {
|
|||||||
name: "AI对话助手",
|
name: "AI对话助手",
|
||||||
description: "智能回复,提高互动质量",
|
description: "智能回复,提高互动质量",
|
||||||
icon: (
|
icon: (
|
||||||
<MessageOutline className={styles.icon} style={{ color: "#1890ff" }} />
|
<MessageOutlined className={styles.icon} style={{ color: "#1890ff" }} />
|
||||||
),
|
),
|
||||||
path: "/workspace/ai-assistant",
|
path: "/workspace/ai-assistant",
|
||||||
bgColor: "#e6f7ff",
|
bgColor: "#e6f7ff",
|
||||||
@@ -100,7 +100,12 @@ const Workspace: React.FC = () => {
|
|||||||
id: "ai-analyzer",
|
id: "ai-analyzer",
|
||||||
name: "AI数据分析",
|
name: "AI数据分析",
|
||||||
description: "智能分析客户行为特征",
|
description: "智能分析客户行为特征",
|
||||||
icon: <PieOutline className={styles.icon} style={{ color: "#531dab" }} />,
|
icon: (
|
||||||
|
<PieChartOutlined
|
||||||
|
className={styles.icon}
|
||||||
|
style={{ color: "#531dab" }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
path: "/workspace/ai-analyzer",
|
path: "/workspace/ai-analyzer",
|
||||||
bgColor: "#f0f0ff",
|
bgColor: "#f0f0ff",
|
||||||
isNew: true,
|
isNew: true,
|
||||||
@@ -109,7 +114,12 @@ const Workspace: React.FC = () => {
|
|||||||
id: "ai-strategy",
|
id: "ai-strategy",
|
||||||
name: "AI策略优化",
|
name: "AI策略优化",
|
||||||
description: "智能优化获客策略",
|
description: "智能优化获客策略",
|
||||||
icon: <AppOutline className={styles.icon} style={{ color: "#13c2c2" }} />,
|
icon: (
|
||||||
|
<AppstoreOutlined
|
||||||
|
className={styles.icon}
|
||||||
|
style={{ color: "#13c2c2" }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
path: "/workspace/ai-strategy",
|
path: "/workspace/ai-strategy",
|
||||||
bgColor: "#e6fffb",
|
bgColor: "#e6fffb",
|
||||||
isNew: true,
|
isNew: true,
|
||||||
@@ -118,7 +128,12 @@ const Workspace: React.FC = () => {
|
|||||||
id: "ai-forecast",
|
id: "ai-forecast",
|
||||||
name: "AI销售预测",
|
name: "AI销售预测",
|
||||||
description: "智能预测销售趋势",
|
description: "智能预测销售趋势",
|
||||||
icon: <PieOutline className={styles.icon} style={{ color: "#d48806" }} />,
|
icon: (
|
||||||
|
<PieChartOutlined
|
||||||
|
className={styles.icon}
|
||||||
|
style={{ color: "#d48806" }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
path: "/workspace/ai-forecast",
|
path: "/workspace/ai-forecast",
|
||||||
bgColor: "#fffbe6",
|
bgColor: "#fffbe6",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { NavBar, Button } from "antd-mobile";
|
import { NavBar, Button } from "antd-mobile";
|
||||||
import { AddOutline } from "antd-mobile-icons";
|
import { PlusOutlined } from "@ant-design/icons";
|
||||||
import Layout from "@/components/Layout/Layout";
|
import Layout from "@/components/Layout/Layout";
|
||||||
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ const AutoLike: React.FC = () => {
|
|||||||
}
|
}
|
||||||
right={
|
right={
|
||||||
<Button size="small" color="primary">
|
<Button size="small" color="primary">
|
||||||
<AddOutline />
|
<PlusOutlined />
|
||||||
<span style={{ marginLeft: 4, fontSize: 12 }}>新建任务</span>
|
<span style={{ marginLeft: 4, fontSize: 12 }}>新建任务</span>
|
||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Card, NavBar, TabBar, Grid } from "antd-mobile";
|
import { Card, NavBar, TabBar, Grid } from "antd-mobile";
|
||||||
import {
|
import {
|
||||||
AppOutline,
|
AppstoreOutlined,
|
||||||
UserOutline,
|
UserOutlined,
|
||||||
PieOutline, // 替换 BarChartOutline
|
PieChartOutlined,
|
||||||
ShopbagOutline,
|
ShoppingOutlined,
|
||||||
BellOutline,
|
BellOutlined,
|
||||||
} from "antd-mobile-icons";
|
} from "@ant-design/icons";
|
||||||
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
import MeauMobile from "@/components/MeauMobile/MeauMoible";
|
||||||
import Layout from "@/components/Layout/Layout";
|
import Layout from "@/components/Layout/Layout";
|
||||||
import style from "./index.module.scss";
|
import style from "./index.module.scss";
|
||||||
@@ -14,22 +14,22 @@ const sceneStats = [
|
|||||||
{
|
{
|
||||||
label: "公众号获客",
|
label: "公众号获客",
|
||||||
value: 234,
|
value: 234,
|
||||||
icon: <ShopbagOutline style={{ fontSize: 28, color: "#4caf50" }} />,
|
icon: <ShoppingOutlined style={{ fontSize: 28, color: "#4caf50" }} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "海报获客",
|
label: "海报获客",
|
||||||
value: 167,
|
value: 167,
|
||||||
icon: <AppOutline style={{ fontSize: 28, color: "#ff9800" }} />,
|
icon: <AppstoreOutlined style={{ fontSize: 28, color: "#ff9800" }} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "抖音获客",
|
label: "抖音获客",
|
||||||
value: 156,
|
value: 156,
|
||||||
icon: <PieOutline style={{ fontSize: 28, color: "#2196f3" }} />,
|
icon: <PieChartOutlined style={{ fontSize: 28, color: "#2196f3" }} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "小红书获客",
|
label: "小红书获客",
|
||||||
value: 89,
|
value: 89,
|
||||||
icon: <UserOutline style={{ fontSize: 28, color: "#e91e63" }} />,
|
icon: <UserOutlined style={{ fontSize: 28, color: "#e91e63" }} />,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import Scene from "@/pages/scene";
|
||||||
import Scenarios from "@/pages/scenarios/Scenarios";
|
import Scenarios from "@/pages/scenarios/Scenarios";
|
||||||
import NewPlan from "@/pages/scenarios/new/page";
|
import NewPlan from "@/pages/scenarios/new/page";
|
||||||
import ScenarioList from "@/pages/scenarios/ScenarioList";
|
import ScenarioList from "@/pages/scenarios/ScenarioList";
|
||||||
@@ -5,12 +6,12 @@ import ScenarioList from "@/pages/scenarios/ScenarioList";
|
|||||||
const scenarioRoutes = [
|
const scenarioRoutes = [
|
||||||
{
|
{
|
||||||
path: "/scene",
|
path: "/scene",
|
||||||
element: <Scenarios />,
|
element: <Scene />,
|
||||||
auth: true,
|
auth: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/scenarios",
|
path: "/scenarios",
|
||||||
element: <Scenarios />,
|
element: <Scene />,
|
||||||
auth: true,
|
auth: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ const PermissionRoute: React.FC<PermissionRouteProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 检查角色权限(如果需要)
|
// 检查角色权限(如果需要)
|
||||||
if (requiredRole && user.role !== requiredRole) {
|
if (requiredRole && user.isAdmin !== 1) {
|
||||||
navigate("/");
|
navigate("/");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -36,7 +36,7 @@ const PermissionRoute: React.FC<PermissionRouteProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 如果角色不匹配,不渲染子组件
|
// 如果角色不匹配,不渲染子组件
|
||||||
if (requiredRole && user.role !== requiredRole) {
|
if (requiredRole && user.isAdmin !== 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
迁移提示词(Checklist)
|
|
||||||
当你将其他项目迁移到 nkebao 项目结构时,可参考以下提示词进行自适应改造:
|
|
||||||
1. 目录与文件结构
|
|
||||||
按 nkebao 目录规范拆分:api、router/module、store/module、pages、styles 等
|
|
||||||
公共样式、reset、rem 适配、兼容性样式统一放到 styles/global.scss
|
|
||||||
2. 路由迁移
|
|
||||||
将原有路由配置拆分为 module 目录下的多个路由模块,导出数组
|
|
||||||
路由配置中增加 auth/requiredRole 字段,适配权限拦截
|
|
||||||
如有嵌套路由,按 useRoutes 结构组织
|
|
||||||
3. 状态管理迁移
|
|
||||||
将原有全局状态拆分为多个 zustand store,放到 store/module 下
|
|
||||||
如需持久化,使用 createPersistStore 工具
|
|
||||||
组件中统一用 useXxxStore 访问和修改状态
|
|
||||||
4. 请求管理迁移
|
|
||||||
所有接口请求统一用 src/api/request.ts 封装
|
|
||||||
需要防抖的接口可自定义 debounceGap 参数
|
|
||||||
错误提示统一用 Toast,必要时细分错误类型
|
|
||||||
需要 token 的接口无需手动加 header,自动注入
|
|
||||||
5. 组件与样式迁移
|
|
||||||
组件按业务拆分到 pages/ 下
|
|
||||||
样式统一用 scss,变量、reset、兼容性样式集中管理
|
|
||||||
移动端适配用 rem,PC 端可用媒体查询补充
|
|
||||||
6. 工程化与规范
|
|
||||||
路径引用统一用 @/xxx
|
|
||||||
保持 tsconfig.json、vite.config.ts、.eslintrc.js、postcss.config.js 配置一致
|
|
||||||
代码风格、格式化、类型声明与 nkebao 保持一致
|
|
||||||
7. 其他
|
|
||||||
如有全局 loading、消息、权限等需求,建议用 zustand/store 单独模块管理
|
|
||||||
Reference in New Issue
Block a user