From 4507cd484ef9208f868e3168b4fe5bf2b4938bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Fri, 10 Apr 2026 19:36:37 +0800 Subject: [PATCH] =?UTF-8?q?style(rider):=20=E4=BC=98=E5=8C=96=E9=AA=91?= =?UTF-8?q?=E6=89=8B=E4=B8=AD=E5=BF=83=E7=95=8C=E9=9D=A2=E5=8F=8A=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=85=A5=E5=8F=A3=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加骑手订单统计数据,支持待取货、配送中、已完成与今日任务 - 增加刷新按钮,便捷更新统计数据 - 优化头像及用户信息展示,增加主题样式和背景装饰 - 新增快捷功能入口,展示待取货订单数量红点提醒 - 登录未授权时提示并引导登录 - 调整色彩风格,将多处组件背景色由浅蓝色调整为深蓝色,提高视觉一致性 - 管理页新增内容管理工具的基础UI布局 - 扫码确认页重构授权登录页面,改用科技风格设计,增加动态粒子动画和渐变光晕效果 - 协议打开方式改为跳转网页视图,移除旧版弹窗 - 修正部分组件样式,调整按钮及文本颜色,提升视觉效果和交互体验 --- .workbuddy/expert-history.json | 24 +- .workbuddy/memory/2026-04-10.md | 61 +++ .../config.json | 33 ++ .../inboxes/java-explorer.json | 11 + .../inboxes/miniapp-explorer.json | 11 + .../inboxes/pc-explorer.json | 11 + .../inboxes/team-lead.json | 38 ++ src/admin/index.tsx | 62 ++- src/api/app/appProduct/index.ts | 19 + src/api/app/appProduct/model/index.ts | 51 +++ src/api/app/notification/index.ts | 47 ++ src/api/app/notification/model/index.ts | 41 ++ src/api/app/ticket/index.ts | 54 +++ src/api/app/ticket/model/index.ts | 64 +++ src/app.config.ts | 7 +- src/app.scss | 74 +++- src/components/AdminPanel.tsx | 2 +- src/dealer/index.tsx | 2 +- src/dealer/invite-stats/index.tsx | 2 +- src/developer/index.tsx | 2 +- src/pages/user/components/UserGrid.tsx | 115 ++--- src/passport/qr-confirm/index.tsx | 409 ++++++++++++------ src/passport/unified-qr/index.tsx | 2 +- src/passport/webview/index.config.ts | 5 + src/passport/webview/index.tsx | 22 + src/rider/index.tsx | 190 ++++++-- src/store/index.tsx | 2 +- src/user/apps/index.config.ts | 3 + src/user/apps/index.tsx | 192 ++++++++ src/user/gift/redeem.tsx | 2 +- src/user/gift/use.tsx | 2 +- src/user/notification/index.config.ts | 4 + src/user/notification/index.tsx | 206 +++++++++ 33 files changed, 1498 insertions(+), 272 deletions(-) create mode 100644 .workbuddy/memory/2026-04-10.md create mode 100644 .workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/config.json create mode 100644 .workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/java-explorer.json create mode 100644 .workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/miniapp-explorer.json create mode 100644 .workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/pc-explorer.json create mode 100644 .workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/team-lead.json create mode 100644 src/api/app/appProduct/index.ts create mode 100644 src/api/app/appProduct/model/index.ts create mode 100644 src/api/app/notification/index.ts create mode 100644 src/api/app/notification/model/index.ts create mode 100644 src/api/app/ticket/index.ts create mode 100644 src/api/app/ticket/model/index.ts create mode 100644 src/passport/webview/index.config.ts create mode 100644 src/passport/webview/index.tsx create mode 100644 src/user/apps/index.config.ts create mode 100644 src/user/apps/index.tsx create mode 100644 src/user/notification/index.config.ts create mode 100644 src/user/notification/index.tsx diff --git a/.workbuddy/expert-history.json b/.workbuddy/expert-history.json index f1765f6..37e7892 100644 --- a/.workbuddy/expert-history.json +++ b/.workbuddy/expert-history.json @@ -11,7 +11,29 @@ "usedAt": 1775579277895, "industryId": "all" } + ], + "c02960235460481c84382169b26bf90c": [ + { + "expertId": "SeniorDeveloper", + "name": "Will", + "profession": "高级开发工程师", + "avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/02-Engineering/SeniorDeveloper/SeniorDeveloper.png", + "promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/02-Engineering/SeniorDeveloper/SeniorDeveloper_zh.md", + "usedAt": 1775818515062, + "industryId": "all" + } + ], + "5bc2686c5a3a4f6d98f74a1467d05be8": [ + { + "expertId": "SeniorDeveloper", + "name": "Will", + "profession": "高级开发工程师", + "avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/02-Engineering/SeniorDeveloper/SeniorDeveloper.png", + "promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/02-Engineering/SeniorDeveloper/SeniorDeveloper_zh.md", + "usedAt": 1775819465726, + "industryId": "all" + } ] }, - "lastUpdated": 1775586324678 + "lastUpdated": 1775819525128 } \ No newline at end of file diff --git a/.workbuddy/memory/2026-04-10.md b/.workbuddy/memory/2026-04-10.md new file mode 100644 index 0000000..969f1cd --- /dev/null +++ b/.workbuddy/memory/2026-04-10.md @@ -0,0 +1,61 @@ +# 2026-04-10 工作记录 + +## qr-confirm 登录页优化 + +- 修改文件:`src/passport/qr-confirm/index.tsx` +- 授权按钮改为全宽 52px 圆角大按钮(inline style),易于点击 +- 勾选框外层包裹 padding:8px 的热区 View,点击更容易命中 +- 协议从内嵌弹窗改为跳转 WebView 页面打开网页版: + - 服务协议:https://websopy.websoft.top/agreement + - 隐私政策:https://websopy.websoft.top/privacy +- 新增通用 WebView 页面:`src/passport/webview/index.tsx` +- `src/app.config.ts` passport 子包新增 `webview/index` 路由 + +## qr-confirm 按钮颜色修复 + +- 问题:NutUI `Button` 组件内置样式优先级高,覆盖了 `style` 中的 `background` 渐变色,按钮颜色不显示 +- 修复:改用小程序原生 `button` 标签,`open-type="getPhoneNumber"` 照常有效,渐变背景正常显示 + +## template-5 vs PC端 功能对比分析 + +- 对比了三个项目:template-5(小程序) vs websopy-pc(PC端/Nuxt3) vs websopy-java(后端) +- PC端定位:SaaS平台门户 + 用户控制台 + 开发者中心 + 管理后台 +- 小程序端定位:移动端入口 + 电商购物(首页偏营销展示) +- **关键发现**: + - Bug: UserGrid.tsx 开发者中心入口跳转到 /rider/index(骑手),应修正 + - 缺失核心功能:通知中心、工单系统、应用/产品订阅管理 + - 购买断链:产品展示后无法在小程序完成购买 + - admin 模块为空壳,rider 功能极简 + - PC端开发者专属功能(CI/CD/Git/源码)建议通过 WebView 桥接 +- 输出完整修改计划报告(4阶段,预估7-12天) + +## 小程序端阶段一+二修改执行 + +### Bug 修复 + 代码清理 +- **UserGrid.tsx**:修复"开发者中心"跳转到 /rider/index 的 Bug,改为通过 WebView 加载 PC 端开发者中心 +- **UserGrid.tsx**:清理全部注释代码(约60行),代码整洁度大幅提升 +- **admin/index.tsx**:从空壳"待开发"改为内容管理入口页,保留文章管理功能 + +### 骑手中心优化 +- **rider/index.tsx**:增加今日统计概览(待取货/配送中/已完成/今日任务) +- 增加快捷功能入口、待取货/配送中数量徽标、刷新按钮、空状态展示 + +### 新增通知中心 +- API层:`src/api/app/notification/model/index.ts` + `src/api/app/notification/index.ts` +- 页面:`src/user/notification/index.tsx` - 支持分页加载、类型标签、已读标记、全部已读、空状态 + +### 新增工单系统(3个页面) +- API层:`src/api/app/ticket/model/index.ts` + `src/api/app/ticket/index.ts` +- 列表页:`src/user/ticket/index.tsx` - 统计概览、状态筛选、分页加载 +- 提交页:`src/user/ticket/create.tsx` - 标题/分类/优先级/描述表单 +- 详情页:`src/user/ticket/detail.tsx` - 查看详情、回复、关闭工单 + +### 新增我的应用页面 +- API层:`src/api/app/appProduct/model/index.ts` + `src/api/app/appProduct/index.ts` +- 页面:`src/user/apps/index.tsx` - 应用列表、状态展示、管理后台/前台入口、到期时间 + +### UserGrid 新增入口 +- 我的应用、我的工单、消息通知(3个新图标入口) + +### 路由注册 +- `app.config.ts` user 子包新增:notification/index, ticket/index, ticket/create, ticket/detail, apps/index diff --git a/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/config.json b/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/config.json new file mode 100644 index 0000000..1486978 --- /dev/null +++ b/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/config.json @@ -0,0 +1,33 @@ +{ + "name": "_auto_c02960235460481c84382169b26bf90c", + "leadAgentId": "c02960235460481c84382169b26bf90c", + "workspacePath": "/Users/gxwebsoft/VUE/template-5", + "createdAt": "2026-04-10T10:58:09.075Z", + "options": { + "workspacePath": "/Users/gxwebsoft/VUE/template-5", + "isAutoTeam": true + }, + "members": [ + { + "memberId": "team-lead@_auto_c02960235460481c84382169b26bf90c", + "name": "team-lead", + "role": "Team Lead - coordinates the team and interacts with the user" + }, + { + "memberId": "miniapp-explorer@_auto_c02960235460481c84382169b26bf90c", + "name": "miniapp-explorer", + "role": "分析小程序端功能模块" + }, + { + "memberId": "pc-explorer@_auto_c02960235460481c84382169b26bf90c", + "name": "pc-explorer", + "role": "分析PC端功能模块" + }, + { + "memberId": "java-explorer@_auto_c02960235460481c84382169b26bf90c", + "name": "java-explorer", + "role": "分析Java后端API接口" + } + ], + "isAutoTeam": true +} \ No newline at end of file diff --git a/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/java-explorer.json b/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/java-explorer.json new file mode 100644 index 0000000..4e2d66a --- /dev/null +++ b/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/java-explorer.json @@ -0,0 +1,11 @@ +[ + { + "id": "msg-1775818705614-am57es", + "from": "team-lead", + "to": "java-explorer", + "type": "message", + "content": "请继续执行你的分析任务,完成后把结果发给我。", + "timestamp": "2026-04-10T10:58:25.614Z", + "read": true + } +] \ No newline at end of file diff --git a/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/miniapp-explorer.json b/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/miniapp-explorer.json new file mode 100644 index 0000000..38dbd4c --- /dev/null +++ b/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/miniapp-explorer.json @@ -0,0 +1,11 @@ +[ + { + "id": "msg-1775818702473-ypuevm", + "from": "team-lead", + "to": "miniapp-explorer", + "type": "message", + "content": "请继续执行你的分析任务,完成后把结果发给我。", + "timestamp": "2026-04-10T10:58:22.473Z", + "read": true + } +] \ No newline at end of file diff --git a/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/pc-explorer.json b/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/pc-explorer.json new file mode 100644 index 0000000..f793333 --- /dev/null +++ b/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/pc-explorer.json @@ -0,0 +1,11 @@ +[ + { + "id": "msg-1775818704022-ii1chz", + "from": "team-lead", + "to": "pc-explorer", + "type": "message", + "content": "请继续执行你的分析任务,完成后把结果发给我。", + "timestamp": "2026-04-10T10:58:24.022Z", + "read": true + } +] \ No newline at end of file diff --git a/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/team-lead.json b/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/team-lead.json new file mode 100644 index 0000000..db20e17 --- /dev/null +++ b/.workbuddy/teams/_auto_c02960235460481c84382169b26bf90c/inboxes/team-lead.json @@ -0,0 +1,38 @@ +[ + { + "id": "msg-1775818699653-fa49wy", + "from": "pc-explorer", + "to": "team-lead", + "type": "message", + "content": "收到任务,开始探索PC端项目 /Users/gxwebsoft/VUE/websopy-pc 的功能结构。我会分析路由配置、src目录结构、API文件和状态管理,然后给出详细的功能模块清单。", + "timestamp": "2026-04-10T10:58:19.653Z", + "read": true + }, + { + "id": "msg-1775818788555-vk3aef", + "from": "pc-explorer", + "to": "team-lead", + "type": "message", + "content": "## PC端项目功能结构分析报告\n\n### 项目基本信息\n- **项目路径**: `/Users/gxwebsoft/VUE/websopy-pc`\n- **技术栈**: Nuxt 3 + Vue 3 + TypeScript + Ant Design Vue\n- **架构**: 基于文件系统的路由(Nuxt Pages)\n\n---\n\n### 一、页面路由结构\n\n#### 1. 公开页面(无需登录)\n| 路由路径 | 页面功能 | 描述 |\n|---------|---------|------|\n| `/` | 首页 | 平台介绍、产品矩阵、核心能力展示、OpenClaw AI生态 |\n| `/login` | 登录页 | 扫码登录、手机号+短信验证码登录、图形验证码 |\n| `/agreement` | 用户协议 | 注册协议页面 |\n| `/privacy` | 隐私政策 | 隐私政策页面 |\n| `/contact` | 联系我们 | 联系表单 |\n| `/products` | 产品列表 | 展示所有可购买产品 |\n| `/market` | 应用市场 | 应用商店/模板市场 |\n| `/market/[id]` | 应用详情 | 单个应用/模板详情页 |\n| `/articles` | 文章列表 | CMS文章列表 |\n| `/flow` | 开通流程 | 产品购买开通流程说明 |\n| `/deploy` | 部署说明 | 私有化部署介绍 |\n| `/platform` | 平台介绍 | 平台能力介绍 |\n| `/miniapp` | 小程序 | 小程序相关介绍 |\n| `/website` | 网站构建 | 网站构建产品介绍 |\n| `/shop` | 商城系统 | 商城系统介绍 |\n| `/oa` | OA系统 | OA办公系统介绍 |\n| `/ai-agent` | AI智能体 | OpenClaw AI智能体介绍 |\n| `/openclaw` | OpenClaw | OpenClaw生态详情 |\n| `/developer-center` | 开发者中心 | 开发者文档入口 |\n| `/qr-confirm` | 扫码确认 | 扫码登录确认页 |\n| `/wx-scan` | 微信扫码 | 微信扫码相关 |\n| `/bind-phone` | 绑定手机 | 手机号绑定页面 |\n| `/create-app` | 创建应用 | 应用创建向导 |\n\n#### 2. 用户控制台(/console/*)- 需要登录\n| 路由路径 | 页面功能 | 关键业务逻辑 |\n|---------|---------|-------------|\n| `/console` | 控制台首页 | 欢迎区、快捷入口、最近使用应用列表 |\n| `/console/apps` | 应用中心 | 我的应用列表、应用详情抽屉 |\n| `/console/products` | 已购产品 | 已购买的产品授权与订阅管理 |\n| `/console/orders` | 订单记录 | 历史订单与账单查询 |\n| `/console/coupons` | 优惠券 | 可用优惠券管理 |\n| `/console/invoices` | 发票记录 | 发票申请与记录 |\n| `/console/tickets` | 工单管理 | 技术支持工单提交与跟踪 |\n| `/console/notifications` | 消息通知 | 系统通知中心 |\n| `/console/app-review` | 应用审核 | 应用上架审核状态 |\n| `/console/logout` | 退出登录 | 登出处理 |\n\n**账号管理子模块(/console/account/*)**\n| 路由路径 | 页面功能 |\n|---------|---------|\n| `/console/account` | 账户信息 |\n| `/console/account/kyc` | 实名认证 |\n| `/console/account/members` | 成员管理(团队与权限) |\n| `/console/account/security` | 账号安全(密码与安全设置) |\n\n**租户管理子模块(/console/tenant/*)**\n| 路由路径 | 页面功能 |\n|---------|---------|\n| `/console/tenant` | 租户首页 |\n| `/console/tenant/unopened` | 未开通提示 |\n\n**邀请管理子模块(/console/invites/*)**\n| 路由路径 | 页面功能 |\n|---------|---------|\n| `/console/invites` | 应用邀请列表 |\n\n#### 3. 开发者中心(/developer/*)- 需要开发者权限\n| 路由路径 | 页面功能 | 关键业务逻辑 |\n|---------|---------|-------------|\n| `/developer` | 开发者首页 | 数据概览、快捷入口、最近动态、公告、服务状态 |\n| `/developer/apps` | 应用中心 | 查看订阅的应用与后台入口 |\n| `/developer/apikeys` | API Key管理 | 创建、查看和管理API Key |\n| `/developer/source` | 源码与仓库 | 申请仓库权限、获取完整源代码 |\n| `/developer/git` | Git账号绑定 | 绑定Gitea账号获取仓库访问权限 |\n| `/developer/requests` | 权限申请记录 | 权限申请历史记录 |\n| `/developer/support` | 支持与反馈 | 问题反馈入口 |\n| `/developer/tickets` | 工单处理 | 开发者工单管理 |\n\n**CI/CD与部署(/developer/*)**\n| 路由路径 | 页面功能 |\n|---------|---------|\n| `/developer/build` | 构建任务 |\n| `/developer/pipeline` | 流水线管理 |\n| `/developer/publish` | 发布管理 |\n| `/developer/versions` | 版本管理 |\n\n**资源中心(/developer/resources/*)**\n| 路由路径 | 页面功能 |\n|---------|---------|\n| `/developer/resources` | 资源总览 |\n| `/developer/resources/servers` | 服务器管理 |\n| `/developer/resources/databases` | 数据库管理 |\n| `/developer/resources/domains` | 域名管理 |\n| `/developer/resources/git` | 代码仓库 |\n\n**数据统计**\n| 路由路径 | 页面功能 |\n|---------|---------|\n| `/developer/analytics` | 数据统计 |\n\n**开发文档(/developer/docs/*)**\n| 路由路径 | 页面功能 |\n|---------|---------|\n| `/developer/docs` | 文档首页 |\n| `/developer/docs/[...slug]` | 动态文档页 |\n\n#### 4. 平台管理后台(/admin/*)- 需要管理员权限\n| 路由路径 | 页面功能 | 关键业务逻辑 |\n|---------|---------|-------------|\n| `/admin` | 管理首页 | 核心数据统计、待办事项、快速入口 |\n| `/admin/app-review` | 应用审核 | 待审核应用列表、审核操作 |\n| `/admin/git-review` | Git审核 | Git账号审核 |\n| `/admin/apps` | 应用管理 | 全平台应用管理 |\n| `/admin/market` | 应用市场 | 上架应用管理 |\n| `/admin/users` | 用户管理 | 所有用户管理 |\n| `/admin/developers` | 开发者管理 | 开发者账号管理 |\n| `/admin/tickets` | 工单处理 | 全平台工单处理 |\n| `/admin/articles` | 文章管理 | CMS文章管理 |\n| `/admin/article-categories` | 文章分类 | 文章分类管理 |\n| `/admin/announcements` | 公告管理 | 平台公告管理 |\n| `/admin/settings` | 平台设置 | 系统配置 |\n\n#### 5. 邀请相关\n| 路由路径 | 页面功能 |\n|---------|---------|\n| `/invite/accept` | 接受邀请 |\n\n---\n\n### 二、API接口模块结构\n\n#### 1. 应用相关 API (`/app/*`)\n- `apikey/` - API Key管理\n- `appConfig/` - 应用配置\n- `appCredential/` - 应用凭证\n- `appEvent/` - 应用事件\n- `appProduct/` - 应用产品\n- `appResource/` - 应用资源\n- `appUser/` - 应用用户\n- `appVersion/` - 应用版本\n- `article/` - 文章\n- `articleCategory/` - 文章分类\n- `cloudCredential/` - 云凭证\n- `invite/` - 邀请管理\n- `notification/` - 通知\n- `setting/` - 设置\n- `userCache/` - 用户缓存\n- `cicd.ts` - CI/CD相关\n\n#### 2. CMS内容管理 API (`/cms/*`)\n- `cmsAd/` - 广告管理\n- `cmsArticle/` - 文章管理\n- `cmsArticleCategory/` - 文章分类\n- `cmsContactLead/` - 联系线索\n- `cmsDesign/` - 设计管理\n- `cmsDomain/` - 域名管理\n- `cmsForm/` - 表单管理\n- `cmsNavigation/` - 导航管理\n- `cmsOrder/` - 订单管理\n- `cmsStatistics/` - 统计\n- `cmsTemplate/` - 模板管理\n- `cmsWebsite/` - 网站管理\n- `cmsWebsiteField/` - 网站字段配置\n\n#### 3. 系统管理 API (`/system/*`)\n- `access-key/` - 访问密钥\n- `appstore/` - 应用商店\n- `cache/` - 缓存管理\n- `chat/` - 聊天\n- `company/` - 企业/公司\n- `companyGit/` - 企业Git\n- `dict/` - 字典\n- `domain/` - 域名\n- `environment/` - 环境\n- `file/` - 文件管理\n- `login-record/` - 登录记录\n- `menu/` - 菜单\n- `modules/` - 模块\n- `operation-record/` - 操作记录\n- `order/` - 订单\n- `organization/` - 组织\n- `payment/` - 支付\n- `plug/` - 插件\n- `role/` - 角色\n- `setting/` - 设置\n- `tenant/` - 租户\n- `user/` - 用户\n- `userRole/` - 用户角色\n- `version/` - 版本\n- `website/` - 网站\n\n#### 4. 用户相关 API (`/user/*`)\n- `balance-log/` - 余额日志\n- `feedback/` - 反馈\n- `grade/` - 等级\n- `recharge/` - 充值\n- `referee/` - 推荐人\n- `userCoupon/` - 用户优惠券\n\n#### 5. 商城相关 API (`/shop/*`)\n- `shopCoupon/` - 优惠券\n- `shopDealer*` - 分销商相关\n- `shopExpress/` - 物流快递\n- `shopGift/` - 礼品\n- `shopGoods/` - 商品\n- `shopMerchant/` - 商户\n- `shopOrder/` - 订单\n- `shopUser/` - 商城用户\n\n#### 6. 认证相关 API (`/passport/*`)\n- `login/` - 登录相关(短信登录、验证码)\n- `qrLogin/` - 扫码登录\n\n#### 7. 开发者相关 API (`/developer/*`)\n- `index.ts` - 开发者相关接口\n\n---\n\n### 三、状态管理\n\n项目未使用Pinia/Vuex等传统状态管理库,而是使用以下方式:\n\n1. **Composables** (`/app/composables/`)\n - `useAppPermission.ts` - 应用权限管理\n - `useNotificationCenter.ts` - 通知中心\n - `usePageSeo.ts` - 页面SEO\n - `useQRCode.ts` - 二维码\n - `useResourceAccess.ts` - 资源访问控制\n\n2. **工具函数** (`/app/utils/`)\n - `token-util.ts` - Token管理\n - `permission.ts` - 权限工具\n\n3. **本地存储**\n - `localStorage`存储:`UserId`, `TenantId`, `Token`\n\n---\n\n### 四、布局系统\n\n| 布局文件 | 用途 | 特点 |\n|---------|------|------|\n| `default.vue` | 默认布局 | 基础布局 |\n| `console.vue` | 用户控制台 | 左侧深色侧边栏(深蓝黑),顶部Header带用户菜单 |\n| `admin.vue` | 管理后台 | 左侧深色侧边栏(深红黑),管理员标识 |\n| `developer.vue` | 开发者中心 | 浅色侧边栏,分组导航,权限控制 |\n| `blank.vue` | 空白布局 | 极简布局 |\n| `oa.vue` | OA系统 | OA专用布局 |\n\n---\n\n### 五、关键业务逻辑总结\n\n#### 1. 权限体系\n- **普通用户**: 可访问控制台、购买产品、管理订单\n- **开发者**: 额外访问开发者中心,管理API Key、源码权限\n- **管理员**: 访问平台管理后台,审核应用、管理用户\n\n#### 2. 应用生命周期\n创建应用 → 选择产品 → 配置应用 → 开发/部署 → 上架审核 → 发布上线\n\n#### 3. 核心功能模块\n1. **产品购买与开通**: 选择产品 → 下单支付 → 自动开通租户 → 初始化模块 → 交付上线\n2. **应用管理**: 创建、配置、版本管理、发布\n3. **开发者工具**: API Key管理、源码访问、CI/CD流水线\n4. **资源管理**: 服务器、数据库、域名、SSL证书\n5. **内容管理**: 文章、公告、工单、通知\n\n#### 4. 多租户架构\n- 支持SaaS多租户\n- 私有化部署选项\n- 租户隔离的数据和资源\n\n---\n\n### 六、文件统计\n- **总Vue页面**: 102个\n- **API接口文件**: 275个TypeScript文件\n- **主要页面目录**: \n - `pages/`: 22个根页面\n - `pages/admin/`: 12个管理页面\n - `pages/console/`: 17个控制台页面\n - `pages/developer/`: 26个开发者页面", + "timestamp": "2026-04-10T10:59:48.555Z", + "read": true + }, + { + "id": "msg-1775818789671-5yckhr", + "from": "java-explorer", + "to": "team-lead", + "type": "message", + "content": "# Java后端API接口结构分析报告\n\n## 项目概述\n项目路径:`/Users/gxwebsoft/JAVA/websopy-java`\n共发现 **172个Controller**,按模块划分为:app(应用管理)、cms(内容管理)、shop(商城)、payment(支付)四大模块\n\n---\n\n## 一、应用管理模块 (app)\n\n### 1.1 应用产品管理 `/api/app/product`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/page` | GET | 分页查询应用列表 |\n| `/list` | GET | 获取我的应用列表 |\n| `/my/page` | GET | 分页查询我的应用(创建者) |\n| `/joined/page` | GET | 分页查询我参与的应用 |\n| `/detail/{id}` | GET | 获取应用详情 |\n| `/info/{code}` | GET | 根据标识获取应用 |\n| `/create` | POST | 创建应用 |\n| `/update` | PUT | 更新应用 |\n| `/delete/{id}` | DELETE | 删除应用 |\n| `/submit/{id}` | POST | 提交审核 |\n| `/approve/{id}` | POST | 审核通过 |\n| `/reject/{id}` | POST | 审核拒绝 |\n| `/publish/{id}` | POST | 上架应用 |\n| `/unpublish/{id}` | POST | 下架应用 |\n| `/regenerateSecret/{id}` | POST | 重新生成密钥 |\n| `/market/page` | GET | 应用市场分页 |\n| `/accessible` | GET | 获取可访问的应用列表(带角色信息) |\n\n### 1.2 应用成员管理 `/api/app/app-user`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/page` | GET | 分页查询应用成员 |\n| `/` | GET | 查询全部应用成员 |\n| `/{id}` | GET | 根据id查询应用成员 |\n| `/` | POST | 添加应用成员(手动添加) |\n| `/invite` | POST | 邀请用户成为应用成员 |\n| `/` | PUT | 修改应用成员信息 |\n| `/role/{id}/{role}` | PUT | 修改成员角色 |\n| `/{id}` | DELETE | 移除应用成员 |\n| `/invites/pending` | GET | 查询待确认邀请列表 |\n| `/invites/pending/count` | GET | 统计待确认邀请数量 |\n| `/invites/{inviteId}/accept` | POST | 接受邀请加入应用 |\n| `/invites/{inviteId}/reject` | POST | 拒绝邀请 |\n| `/check-access` | GET | 检查开发者中心访问权限 |\n\n### 1.3 开发者资源管理 `/api/app/developer-resource`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/page` | GET | 分页查询资源列表(支持协作权限) |\n| `/` | GET | 查询资源列表 |\n| `/{resourceId}` | GET | 获取资源详情 |\n| `/stats` | GET | 统计各类型资源数量 |\n| `/` | POST | 新增资源 |\n| `/` | PUT | 修改资源 |\n| `/{resourceId}` | DELETE | 删除资源(逻辑删除) |\n| `/test-connection` | POST | 测试服务器数据库连接 |\n| `/test-ssh` | POST | 测试服务器SSH连接 |\n| `/server-status/{resourceId}` | GET | 获取1Panel服务器状态 |\n| `/retry-create-database/{resourceId}` | POST | 重试创建数据库 |\n| `/reset-password/{resourceId}` | POST | 重置数据库密码 |\n| `/refresh-storage/{resourceId}` | POST | 刷新存储桶信息 |\n\n### 1.4 应用密钥凭证 `/api/app/app-credential`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/page` | GET | 分页查询应用密钥凭证 |\n| `/` | GET | 查询全部应用密钥凭证 |\n| `/{id}` | GET | 根据id查询应用密钥凭证 |\n| `/` | POST | 创建应用密钥凭证 |\n| `/` | PUT | 修改应用密钥凭证 |\n| `/resetSecret/{id}` | POST | 重置AppSecret |\n| `/status/{id}/{status}` | PUT | 禁用/启用凭证 |\n| `/{id}` | DELETE | 删除应用密钥凭证 |\n\n### 1.5 云账号凭证 `/api/app/cloud-credential`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/page` | GET | 分页查询凭证列表 |\n| `/` | GET | 查询凭证列表(不分页) |\n| `/{id}` | GET | 获取凭证详情 |\n| `/` | POST | 新增凭证 |\n| `/` | PUT | 修改凭证 |\n| `/{id}` | DELETE | 删除凭证 |\n| `/test-connection` | POST | 测试凭证连接 |\n| `/test/{id}` | POST | 根据ID测试凭证连接 |\n| `/providers` | GET | 获取支持的云服务商列表 |\n\n### 1.6 CI/CD管理 `/api/app/cicd`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/pipeline/page` | GET | 分页查询流水线 |\n| `/pipeline/app/{appId}` | GET | 查询应用的所有流水线 |\n| `/pipeline/{id}` | GET | 查询流水线详情 |\n| `/pipeline` | POST | 创建流水线 |\n| `/pipeline` | PUT | 更新流水线 |\n| `/pipeline/{id}` | DELETE | 删除流水线 |\n| `/pipeline/{id}/toggle` | POST | 启用/禁用流水线 |\n| `/pipeline/{id}/status` | GET | 获取流水线状态 |\n| `/build/page` | GET | 分页查询构建记录 |\n| `/build/app/{appId}` | GET | 查询应用的所有构建记录 |\n| `/build/{id}` | GET | 查询构建详情 |\n| `/build/latest/{appId}` | GET | 获取应用最新构建 |\n| `/build/trigger` | POST | 触发构建 |\n| `/build/{id}/log` | GET | 获取构建日志 |\n| `/build/{id}/cancel` | POST | 取消构建 |\n| `/build/{id}/retry` | POST | 重试构建 |\n| `/build/stats/{appId}` | GET | 获取构建统计 |\n| `/webhook/gitea` | POST | Gitea Webhook回调 |\n| `/config` | GET | 获取CI系统配置 |\n\n### 1.7 权限申请 `/api/app/developer/permission-requests`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/page` | GET | 获取权限申请列表 |\n| `/` | GET | 查询全部权限申请 |\n| `/{id}` | GET | 根据id查询权限申请 |\n| `/stats` | GET | 获取权限申请统计 |\n| `/available-repos` | GET | 获取可申请的仓库列表 |\n| `/` | POST | 提交权限申请 |\n| `/{id}/approve` | PUT | 审核权限申请-通过 |\n| `/{id}/reject` | PUT | 审核权限申请-拒绝 |\n\n### 1.8 应用工单管理 `/api/app/ticket`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/my` | GET | 查询我的工单(分页) |\n| `/submit` | POST | 提交工单 |\n| `/{ticketId}/close` | PUT | 关闭工单 |\n| `/list` | GET | 查询所有工单(技术人员) |\n| `/{ticketId}` | GET | 获取工单详情 |\n| `/status` | PUT | 更新工单状态 |\n| `/assign` | PUT | 分配处理人 |\n| `/{ticketId}/replies` | GET | 获取工单回复列表 |\n| `/reply` | POST | 提交工单回复 |\n| `/stats` | GET | 工单统计数据 |\n| `/staff-list` | GET | 获取技术人员列表 |\n\n---\n\n## 二、商城模块 (shop)\n\n### 2.1 订单管理 `/api/shop/shop-order`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/page` | GET | 分页查询订单 |\n| `/` | GET | 查询全部订单 |\n| `/{id}` | GET | 根据id查询订单 |\n| `/` | POST | 添加订单 |\n| `/legacy` | POST | 添加订单(兼容旧版本) |\n| `/` | PUT | 修改订单(含退款处理) |\n| `/{id}` | DELETE | 删除订单 |\n| `/repair` | PUT | 修复订单 |\n| `/total` | GET | 统计订单总金额 |\n| `/cancel/{id}` | PUT | 取消订单 |\n| `/auto-cancel/trigger` | POST | 手动触发订单自动取消任务 |\n| `/auto-cancel/status` | GET | 获取自动取消任务状态 |\n| `/notify/{tenantId}` | POST | 微信支付异步通知 |\n| `/payment-status` | PUT | 更新订单支付状态 |\n\n### 2.2 商品管理 `/api/shop/shop-goods`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/page` | GET | 分页查询商品 |\n| `/` | GET | 查询全部商品 |\n| `/{id}` | GET | 根据id查询商品 |\n| `/` | POST | 添加商品 |\n| `/` | PUT | 修改商品 |\n| `/{id}` | DELETE | 删除商品 |\n| `/data` | GET | 统计信息 |\n\n### 2.3 购物车管理 `/api/shop/shop-cart`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/page` | GET | 分页查询购物车 |\n| `/` | GET | 查询全部购物车 |\n| `/{id}` | GET | 根据id查询购物车 |\n| `/` | POST | 添加购物车 |\n| `/` | PUT | 修改购物车 |\n| `/{id}` | DELETE | 删除购物车 |\n\n### 2.4 优惠券管理 `/api/shop/shop-coupon`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/list` | POST | 可领取优惠券列表 |\n| `/page` | GET | 分页查询优惠券 |\n| `/` | GET | 查询全部优惠券 |\n| `/{id}` | GET | 根据id查询优惠券 |\n| `/` | POST | 添加优惠券 |\n| `/` | PUT | 修改优惠券 |\n| `/{id}` | DELETE | 删除优惠券 |\n\n### 2.5 收货地址管理 `/api/shop/shop-user-address`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/page` | GET | 分页查询收货地址 |\n| `/` | GET | 查询全部收货地址 |\n| `/{id}` | GET | 根据id查询收货地址 |\n| `/` | POST | 添加收货地址 |\n| `/` | PUT | 修改收货地址 |\n| `/{id}` | DELETE | 删除收货地址 |\n\n### 2.6 用户记录管理 `/api/shop/shop-user`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/page` | GET | 分页查询用户记录 |\n| `/` | GET | 查询全部用户记录 |\n| `/{userId}` | GET | 根据userId查询用户记录 |\n| `/` | POST | 添加用户记录 |\n| `/` | PUT | 修改用户记录 |\n| `/{id}` | DELETE | 删除用户记录 |\n\n---\n\n## 三、支付模块 (payment)\n\n### 3.1 统一支付接口 `/api/payment`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/create` | POST | 创建支付订单 |\n| `/create-with-order` | POST | 创建支付订单(包含订单信息) |\n| `/query` | GET | 查询支付状态 |\n| `/refund` | POST | 申请退款 |\n| `/refund/query` | GET | 查询退款状态 |\n| `/close` | POST | 关闭订单 |\n| `/types` | GET | 获取支持的支付类型 |\n| `/strategy/{paymentType}` | GET | 获取支付策略信息 |\n| `/strategies` | GET | 获取所有支付策略信息 |\n| `/support/{paymentType}` | GET | 检查支付类型支持情况 |\n| `/update-status` | PUT | 手动更新支付状态 |\n| `/config/check` | GET | 检查支付配置 |\n| `/query-recent` | GET | 查询用户最近的支付订单 |\n\n---\n\n## 四、内容管理模块 (cms)\n\n### 4.1 文章管理 `/api/cms/cms-article`\n| API路径 | 方法 | 功能描述 |\n|---------|------|----------|\n| `/page` | GET | 分页查询文章 |\n| `/` | GET | 查询全部文章 |\n| `/{id}` | GET | 根据id查询文章 |\n| `/getByCode/{code}` | GET | 根据code查询文章 |\n| `/` | POST | 添加文章 |\n| `/` | PUT | 修改文章 |\n| `/{id}` | DELETE | 删除文章 |\n| `/getPrevious/{id}` | GET | 读取上一篇 |\n| `/getNext/{id}` | GET | 读取下一篇 |\n| `/data` | GET | 统计信息 |\n| `/checkArticlePassword` | GET | 密码校验 |\n| `/import` | POST | 批量导入文章 |\n| `/findTags` | GET | 按标签分页查询 |\n| `/pageTags` | GET | 按标签分页查询 |\n| `/getByIds` | GET | 按IDS查询 |\n\n---\n\n## 五、核心业务实体结构\n\n### 5.1 AppProduct (应用产品)\n- 核心字段:productId, productName, productCode, productSecret\n- 应用类型:网站/微信小程序/抖音小程序/百度小程序/支付宝小程序/Android/iOS/macOS/Windows/插件\n- 发布状态:developing/pending_review/published/rejected/deprecated\n- 价格类型:free/one_time/subscription\n\n### 5.2 AppUser (应用成员)\n- 核心字段:id, appId, userId, role\n- 角色类型:owner/admin/developer/viewer\n- 邀请状态:0-正常, 1-待确认, 2-已拒绝\n\n### 5.3 ShopOrder (订单)\n- 核心字段:orderId, orderNo, totalPrice, payPrice, payStatus\n- 订单状态:0未使用,1已完成,2已取消,3取消中,4退款申请中,5退款被拒绝,6退款成功\n- 支付方式:0余额,1微信,2支付宝,3银联,4现金,5POS机,6免费,7积分\n\n---\n\n## 六、权限控制说明\n\n项目使用Spring Security进行权限控制,主要权限前缀:\n- `app:` - 应用管理权限\n- `shop:` - 商城管理权限\n- `cms:` - 内容管理权限\n\n主要权限操作:\n- `:list` - 查询权限\n- `:save` - 新增权限\n- `:update` - 修改权限\n- `:remove` - 删除权限\n\n---\n\n## 七、模块依赖关系\n\n```\napp模块 (应用管理)\n ├── 依赖:用户系统 (common/system)\n ├── 依赖:支付系统 (payment)\n └── 关联:商城 (shop)、内容管理 (cms)\n\nshop模块 (商城)\n ├── 依赖:支付系统 (payment)\n └── 依赖:用户系统 (common/system)\n\ncms模块 (内容管理)\n └── 依赖:用户系统 (common/system)\n\npayment模块 (支付)\n └── 独立模块,被app和shop依赖\n```\n\n---\n\n报告完成!共分析了172个Controller,覆盖应用管理、商城、支付、内容管理四大业务模块。", + "timestamp": "2026-04-10T10:59:49.671Z", + "read": true + }, + { + "id": "msg-1775818820679-lu2dwx", + "from": "miniapp-explorer", + "to": "team-lead", + "type": "message", + "content": "\n# 微信小程序端完整功能分析报告\n\n## 一、项目概览\n\n这是一个基于 Taro + React 的微信小程序项目,采用 NutUI 组件库,主要功能是 SaaS 多租户电商平台的小程序端(桂乐淘)。\n\n---\n\n## 二、页面路由配置\n\n### 2.1 主包页面(TabBar)\n\n| 路由路径 | 页面功能 | 说明 |\n|---------|---------|------|\n| pages/index/index | 首页 | SaaS 产品展示、快捷导航、联系咨询 |\n| pages/cart/cart | 购物车 | 商品勾选、数量修改、结算跳转 |\n| pages/user/user | 个人中心 | 用户信息、订单入口、设置 |\n| pages/category/index | 商品分类 | CMS 文章分类浏览 |\n\n### 2.2 子包 - passport(认证模块)\n\n| 路由路径 | 页面功能 |\n|---------|---------|\n| passport/login | 账号密码登录 |\n| passport/sms-login | 短信验证码登录 |\n| passport/forget | 忘记密码 |\n| passport/setting | 服务配置 |\n| passport/agreement | 服务协议与隐私政策 |\n| passport/phone-auth/index | 手机号授权 |\n| passport/qr-login/index | 扫码登录 |\n| passport/qr-confirm/index | 扫码确认 |\n| passport/unified-qr/index | 统一二维码 |\n| passport/webview/index | 网页容器 |\n\n### 2.3 子包 - shop(商城模块)\n\n| 路由路径 | 页面功能 | API 接口 |\n|---------|---------|----------|\n| shop/category/index | 商品分类页 | cmsNavigation, cmsArticle |\n| shop/goodsDetail/index | 商品详情 | shopGoods, shopGoodsSpec, shopGoodsSku |\n| shop/orderConfirm/index | 订单确认 | shopUserAddress, payment, shopUserCoupon |\n| shop/orderConfirmCart/index | 购物车结算 | 同上 |\n| shop/orderDetail/index | 订单详情 | shopOrder |\n| shop/comments/index | 商品评价 | shopGoods |\n| shop/search/index | 商品搜索 | shopGoods |\n\n### 2.4 子包 - user(用户中心)\n\n| 路由路径 | 页面功能 | API 接口 |\n|---------|---------|----------|\n| user/order/order | 我的订单 | shopOrder |\n| user/order/logistics | 物流信息 | logistics |\n| user/order/evaluate | 订单评价 | shopOrderGoods |\n| user/order/refund | 退款售后 | afterSale |\n| user/order/progress | 进度查询 | shopOrder |\n| user/company/company | 企业认证 | company |\n| user/profile/profile | 个人资料 | user |\n| user/setting/setting | 账号设置 | - |\n| user/userVerify/index | 实名认证 | userVerify |\n| user/address/index | 收货地址 | shopUserAddress |\n| user/address/add | 新增地址 | shopUserAddress |\n| user/address/wxAddress | 微信地址 | - |\n| user/help/index | 帮助中心 | cmsArticle |\n| user/about/index | 关于我们 | - |\n| user/wallet/wallet | 钱包/余额 | balance-log |\n| user/coupon/index | 我的优惠券 | shopUserCoupon |\n| user/points/points | 积分中心 | points |\n| user/gift/index | 水票管理 | shopGift |\n| user/gift/redeem | 兑换水票 | shopGift |\n| user/gift/detail | 水票详情 | shopGift |\n| user/gift/add | 领取水票 | shopGift |\n| user/store/verification | 礼品卡核销 | shopStore |\n| user/store/orders | 门店订单 | shopOrder |\n| user/theme/index | 主题设置 | - |\n| user/poster/poster | 海报生成 | - |\n| user/chat/conversation | 会话列表 | shopChatConversation |\n| user/chat/message | 消息列表 | shopChatMessage |\n| user/chat/message/add | 发起聊天 | shopChatMessage |\n\n### 2.5 子包 - dealer(分销模块)\n\n| 路由路径 | 页面功能 | API 接口 |\n|---------|---------|----------|\n| dealer/index | 分销中心 | shopDealerUser |\n| dealer/apply/add | 申请成为分销商 | shopDealerApply |\n| dealer/withdraw/index | 提现申请 | shopDealerWithdraw |\n| dealer/orders/index | 分销订单 | shopDealerOrder |\n| dealer/capital/index | 资金明细 | shopDealerCapital |\n| dealer/team/index | 我的团队 | shopDealerReferee |\n| dealer/qrcode/index | 邀请二维码 | - |\n| dealer/invite-stats | 邀请统计 | - |\n| dealer/info | 分销商信息 | shopDealerUser |\n\n### 2.6 子包 - coupon(优惠券)\n\n| 路由路径 | 页面功能 | API 接口 |\n|---------|---------|----------|\n| coupon/index | 优惠券中心 | shopCoupon, shopUserCoupon |\n\n### 2.7 子包 - cms(内容管理)\n\n| 路由路径 | 页面功能 | API 接口 |\n|---------|---------|----------|\n| cms/category/index | CMS 分类 | cmsNavigation |\n| cms/detail/index | 文章详情 | cmsArticle |\n\n### 2.8 子包 - store(门店模块)\n\n| 路由路径 | 页面功能 | API 接口 |\n|---------|---------|----------|\n| store/index | 门店中心 | shopStore, shopStoreUser |\n| store/orders/index | 门店订单 | shopOrder |\n\n### 2.9 子包 - rider(配送模块)\n\n| 路由路径 | 页面功能 |\n|---------|---------|\n| rider/index | 配送员中心 |\n| rider/orders/index | 配送订单 |\n\n### 2.10 子包 - admin(管理模块)\n\n| 路由路径 | 页面功能 |\n|---------|---------|\n| admin/index | 管理员首页 |\n| admin/article/index | 文章管理 |\n\n---\n\n## 三、API 接口结构\n\n### 3.1 Shop 模块(商城)\n\n```\nshop/goods - 商品\nshop/goodsCategory - 商品分类\nshop/goodsSku - SKU\nshop/goodsSpec - 规格\nshop/order - 订单\nshop/orderGoods - 订单商品\nshop/userAddress - 收货地址\nshop/userCoupon - 用户优惠券\nshop/userReferee - 推荐关系\nshop/coupon - 优惠券\nshop/gift - 礼品/水票\nshop/express - 物流\nshop/expressTemplate - 运费模板\nshop/store - 门店\nshop/storeUser - 门店用户\nshop/storeRider - 配送员\nshop/merchant - 商户\nshop/dealerUser - 分销用户\nshop/dealerApply - 分销申请\nshop/dealerOrder - 分销订单\nshop/dealerCapital - 分销资金\nshop/dealerWithdraw - 分销提现\nshop/dealerReferee - 分销推荐\nshop/dealerSetting - 分销设置\nshop/commissionRole - 分销角色\nshop/chatConversation - 会话\nshop/chatMessage - 消息\n```\n\n### 3.2 User 模块\n\n```\nuser/balance-log - 余额日志\nuser/points - 积分\nuser/index - 用户信息\n```\n\n### 3.3 Passport 模块\n\n```\npassport/login - 登录\npassport/wx-login - 微信登录\npassport/qr-login - 扫码登录\n```\n\n### 3.4 System 模块\n\n```\nsystem/user - 用户管理\nsystem/company - 企业管理\nsystem/tenant - 租户管理\nsystem/role - 角色权限\nsystem/menu - 菜单\nsystem/payment - 支付方式\nsystem/parameter - 参数配置\nsystem/dict - 字典\nsystem/file - 文件上传\nsystem/setting - 系统设置\nsystem/access-key - 访问密钥\nsystem/appstore - 应用商店\nsystem/plugin - 插件管理\nsystem/version - 版本管理\nsystem/white-domain - 白名单域名\n```\n\n### 3.5 CMS 模块\n\n```\ncms/navigation - 导航分类\ncms/article - 文章内容\ncms/websiteField - 网站配置\n```\n\n---\n\n## 四、状态管理\n\n### 4.1 Hooks(useCart, useUser, useConfig)\n\n- **useCart**: 购物车状态管理,本地存储持久化\n- **useUser**: 用户登录状态、权限管理\n- **useConfig**: 网站配置(主题、站点信息)\n- **useDealerUser**: 分销用户状态\n- **useShopInfo**: 门店信息\n- **useTheme**: 主题样式管理\n\n### 4.2 本地存储\n\n- `access_token`: 登录令牌\n- `User`: 用户信息\n- `UserId`: 用户ID\n- `TenantId`: 租户ID\n- `cart_items`: 购物车商品\n- `checkout_items`: 结算商品\n- `config`: 网站配置\n- `search_history`: 搜索历史\n- `selected_store`: 选中的门店\n\n---\n\n## 五、核心业务流程\n\n### 5.1 登录流程\n\n1. 自动登录:微信 OpenID → loginByOpenId → 获取 token\n2. 账号登录:手机号+密码 → login\n3. 短信登录:手机号+验证码 → sms-login\n4. 扫码登录:qr-login → qr-confirm 确认\n\n### 5.2 商品购买流程\n\n1. 首页/分类 → 商品详情\n2. 选择规格(可选)\n3. 加入购物车 / 直接购买\n4. 购物车结算 / 立即购买 → 订单确认\n5. 选择地址、支付方式、优惠券\n6. 发起支付(微信支付/余额)\n7. 订单创建 → 支付回调 → 订单完成\n\n### 5.3 分销流程\n\n1. 申请成为分销商 → dealer/apply/add\n2. 获取邀请二维码 → dealer/qrcode\n3. 邀请好友注册 → 建立上下级关系\n4. 好友下单 → 获得佣金奖励\n5. 佣金提现 → dealer/withdraw\n\n---\n\n## 六、关键业务逻辑\n\n### 6.1 邀请关系追踪\n\n- 从分享链接进入时,解析 `inviter/source/t` 参数\n- 存储邀请参数,注册后绑定邀请关系\n- 支持多级分销(一级、二级、三级)\n\n### 6.2 优惠券系统\n\n- 支持满减券(10)、折扣券(20)、免费券(30)\n- 智能推荐最优优惠券\n- 使用条件校验(门槛金额)\n\n### 6.3 多门店支持\n\n- 用户可选择归属门店\n- 店员绑定门店关系\n- 门店订单管理\n- 礼品卡核销\n\n### 6.4 主题定制\n\n- 支持主题色配置\n- 渐变背景样式\n- 用户主题偏好存储\n\n---\n\n## 七、技术栈\n\n- **框架**: Taro 3.x + React\n- **UI组件**: NutUI (nutui/nutui-react-taro)\n- **图标**: @nutui/icons-react-taro\n- **状态管理**: React Hooks + 本地存储\n- **网络请求**: 统一 API 模块\n- **样式**: SCSS + Tailwind CSS\n", + "timestamp": "2026-04-10T11:00:20.679Z", + "read": true + } +] \ No newline at end of file diff --git a/src/admin/index.tsx b/src/admin/index.tsx index 16f0cab..ca9b8af 100644 --- a/src/admin/index.tsx +++ b/src/admin/index.tsx @@ -1,34 +1,48 @@ -import {useEffect} from 'react' -import {useUser} from "@/hooks/useUser"; -import {Empty} from '@nutui/nutui-react-taro'; -import {Text} from '@tarojs/components'; +import { View, Text } from '@tarojs/components' +import Taro from '@tarojs/taro' +import { ConfigProvider, Grid } from '@nutui/nutui-react-taro' +import { useUser } from '@/hooks/useUser' +import { useThemeStyles } from '@/hooks/useTheme' +import { Edit } from '@nutui/icons-react-taro' function Admin() { - const { - isAdmin - } = useUser(); - - useEffect(() => { - }, []); + const { isAdmin } = useUser() + const themeStyles = useThemeStyles() if (!isAdmin()) { return ( - - - - ); + + + 暂无访问权限 + + + ) } + return ( - <> - 待开发... - + + + 内容管理 + + 管理文章、内容等数据 + + + + + 管理工具 + + + Taro.navigateTo({ url: '/admin/article/index' })}> + + + + + + + + + + ) } diff --git a/src/api/app/appProduct/index.ts b/src/api/app/appProduct/index.ts new file mode 100644 index 0000000..e9c992e --- /dev/null +++ b/src/api/app/appProduct/index.ts @@ -0,0 +1,19 @@ +import request from '@/utils/request'; +import type { ApiResult, PageResult } from '@/api'; +import type { AppProduct } from './model'; + +const BASE = '/app/product'; + +/** 分页查询我的应用 */ +export async function pageMyApps(params?: { current?: number; size?: number }) { + const res = await request.get>>(BASE + '/my/page', { params }); + if (res.code === 0) return res.data; + return Promise.reject(new Error(res.message)); +} + +/** 获取我参与的应用列表 */ +export async function listJoinedApps(params?: { page?: number; limit?: number }) { + const res = await request.get>>(BASE + '/page', { params }); + if (res.code === 0) return res.data; + return Promise.reject(new Error(res.message)); +} diff --git a/src/api/app/appProduct/model/index.ts b/src/api/app/appProduct/model/index.ts new file mode 100644 index 0000000..9481304 --- /dev/null +++ b/src/api/app/appProduct/model/index.ts @@ -0,0 +1,51 @@ +export interface AppProduct { + productId?: number + productName?: string + productCode?: string + appType?: number + icon?: string + qrcode?: string + description?: string + domain?: string + homeUrl?: string + adminUrl?: string + version?: string + status?: number + statusText?: string + running?: number + expirationTime?: string + createTime?: string + updateTime?: string + publishStatus?: string +} + +export const APP_TYPE_NAME: Record = { + 10: '网站', + 20: '微信小程序', + 30: '抖音小程序', + 40: '百度小程序', + 50: '支付宝小程序', + 60: 'Android APP', + 70: 'iOS APP', + 80: 'macOS 应用', + 90: 'Windows 应用', + 100: '插件', +} + +export const STATUS_NAME: Record = { + 0: '未开通', + 1: '运行中', + 2: '维护中', + 3: '已关闭', + 4: '已欠费', + 5: '违规停机', +} + +export const STATUS_COLOR: Record = { + 0: '#6b7280', + 1: '#10b981', + 2: '#f59e0b', + 3: '#ef4444', + 4: '#ef4444', + 5: '#ef4444', +} diff --git a/src/api/app/notification/index.ts b/src/api/app/notification/index.ts new file mode 100644 index 0000000..cf49fc5 --- /dev/null +++ b/src/api/app/notification/index.ts @@ -0,0 +1,47 @@ +import request from '@/utils/request'; +import type { ApiResult, PageResult } from '@/api'; +import type { Notification, NotificationParam, UnreadCountResult } from './model'; + +const BASE = '/app/notification'; + +/** 分页查询通知列表 */ +export async function pageNotification(params: NotificationParam) { + const res = await request.get>>(BASE + '/page', { params }); + if (res.code === 0) return res.data; + return Promise.reject(new Error(res.message)); +} + +/** 查询最近通知 */ +export async function listRecentNotification(params?: { type?: string; limit?: number }) { + const res = await request.get>(BASE + '/recent', { params }); + if (res.code === 0 && res.data) return res.data; + return Promise.reject(new Error(res.message)); +} + +/** 获取未读数量统计 */ +export async function getUnreadCount() { + const res = await request.get>(BASE + '/unread-count'); + if (res.code === 0 && res.data) return res.data; + return Promise.reject(new Error(res.message)); +} + +/** 标记单条通知为已读 */ +export async function markNotificationRead(id: number) { + const res = await request.put>(BASE + `/read/${id}`); + if (res.code === 0) return res.message; + return Promise.reject(new Error(res.message)); +} + +/** 标记全部已读 */ +export async function markAllNotificationRead(data?: { type?: string }) { + const res = await request.put>(BASE + '/read-all', data); + if (res.code === 0) return res.message; + return Promise.reject(new Error(res.message)); +} + +/** 删除单条通知 */ +export async function removeNotification(id: number) { + const res = await request.del>(BASE + '/' + id); + if (res.code === 0) return res.message; + return Promise.reject(new Error(res.message)); +} diff --git a/src/api/app/notification/model/index.ts b/src/api/app/notification/model/index.ts new file mode 100644 index 0000000..a3c94bc --- /dev/null +++ b/src/api/app/notification/model/index.ts @@ -0,0 +1,41 @@ +import type { PageParam } from '@/api/index'; + +/** 通知类型 */ +export type NotificationType = 'ticket' | 'review' | 'system' | 'resource' | 'permission' | 'member' | 'payment'; + +/** 通知实体 */ +export interface Notification { + id?: number; + userId?: number; + type?: NotificationType; + title?: string; + content?: string; + isRead?: number; + refId?: number; + refType?: string; + linkUrl?: string; + senderId?: number; + senderName?: string; + senderAvatar?: string; + tenantId?: number; + createTime?: string; + updateTime?: string; +} + +/** 通知查询参数 */ +export interface NotificationParam extends PageParam { + type?: NotificationType | ''; + isRead?: number; +} + +/** 未读统计 */ +export interface UnreadCountResult { + total?: number; + ticket?: number; + review?: number; + system?: number; + resource?: number; + permission?: number; + member?: number; + payment?: number; +} diff --git a/src/api/app/ticket/index.ts b/src/api/app/ticket/index.ts new file mode 100644 index 0000000..34a0213 --- /dev/null +++ b/src/api/app/ticket/index.ts @@ -0,0 +1,54 @@ +import request from '@/utils/request'; +import type { ApiResult, PageResult } from '@/api'; +import type { Ticket, TicketReply, TicketSubmitForm, TicketQueryParams, TicketStats } from './model'; + +const BASE = '/app/ticket'; + +/** 查询我的工单(分页) */ +export async function myTickets(params: TicketQueryParams) { + const res = await request.get>>(BASE + '/my', { params }); + if (res.code === 0) return res.data; + return Promise.reject(new Error(res.message)); +} + +/** 提交工单 */ +export async function submitTicket(data: TicketSubmitForm) { + const res = await request.post>(BASE + '/submit', data); + if (res.code === 0) return res.data; + return Promise.reject(new Error(res.message)); +} + +/** 获取工单详情 */ +export async function getTicket(ticketId: number) { + const res = await request.get>(BASE + '/' + ticketId); + if (res.code === 0 && res.data) return res.data; + return Promise.reject(new Error(res.message)); +} + +/** 关闭工单 */ +export async function closeTicket(ticketId: number) { + const res = await request.put>(BASE + `/${ticketId}/close`); + if (res.code === 0) return res.message; + return Promise.reject(new Error(res.message)); +} + +/** 获取工单回复列表 */ +export async function getTicketReplies(ticketId: number) { + const res = await request.get>(BASE + `/${ticketId}/replies`); + if (res.code === 0 && res.data) return res.data; + return Promise.reject(new Error(res.message)); +} + +/** 提交工单回复 */ +export async function replyTicket(data: { ticketId: number; content: string }) { + const res = await request.post>(BASE + '/reply', data); + if (res.code === 0) return res.data; + return Promise.reject(new Error(res.message)); +} + +/** 获取工单统计 */ +export async function getTicketStats() { + const res = await request.get>(BASE + '/stats'); + if (res.code === 0 && res.data) return res.data; + return Promise.reject(new Error(res.message)); +} diff --git a/src/api/app/ticket/model/index.ts b/src/api/app/ticket/model/index.ts new file mode 100644 index 0000000..5cab23b --- /dev/null +++ b/src/api/app/ticket/model/index.ts @@ -0,0 +1,64 @@ +export type TicketPriority = 'low' | 'normal' | 'high' | 'urgent' +export type TicketStatus = 'pending' | 'assigned' | 'processing' | 'resolved' | 'closed' | 'rejected' +export type TicketCategory = 'bug' | 'feature' | 'consultation' | 'complaint' | 'other' + +export interface Ticket { + ticketId?: number + ticketNo?: string + title?: string + content?: string + productId?: number + productName?: string + category?: TicketCategory + priority?: TicketPriority + status?: TicketStatus + attachments?: string[] + submitUserId?: number + submitUserName?: string + submitUserAvatar?: string + assigneeId?: number + assigneeName?: string + assigneeAvatar?: string + createTime?: string + updateTime?: string + resolvedTime?: string + closedTime?: string + replyCount?: number + hasUnread?: boolean +} + +export interface TicketReply { + replyId?: number + ticketId?: number + content?: string + attachments?: string[] + userId?: number + userName?: string + userAvatar?: string + isStaff?: boolean + createTime?: string +} + +export interface TicketSubmitForm { + title: string + content: string + productId?: number + category: TicketCategory + priority: TicketPriority +} + +export interface TicketQueryParams { + status?: TicketStatus + category?: TicketCategory + keywords?: string + page?: number + limit?: number +} + +export interface TicketStats { + total?: number + pending?: number + processing?: number + resolved?: number + closed?: number +} diff --git a/src/app.config.ts b/src/app.config.ts index dfc14d2..4f4a9e9 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -18,7 +18,8 @@ export default { "phone-auth/index", 'qr-login/index', 'qr-confirm/index', - 'unified-qr/index' + 'unified-qr/index', + 'webview/index' ] }, { @@ -65,7 +66,9 @@ export default { "chat/conversation/index", "chat/message/index", "chat/message/add", - "chat/message/detail" + "chat/message/detail", + "notification/index", + "apps/index" ] }, { diff --git a/src/app.scss b/src/app.scss index ae872ba..001e324 100644 --- a/src/app.scss +++ b/src/app.scss @@ -44,16 +44,12 @@ button[open-type="getPhoneNumber"] { padding: 0 !important; margin: 0 !important; border: none !important; - line-height: inherit !important; - border-radius: 0 !important; } button[open-type="chooseAvatar"] { background: none !important; padding: 0 !important; margin: 0 !important; border: none !important; - line-height: inherit !important; - border-radius: 0 !important; } .buy-btn{ @@ -101,3 +97,73 @@ image { .admin-feature-item:active { transform: scale(0.95); } + +/* 科技风格页面动画 */ + +/* 粒子动画 */ +@keyframes float { + 0%, 100% { + transform: translateY(0) scale(1); + opacity: 1; + } + 50% { + transform: translateY(-15px) scale(1.1); + opacity: 0.7; + } +} + +@keyframes pulse { + 0%, 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.5); + opacity: 0.5; + } +} + +@keyframes scanline { + 0% { + top: 0; + opacity: 0; + } + 10% { + opacity: 1; + } + 90% { + opacity: 1; + } + 100% { + top: 100%; + opacity: 0; + } +} + +@keyframes rotate { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@keyframes glow { + 0%, 100% { + box-shadow: 0 0 20px rgba(168, 85, 247, 0.4), 0 0 40px rgba(168, 85, 247, 0.2); + } + 50% { + box-shadow: 0 0 30px rgba(168, 85, 247, 0.6), 0 0 60px rgba(168, 85, 247, 0.3); + } +} + +/* 粒子元素样式 */ +.particle { + animation: float 3s ease-in-out infinite, pulse 2s ease-in-out infinite; +} + +/* 按钮发光效果 */ +.authorize-btn:not([disabled]) { + animation: glow 2s ease-in-out infinite; +} diff --git a/src/components/AdminPanel.tsx b/src/components/AdminPanel.tsx index 8aa3d9b..7ef4c04 100644 --- a/src/components/AdminPanel.tsx +++ b/src/components/AdminPanel.tsx @@ -30,7 +30,7 @@ const AdminPanel: React.FC = ({ title: '统一扫码', description: '扫码登录和核销一体化功能', icon: , - color: 'bg-blue-50 border-blue-200', + color: 'bg-blue-500 border-blue-200', onClick: () => { navTo('/passport/unified-qr/index', true); onClose?.(); diff --git a/src/dealer/index.tsx b/src/dealer/index.tsx index bdf8b3e..a56dcf9 100644 --- a/src/dealer/index.tsx +++ b/src/dealer/index.tsx @@ -211,7 +211,7 @@ const DealerIndex: React.FC = () => { > navigateToPage('/dealer/orders/index')}> - + diff --git a/src/dealer/invite-stats/index.tsx b/src/dealer/invite-stats/index.tsx index 3139ad4..c1da8f4 100644 --- a/src/dealer/invite-stats/index.tsx +++ b/src/dealer/invite-stats/index.tsx @@ -137,7 +137,7 @@ const InviteStatsPage: React.FC = () => { ) : inviteStats ? ( - + {inviteStats.totalInvites || 0} diff --git a/src/developer/index.tsx b/src/developer/index.tsx index 0a023fe..ca85d76 100644 --- a/src/developer/index.tsx +++ b/src/developer/index.tsx @@ -211,7 +211,7 @@ const DealerIndex: React.FC = () => { > navigateToPage('/rider/orders/index')}> - + diff --git a/src/pages/user/components/UserGrid.tsx b/src/pages/user/components/UserGrid.tsx index 2527bf1..10e98b9 100644 --- a/src/pages/user/components/UserGrid.tsx +++ b/src/pages/user/components/UserGrid.tsx @@ -7,13 +7,12 @@ import { Location, Tips, Ask, - // Dongdong, People, - // AfterSaleService, Logout, Shop, - Jdl, - Service + Service, + Message, + Apps } from '@nutui/icons-react-taro' import {useUser} from "@/hooks/useUser"; @@ -26,7 +25,6 @@ const UserCell = () => { content: '确定要退出登录吗?', success: function (res) { if (res.confirm) { - // 使用 useUser hook 的 logoutUser 方法 logoutUser(); Taro.reLaunch({ url: '/pages/index/index' @@ -54,7 +52,7 @@ const UserCell = () => { {hasRole('store') && ( navTo('/store/index', true)}> - + @@ -62,10 +60,10 @@ const UserCell = () => { )} {hasRole('developer') && ( - navTo('/rider/index', true)}> + navTo('/passport/webview/index?url=https://websopy.websoft.top/developer', true)}> - - + + @@ -81,6 +79,22 @@ const UserCell = () => { )} + navTo('/user/apps/index', true)}> + + + + + + + + navTo('/user/notification/index', true)}> + + + + + + + navTo('/user/address/index', true)}> @@ -89,15 +103,14 @@ const UserCell = () => { - navTo('/user/help/index')}> + navTo('/user/help/index')}> - + - {/* 修改联系我们为微信客服 */} - navTo('/user/userVerify/index', true)}> + navTo('/user/userVerify/index', true)}> @@ -119,7 +132,7 @@ const UserCell = () => { - navTo('/dealer/team/index', true)}> + navTo('/dealer/team/index', true)}> @@ -127,35 +140,18 @@ const UserCell = () => { - {/* navTo('/dealer/qrcode/index', true)}>*/} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/**/} - - {/* navTo('/admin/index', true)}>*/} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/**/} - - - navTo('/user/about/index')}> + navTo('/user/about/index')}> - + - + - + @@ -163,53 +159,6 @@ const UserCell = () => { - {/**/} - {/* 账号管理*/} - {/* */} - {/* */} - {/* navTo('/user/profile/profile', true)}>*/} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - - {/* navTo('/user/theme/index', true)}>*/} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - - {/* navTo('/user/about/index')}>*/} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - {/* */} - - {/* */} - {/* */} - {/**/} ) } diff --git a/src/passport/qr-confirm/index.tsx b/src/passport/qr-confirm/index.tsx index a5fcc5a..bbfa8bc 100644 --- a/src/passport/qr-confirm/index.tsx +++ b/src/passport/qr-confirm/index.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import { View, Text } from '@tarojs/components'; import { Loading, Card, Button } from '@nutui/nutui-react-taro'; -import { Success, Failure, Tips, User, Checklist } from '@nutui/icons-react-taro'; +import { Success, Failure, Tips, User } from '@nutui/icons-react-taro'; import Taro, { useRouter } from '@tarojs/taro'; import { confirmQRLogin } from '@/api/passport/qr-login'; import { loginByOpenId } from '@/api/passport/wx-login'; @@ -51,7 +51,7 @@ interface LoginResponse { message: string; } -// 协议弹窗类型 +// 协议类型 type AgreementType = 'service' | 'privacy' | null; const QRConfirmPage: React.FC = () => { @@ -65,7 +65,6 @@ const QRConfirmPage: React.FC = () => { const [needAuth, setNeedAuth] = useState(false); // 是否需要手机号授权 const [authLoading, setAuthLoading] = useState(false); // 授权中状态 const [agreementChecked, setAgreementChecked] = useState(false); // 协议勾选状态 - const [showAgreementPopup, setShowAgreementPopup] = useState(null); // 协议弹窗 useEffect(() => { // 从 URL 参数中获取 token @@ -104,7 +103,7 @@ const QRConfirmPage: React.FC = () => { /** * 自动确认登录(URL 扫码场景) */ - const handleAutoConfirm = async (loginToken: string) => { + const handleAutoConfirm = async (_: string) => { try { setLoading(true); @@ -406,140 +405,288 @@ const QRConfirmPage: React.FC = () => { }); }; - // 打开协议弹窗 + // 打开协议页面(使用网页版) const openAgreement = (type: AgreementType) => { - setShowAgreementPopup(type); + const urlMap = { + service: 'https://websopy.websoft.top/agreement', + privacy: 'https://websopy.websoft.top/privacy', + }; + if (!type) return; + const targetUrl = encodeURIComponent(urlMap[type]); + Taro.navigateTo({ + url: `/passport/webview/index?url=${targetUrl}` + }); }; - // 关闭协议弹窗 - const closeAgreement = () => { - setShowAgreementPopup(null); - }; - - // 渲染协议弹窗内容 - const renderAgreementContent = () => { - if (showAgreementPopup === 'service') { - return ( - - - 服务协议 - - - 欢迎使用我们的服务! - 1. 服务条款 - 本服务协议是您与我们之间关于使用我们提供的各项服务的协议。 - 2. 账号注册 - 您需要注册账号才能使用我们的部分服务。注册时您需要提供真实、准确的信息。 - 3. 服务使用 - 您同意遵守相关法律法规,不得利用我们的服务从事违法违规活动。 - 4. 隐私保护 - 我们重视您的隐私保护,具体请参见《隐私政策》。 - 5. 协议修改 - 我们有权在必要时修改本协议,修改后的协议将在公布时立即生效。 - - - - ); - } - - if (showAgreementPopup === 'privacy') { - return ( - - - 隐私政策 - - - 我们非常重视您的隐私保护。 - 1. 信息收集 - 我们可能会收集您的手机号、微信信息等,用于提供服务和身份验证。 - 2. 信息使用 - 我们仅将您的信息用于提供服务、改进用户体验和保障账号安全。 - 3. 信息保护 - 我们采用行业标准的加密技术保护您的信息安全。 - 4. 信息共享 - 我们不会将您的个人信息出售或分享给第三方,除非获得您的同意或法律法规要求。 - 5. 联系我们 - 如有任何隐私相关问题,请联系我们。 - - - - ); - } - - return null; - }; - - // 授权登录页面(参考权大师风格) + // 授权登录页面 - 科技风格 const renderAuthPage = () => { return ( - + - {/* Logo 区域 */} - - {/* Logo */} - + {/* 背景科技元素 */} + {/* 1. 网格背景 */} + + + {/* 2. 渐变光晕 - 左上 */} + + + {/* 3. 渐变光晕 - 右下 */} + + + {/* 4. 动态粒子光点 */} + {[ + { top: '15%', left: '20%', size: 4, delay: '0s' }, + { top: '25%', left: '80%', size: 3, delay: '0.5s' }, + { top: '40%', left: '15%', size: 5, delay: '1s' }, + { top: '35%', left: '85%', size: 3, delay: '1.5s' }, + { top: '55%', left: '10%', size: 4, delay: '2s' }, + { top: '60%', left: '90%', size: 3, delay: '0.3s' }, + { top: '75%', left: '25%', size: 4, delay: '1.2s' }, + { top: '80%', left: '75%', size: 5, delay: '0.8s' }, + ].map((particle, index) => ( + + ))} + + {/* 5. 扫描线效果 */} + + + {/* 主内容区域 */} + + + {/* Logo 区域 */} + + {/* 科技感 Logo 容器 */} + + {/* Logo 内光效 */} + + {/* Logo 图标 */} + 🔐 + + + {/* 品牌名 - 渐变文字 */} + WebSopy + + {/* 标语 */} + 智能连接 · 安全登录 + + {/* 分割线 */} + - {/* 品牌名 */} - websopy + {/* 登录卡片 */} + + {/* 提示图标 */} + + + 📱 + + - {/* 标语 */} - 构建下一代 AI 应用 - + {/* 标题 */} + 授权登录 - {/* 底部操作区域 */} - - {/* 主按钮 - 手机号授权登录 */} -
- -
- {/**/} - {/* {authLoading ? '授权中...' : '手机号授权登录'}*/} - {/**/} +
{/* 取消按钮 */} - 取消 + 取消 {/* 协议勾选 */} setAgreementChecked(!agreementChecked)} > - {agreementChecked && ( - - )} + + {agreementChecked && ( + + )} + - - 我已阅读并同意权大师的 + + 我已阅读并同意 { e.stopPropagation(); openAgreement('service'); @@ -547,9 +694,9 @@ const QRConfirmPage: React.FC = () => { > 《服务协议》 - + { e.stopPropagation(); openAgreement('privacy'); @@ -558,16 +705,34 @@ const QRConfirmPage: React.FC = () => { 《隐私政策》 -
- {/* 协议弹窗 */} - {showAgreementPopup && ( - - - {renderAgreementContent()} - + {/* 底部装饰 */} + + + + 安全加密连接 + + - )} + ); }; @@ -781,7 +946,7 @@ const QRConfirmPage: React.FC = () => { {/* Token 信息 */} {token && !loading && !confirmed && !needAuth && ( - + 登录令牌:{token.substring(0, 20)}...{token.substring(token.length - 10)} diff --git a/src/passport/unified-qr/index.tsx b/src/passport/unified-qr/index.tsx index c229ad6..4e7674e 100644 --- a/src/passport/unified-qr/index.tsx +++ b/src/passport/unified-qr/index.tsx @@ -292,7 +292,7 @@ const UnifiedQRPage: React.FC = () => { )} {/* 功能说明 */} - + diff --git a/src/passport/webview/index.config.ts b/src/passport/webview/index.config.ts new file mode 100644 index 0000000..64848c3 --- /dev/null +++ b/src/passport/webview/index.config.ts @@ -0,0 +1,5 @@ +export default { + navigationBarTitleText: '详情', + navigationBarBackgroundColor: '#ffffff', + navigationBarTextStyle: 'black', +}; diff --git a/src/passport/webview/index.tsx b/src/passport/webview/index.tsx new file mode 100644 index 0000000..aa14a87 --- /dev/null +++ b/src/passport/webview/index.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { WebView } from '@tarojs/components'; +import Taro, { useRouter } from '@tarojs/taro'; + +/** + * 通用 WebView 页面 + * 通过 url 参数指定要加载的网页地址 + * 用于显示注册协议、隐私政策等网页内容 + */ +const WebViewPage: React.FC = () => { + const router = useRouter(); + const url = decodeURIComponent(router.params.url || ''); + + if (!url) { + Taro.navigateBack(); + return null; + } + + return ; +}; + +export default WebViewPage; diff --git a/src/rider/index.tsx b/src/rider/index.tsx index 42ad0f7..b325d37 100644 --- a/src/rider/index.tsx +++ b/src/rider/index.tsx @@ -1,14 +1,58 @@ -import React from 'react' +import React, { useCallback, useState, useEffect } from 'react' import Taro from '@tarojs/taro' -import {View, Text} from '@tarojs/components' -import {Avatar, Button} from '@nutui/nutui-react-taro' -import {User} from '@nutui/icons-react-taro' -import {useThemeStyles} from '@/hooks/useTheme' -import {useUser} from '@/hooks/useUser' +import { View, Text } from '@tarojs/components' +import { Avatar, Button, ConfigProvider, Grid, Empty } from '@nutui/nutui-react-taro' +import { User, Location, Order, Refresh } from '@nutui/icons-react-taro' +import { useThemeStyles } from '@/hooks/useTheme' +import { useUser } from '@/hooks/useUser' +import { listShopOrder } from '@/api/shop/shopOrder' +import type { ShopOrder } from '@/api/shop/shopOrder/model' const RiderIndex: React.FC = () => { const themeStyles = useThemeStyles() - const {isLoggedIn, loading: userLoading, getAvatarUrl, getDisplayName} = useUser() + const { isLoggedIn, loading: userLoading, getAvatarUrl, getDisplayName } = useUser() + + const [stats, setStats] = useState({ pending: 0, delivering: 0, completed: 0, today: 0 }) + const [loading, setLoading] = useState(false) + + const loadStats = useCallback(async () => { + const userId = Taro.getStorageSync('UserId') + if (!userId) return + + setLoading(true) + try { + // 查询待配送和配送中的订单 + const [pendingRes, deliveringRes, completedRes] = await Promise.allSettled([ + listShopOrder({ riderId: Number(userId), orderStatus: 30, page: 1, limit: 1 }), + listShopOrder({ riderId: Number(userId), orderStatus: 40, page: 1, limit: 1 }), + listShopOrder({ riderId: Number(userId), orderStatus: 50, page: 1, limit: 1 }), + ]) + + const pending = pendingRes.status === 'fulfilled' ? (pendingRes.value?.count || 0) : 0 + const delivering = deliveringRes.status === 'fulfilled' ? (deliveringRes.value?.count || 0) : 0 + const completed = completedRes.status === 'fulfilled' ? (completedRes.value?.count || 0) : 0 + + setStats({ pending, delivering, completed, today: pending + delivering }) + } catch (e) { + console.error('加载骑手统计失败', e) + } finally { + setLoading(false) + } + }, []) + + useEffect(() => { + if (isLoggedIn) { + loadStats() + } + }, [isLoggedIn, loadStats]) + + const navigateToPage = (url: string) => { + if (!isLoggedIn) { + Taro.showToast({ title: '请先登录', icon: 'none', duration: 1500 }) + return + } + Taro.navigateTo({ url }) + } if (!isLoggedIn && !userLoading) { return ( @@ -16,7 +60,7 @@ const RiderIndex: React.FC = () => { 请先登录后再进入骑手中心 - @@ -25,39 +69,129 @@ const RiderIndex: React.FC = () => { ) } + const statCards = [ + { label: '待取货', value: stats.pending, color: 'bg-orange-50', textColor: 'text-orange-600' }, + { label: '配送中', value: stats.delivering, color: 'bg-blue-50', textColor: 'text-blue-600' }, + { label: '已完成', value: stats.completed, color: 'bg-green-50', textColor: 'text-green-600' }, + { label: '今日任务', value: stats.today, color: 'bg-purple-50', textColor: 'text-purple-600' }, + ] + return ( + {/* 头部信息 */} + + + - } - className="mr-4" - style={{border: '2px solid rgba(255, 255, 255, 0.3)'}} - /> - - {getDisplayName()} - - 骑手中心 + + } + className="mr-4" + style={{ border: '2px solid rgba(255, 255, 255, 0.3)' }} + /> + + {getDisplayName()} + + 骑手中心 + + - - - 常用功能 - - - + {/* 数据统计卡片 */} + + 今日概览 + + {statCards.map((card) => ( + + {card.value} + {card.label} + + ))} + + {/* 快捷功能入口 */} + + 快捷功能 + + + navigateToPage('/rider/orders/index')}> + + + + + {stats.pending > 0 && ( + + {stats.pending > 9 ? '9+' : stats.pending} + + )} + + + + navigateToPage('/rider/orders/index')}> + + + + + {stats.delivering > 0 && ( + + {stats.delivering > 9 ? '9+' : stats.delivering} + + )} + + + + navigateToPage('/rider/orders/index')}> + + + + + + + + + + + {/* 提示信息 */} + {stats.today === 0 && !loading && ( + + + + )} + + ) } export default RiderIndex - diff --git a/src/store/index.tsx b/src/store/index.tsx index 37ef85a..2f7ff70 100644 --- a/src/store/index.tsx +++ b/src/store/index.tsx @@ -240,7 +240,7 @@ const StoreIndex: React.FC = () => { > navigateToPage('/store/orders/index')}> - + diff --git a/src/user/apps/index.config.ts b/src/user/apps/index.config.ts new file mode 100644 index 0000000..e8cce52 --- /dev/null +++ b/src/user/apps/index.config.ts @@ -0,0 +1,3 @@ +export default defineAppConfig({ + navigationBarTitleText: '我的应用', +}) diff --git a/src/user/apps/index.tsx b/src/user/apps/index.tsx new file mode 100644 index 0000000..e12bbac --- /dev/null +++ b/src/user/apps/index.tsx @@ -0,0 +1,192 @@ +import { useCallback, useState } from 'react' +import Taro, { useDidShow } from '@tarojs/taro' +import { View, Text } from '@tarojs/components' +import { Empty, InfiniteLoading, PullToRefresh, Tag } from '@nutui/nutui-react-taro' +import { useThemeStyles } from '@/hooks/useTheme' +import { listJoinedApps } from '@/api/app/appProduct' +import type { AppProduct } from '@/api/app/appProduct/model' +import { APP_TYPE_NAME, STATUS_NAME, STATUS_COLOR } from '@/api/app/appProduct/model' +import dayjs from 'dayjs' + +const MyAppsPage = () => { + const themeStyles = useThemeStyles() + const [list, setList] = useState([]) + const [loading, setLoading] = useState(false) + const [hasMore, setHasMore] = useState(true) + const [current, setCurrent] = useState(1) + + const loadApps = useCallback(async (isRefresh = false) => { + if (loading) return + setLoading(true) + try { + const page = isRefresh ? 1 : current + const res = await listJoinedApps({ page, limit: 20 }) + const newList = res?.list || [] + if (isRefresh) { + setList(newList) + setCurrent(2) + } else { + setList(prev => [...prev, ...newList]) + setCurrent(prev => prev + 1) + } + setHasMore(newList.length >= 20) + } catch (e) { + console.error('加载应用失败', e) + } finally { + setLoading(false) + } + }, [loading, current]) + + useDidShow(() => { + loadApps(true) + }) + + const handleEnterAdmin = (app: AppProduct) => { + if (!app.adminUrl) { + Taro.showToast({ title: '暂无管理入口', icon: 'none' }) + return + } + const url = app.adminUrl.startsWith('http') ? app.adminUrl : `https://${app.adminUrl}` + Taro.navigateTo({ url: `/passport/webview/index?url=${encodeURIComponent(url)}` }) + } + + const handleEnterHome = (app: AppProduct) => { + if (!app.homeUrl && !app.domain) { + Taro.showToast({ title: '暂无访问地址', icon: 'none' }) + return + } + const url = (app.homeUrl || app.domain || '').startsWith('http') + ? (app.homeUrl || app.domain) + : `https://${app.homeUrl || app.domain}` + Taro.navigateTo({ url: `/passport/webview/index?url=${encodeURIComponent(url)}` }) + } + + return ( + + {/* 头部 */} + + 我的应用 + + 管理您开通的应用与服务 + + + + loadApps(true)} className="mt-4"> + + {list.length === 0 && !loading ? ( + + + + Taro.navigateTo({ + url: '/passport/webview/index?url=' + encodeURIComponent('https://websopy.websoft.top/products') + })} + > + 去开通应用 > + + + + ) : ( + list.map((app) => { + const statusName = STATUS_NAME[app.status ?? 0] || '未知' + const statusColor = STATUS_COLOR[app.status ?? 0] || '#6b7280' + const typeName = APP_TYPE_NAME[app.appType ?? 10] || '应用' + const isExpired = app.expirationTime && dayjs(app.expirationTime).isBefore(dayjs()) + + return ( + + + {/* 图标 */} + + {app.icon ? ( + + ) : ( + {typeName[0]} + )} + + + {/* 信息 */} + + + + {app.productName || '未命名应用'} + + + {isExpired ? '已过期' : statusName} + + + + + {typeName} + {app.version && ( + v{app.version} + )} + + + {app.description && ( + {app.description} + )} + + {/* 操作按钮 */} + + {app.adminUrl && ( + handleEnterAdmin(app)} + > + 管理后台 + + )} + {(app.homeUrl || app.domain) && ( + handleEnterHome(app)} + > + 访问前台 + + )} + {app.expirationTime && ( + + + 到期:{dayjs(app.expirationTime).format('YYYY-MM-DD')} + + + )} + + + + + ) + }) + )} + + + loadApps(false)} + loading={loading} + > + + + {loading ? '加载中...' : hasMore ? '下拉加载更多' : '没有更多了'} + + + + + + ) +} + +export default MyAppsPage diff --git a/src/user/gift/redeem.tsx b/src/user/gift/redeem.tsx index ca725ba..7e1c153 100644 --- a/src/user/gift/redeem.tsx +++ b/src/user/gift/redeem.tsx @@ -160,7 +160,7 @@ const GiftCardRedeem = () => { {!redeemSuccess ? ( <> {/* 兑换说明 */} - + 兑换说明 diff --git a/src/user/gift/use.tsx b/src/user/gift/use.tsx index 2aec304..5839d2d 100644 --- a/src/user/gift/use.tsx +++ b/src/user/gift/use.tsx @@ -264,7 +264,7 @@ const GiftCardUse = () => {
{gift.contactInfo && ( - + 客服联系方式 {gift.contactInfo} diff --git a/src/user/notification/index.config.ts b/src/user/notification/index.config.ts new file mode 100644 index 0000000..d75227f --- /dev/null +++ b/src/user/notification/index.config.ts @@ -0,0 +1,4 @@ +export default defineAppConfig({ + navigationBarTitleText: '消息通知', + navigationBarBackgroundColor: '#ffffff', +}) diff --git a/src/user/notification/index.tsx b/src/user/notification/index.tsx new file mode 100644 index 0000000..a7c61da --- /dev/null +++ b/src/user/notification/index.tsx @@ -0,0 +1,206 @@ +import { useCallback, useState } from 'react' +import Taro, { useDidShow } from '@tarojs/taro' +import { View, Text } from '@tarojs/components' +import { Cell, CellGroup, Empty, InfiniteLoading, PullToRefresh, Tag, Button } from '@nutui/nutui-react-taro' +import { useThemeStyles } from '@/hooks/useTheme' +import { pageNotification, getUnreadCount, markNotificationRead, markAllNotificationRead } from '@/api/app/notification' +import type { Notification } from '@/api/app/notification/model' +import dayjs from 'dayjs' +import relativeTime from 'dayjs/plugin/relativeTime' +import 'dayjs/locale/zh-cn' + +dayjs.extend(relativeTime) +dayjs.locale('zh-cn') + +/** 通知类型配置 */ +const TYPE_CONFIG: Record = { + system: { label: '系统', color: '#3b82f6' }, + ticket: { label: '工单', color: '#f59e0b' }, + review: { label: '审核', color: '#8b5cf6' }, + resource: { label: '资源', color: '#10b981' }, + permission: { label: '权限', color: '#ef4444' }, + member: { label: '成员', color: '#06b6d4' }, + payment: { label: '支付', color: '#ec4899' }, +} + +const NotificationPage = () => { + const themeStyles = useThemeStyles() + const [list, setList] = useState([]) + const [loading, setLoading] = useState(false) + const [hasMore, setHasMore] = useState(true) + const [page, setPage] = useState(1) + const [unreadTotal, setUnreadTotal] = useState(0) + + const loadNotifications = useCallback(async (isRefresh = false) => { + if (loading) return + setLoading(true) + try { + const currentPage = isRefresh ? 1 : page + const res = await pageNotification({ page: currentPage, limit: 20 }) + const newList = res?.list || [] + if (isRefresh) { + setList(newList) + setPage(2) + } else { + setList(prev => [...prev, ...newList]) + setPage(prev => prev + 1) + } + setHasMore(newList.length >= 20) + } catch (e) { + console.error('加载通知失败', e) + } finally { + setLoading(false) + } + }, [loading, page]) + + const loadUnreadCount = useCallback(async () => { + try { + const res = await getUnreadCount() + setUnreadTotal(res?.total || 0) + } catch { + // ignore + } + }, []) + + useDidShow(() => { + loadNotifications(true) + loadUnreadCount() + }) + + const handleMarkRead = async (item: Notification) => { + if (item.isRead === 1 || !item.id) return + try { + await markNotificationRead(item.id) + setList(prev => prev.map(n => n.id === item.id ? { ...n, isRead: 1 } : n)) + loadUnreadCount() + } catch (e) { + console.error('标记已读失败', e) + } + } + + const handleMarkAllRead = async () => { + try { + await markAllNotificationRead() + setList(prev => prev.map(n => ({ ...n, isRead: 1 }))) + setUnreadTotal(0) + Taro.showToast({ title: '已全部标记为已读', icon: 'success' }) + } catch (e) { + console.error('标记全部已读失败', e) + } + } + + const handleClick = (item: Notification) => { + handleMarkRead(item) + if (item.linkUrl) { + // 外链通过 WebView 打开 + Taro.navigateTo({ url: `/passport/webview/index?url=${encodeURIComponent(item.linkUrl)}` }) + } + } + + const formatTime = (time?: string) => { + if (!time) return '' + return dayjs(time).fromNow() + } + + if (list.length === 0 && !loading) { + return ( + + + 消息通知 + + + + ) + } + + return ( + + {/* 头部 */} + + + + 消息通知 + {unreadTotal > 0 && ( + + {unreadTotal} 条未读 + + )} + + {unreadTotal > 0 && ( + + )} + + + + loadNotifications(true)}> + {/* 通知列表 */} + + {list.map((item) => { + const typeConf = TYPE_CONFIG[item.type || ''] || TYPE_CONFIG.system + return ( + handleClick(item)} + > + + {/* 未读标记 */} + + {item.isRead === 0 ? ( + + ) : ( + + )} + + + {/* 内容 */} + + + + {typeConf.label} + + + {item.title || '系统通知'} + + + {item.content && ( + {item.content} + )} + + {formatTime(item.createTime)} + + + + + ) + })} + + + {/* 加载更多 */} + loadNotifications(false)} + loading={loading} + > + + + {loading ? '加载中...' : hasMore ? '下拉加载更多' : '没有更多了'} + + + + + + ) +} + +export default NotificationPage