Sfoglia il codice sorgente

初始提交:新大洲车联网CRM系统管理后台前端代码

- 更新 .gitignore,过滤不需要提交的文件
- 移除环境变量文件和 package-lock.json
- 添加社区模块、车辆模块等新功能
wangmeng 2 mesi fa
parent
commit
753753f6a6
100 ha cambiato i file con 2195 aggiunte e 18499 eliminazioni
  1. 0 37
      .env
  2. 0 37
      .env.dev
  3. 0 34
      .env.local
  4. 0 34
      .env.prod
  5. 0 34
      .env.stage
  6. 0 34
      .env.test
  7. 84 7
      .gitignore
  8. 60 271
      README.md
  9. 2 2
      index.html
  10. 0 17486
      package-lock.json
  11. 11 10
      package.json
  12. 38 0
      src/api/community/comment/index.ts
  13. 72 0
      src/api/community/post/index.ts
  14. 51 0
      src/api/community/sensitive/index.ts
  15. 55 0
      src/api/community/topic/index.ts
  16. 78 0
      src/api/system/version/index.ts
  17. 14 0
      src/api/vehicle/alarm/index.ts
  18. 2 2
      src/components/DiyEditor/components/mobile/Carousel/config.ts
  19. 1 1
      src/components/DiyEditor/components/mobile/NoticeBar/config.ts
  20. 8 8
      src/components/DiyEditor/components/mobile/TabBar/config.ts
  21. 1 1
      src/components/DiyEditor/components/mobile/UserCard/index.vue
  22. 1 1
      src/components/DiyEditor/index.vue
  23. 2 1
      src/components/DocAlert/index.vue
  24. 1 1
      src/components/FormCreate/src/useFormCreateDesigner.ts
  25. 1 1
      src/components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue
  26. 1 1
      src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue
  27. 1 1
      src/components/bpmnProcessDesigner/package/penal/listeners/ElementListeners.vue
  28. 1 1
      src/components/bpmnProcessDesigner/package/penal/listeners/UserTaskListeners.vue
  29. 45 10
      src/config/axios/service.ts
  30. 1 1
      src/layout/components/UserInfo/src/UserInfo.vue
  31. 1 1
      src/permission.ts
  32. 10 0
      src/router/modules/remaining.ts
  33. 1 1
      src/store/modules/tagsView.ts
  34. 1 1
      src/utils/constants.ts
  35. 1 1
      src/utils/formCreate.ts
  36. 47 379
      src/views/Home/Index.vue
  37. 9 10
      src/views/Login/Login.vue
  38. 1 1
      src/views/Login/SocialLogin.vue
  39. 6 16
      src/views/Login/components/LoginForm.vue
  40. 1 1
      src/views/Login/components/MobileForm.vue
  41. 2 2
      src/views/Login/components/SSOLogin.vue
  42. 1 1
      src/views/ai/chat/index/components/message/MessageListEmpty.vue
  43. 1 1
      src/views/ai/chat/manager/index.vue
  44. 1 1
      src/views/ai/image/manager/index.vue
  45. 1 1
      src/views/ai/knowledge/knowledge/index.vue
  46. 1 1
      src/views/ai/mindmap/manager/index.vue
  47. 1 1
      src/views/ai/model/apiKey/index.vue
  48. 1 1
      src/views/ai/model/chatRole/index.vue
  49. 1 1
      src/views/ai/model/model/index.vue
  50. 1 1
      src/views/ai/model/tool/index.vue
  51. 1 1
      src/views/ai/music/manager/index.vue
  52. 1 1
      src/views/ai/utils/constants.ts
  53. 1 1
      src/views/ai/utils/utils.ts
  54. 1 1
      src/views/ai/write/manager/index.vue
  55. 1 1
      src/views/bpm/category/index.vue
  56. 1 1
      src/views/bpm/form/index.vue
  57. 1 1
      src/views/bpm/group/index.vue
  58. 1 1
      src/views/bpm/model/definition/index.vue
  59. 1 1
      src/views/bpm/oa/leave/index.vue
  60. 1 1
      src/views/bpm/processExpression/index.vue
  61. 1 1
      src/views/bpm/processInstance/index.vue
  62. 1 1
      src/views/bpm/processInstance/manager/index.vue
  63. 1 1
      src/views/bpm/processInstance/report/index.vue
  64. 1 1
      src/views/bpm/processListener/index.vue
  65. 1 1
      src/views/bpm/task/copy/index.vue
  66. 4 4
      src/views/bpm/task/done/index.vue
  67. 1 1
      src/views/bpm/task/manager/index.vue
  68. 4 4
      src/views/bpm/task/todo/index.vue
  69. 238 0
      src/views/community/comment/index.vue
  70. 536 0
      src/views/community/post/index.vue
  71. 142 0
      src/views/community/sensitive/SensitiveWordForm.vue
  72. 241 0
      src/views/community/sensitive/index.vue
  73. 133 0
      src/views/community/topic/TopicForm.vue
  74. 224 0
      src/views/community/topic/index.vue
  75. 1 1
      src/views/crm/backlog/index.vue
  76. 2 2
      src/views/crm/business/index.vue
  77. 2 2
      src/views/crm/business/status/index.vue
  78. 2 2
      src/views/crm/clue/index.vue
  79. 2 2
      src/views/crm/contact/index.vue
  80. 2 2
      src/views/crm/contract/config/index.vue
  81. 2 2
      src/views/crm/contract/index.vue
  82. 2 2
      src/views/crm/customer/index.vue
  83. 2 2
      src/views/crm/customer/limitConfig/index.vue
  84. 2 2
      src/views/crm/customer/pool/index.vue
  85. 2 2
      src/views/crm/customer/poolConfig/index.vue
  86. 1 1
      src/views/crm/product/category/index.vue
  87. 1 1
      src/views/crm/product/index.vue
  88. 2 2
      src/views/crm/receivable/index.vue
  89. 2 2
      src/views/crm/receivable/plan/index.vue
  90. 1 1
      src/views/erp/finance/account/index.vue
  91. 1 1
      src/views/erp/finance/payment/index.vue
  92. 1 1
      src/views/erp/finance/receipt/index.vue
  93. 1 1
      src/views/erp/home/index.vue
  94. 1 1
      src/views/erp/product/category/index.vue
  95. 1 1
      src/views/erp/product/product/index.vue
  96. 1 1
      src/views/erp/product/unit/index.vue
  97. 1 1
      src/views/erp/purchase/in/index.vue
  98. 1 1
      src/views/erp/purchase/order/index.vue
  99. 1 1
      src/views/erp/purchase/return/index.vue
  100. 0 0
      src/views/erp/purchase/supplier/index.vue

File diff suppressed because it is too large
+ 0 - 37
.env


+ 0 - 37
.env.dev

@@ -1,37 +0,0 @@
-# 开发环境:本地只启动前端项目,依赖开发环境(后端、APP)
-NODE_ENV=production
-
-VITE_DEV=true
-
-# 请求路径
-VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
-
-# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
-VITE_UPLOAD_TYPE=server
-
-# 接口地址
-VITE_API_URL=/admin-api
-
-# 是否删除debugger
-VITE_DROP_DEBUGGER=false
-
-# 是否删除console.log
-VITE_DROP_CONSOLE=false
-
-# 是否sourcemap
-VITE_SOURCEMAP=true
-
-# 打包路径
-VITE_BASE_PATH=/
-
-# 输出路径
-VITE_OUT_DIR=dist
-
-# 商城H5会员端域名
-VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
-
-# 验证码的开关
-VITE_APP_CAPTCHA_ENABLE=true
-
-# GoView域名
-VITE_GOVIEW_URL='http://127.0.0.1:3000'

+ 0 - 34
.env.local

@@ -1,34 +0,0 @@
-# 本地开发环境:本地启动所有项目(前端、后端、APP)时使用,不依赖外部环境
-NODE_ENV=development
-
-VITE_DEV=true
-
-# 请求路径
-VITE_BASE_URL='http://localhost:48080'
-
-# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
-VITE_UPLOAD_TYPE=server
-
-# 接口地址
-VITE_API_URL=/admin-api
-
-# 是否删除debugger
-VITE_DROP_DEBUGGER=false
-
-# 是否删除console.log
-VITE_DROP_CONSOLE=false
-
-# 是否sourcemap
-VITE_SOURCEMAP=false
-
-# 打包路径
-VITE_BASE_PATH=/
-
-# 商城H5会员端域名
-VITE_MALL_H5_DOMAIN='http://localhost:3000'
-
-# 验证码的开关
-VITE_APP_CAPTCHA_ENABLE=false
-
-# GoView域名
-VITE_GOVIEW_URL='http://127.0.0.1:3000'

+ 0 - 34
.env.prod

@@ -1,34 +0,0 @@
-# 生产环境:只在打包时使用
-NODE_ENV=production
-
-VITE_DEV=false
-
-# 请求路径
-VITE_BASE_URL='http://localhost:48080'
-
-# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
-VITE_UPLOAD_TYPE=server
-
-# 接口地址
-VITE_API_URL=/admin-api
-
-# 是否删除debugger
-VITE_DROP_DEBUGGER=true
-
-# 是否删除console.log
-VITE_DROP_CONSOLE=true
-
-# 是否sourcemap
-VITE_SOURCEMAP=false
-
-# 打包路径
-VITE_BASE_PATH=/
-
-# 输出路径
-VITE_OUT_DIR=dist-prod
-
-# 商城H5会员端域名
-VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
-
-# GoView域名
-VITE_GOVIEW_URL='http://127.0.0.1:3000'

+ 0 - 34
.env.stage

@@ -1,34 +0,0 @@
-# 预发布环境:只在打包时使用
-NODE_ENV=production
-
-VITE_DEV=false
-
-# 请求路径
-VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
-
-# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
-VITE_UPLOAD_TYPE=server
-
-# 接口地址
-VITE_API_URL=/admin-api
-
-# 是否删除debugger
-VITE_DROP_DEBUGGER=true
-
-# 是否删除console.log
-VITE_DROP_CONSOLE=true
-
-# 是否sourcemap
-VITE_SOURCEMAP=false
-
-# 打包路径
-VITE_BASE_PATH='http://static-vue3.yudao.iocoder.cn/'
-
-# 输出路径
-VITE_OUT_DIR=dist-stage
-
-# 商城H5会员端域名
-VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
-
-# GoView域名
-VITE_GOVIEW_URL='http://127.0.0.1:3000'

+ 0 - 34
.env.test

@@ -1,34 +0,0 @@
-# 测试环境:只在打包时使用
-NODE_ENV=production
-
-VITE_DEV=false
-
-# 请求路径
-VITE_BASE_URL='http://localhost:48080'
-
-# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
-VITE_UPLOAD_TYPE=server
-
-# 接口地址
-VITE_API_URL=/admin-api
-
-# 是否删除debugger
-VITE_DROP_DEBUGGER=true
-
-# 是否删除console.log
-VITE_DROP_CONSOLE=true
-
-# 是否sourcemap
-VITE_SOURCEMAP=false
-
-# 打包路径
-VITE_BASE_PATH=/admin-ui-vue3/
-
-# 输出路径
-VITE_OUT_DIR=dist-test
-
-# 商城H5会员端域名
-VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
-
-# GoView域名
-VITE_GOVIEW_URL='http://127.0.0.1:3000'

+ 84 - 7
.gitignore

@@ -1,9 +1,86 @@
-node_modules
+# 依赖目录
+node_modules/
+.pnp
+.pnp.js
+
+# 构建输出
+dist/
+dist-ssr/
+dist-prod/
+build/
+*.local
+
+# 日志文件
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+# 编辑器目录和文件
+.idea/
+.vscode/
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+.history/
+
+# 操作系统文件
 .DS_Store
-dist
-dist-ssr
-/dist*
-pnpm-debug
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
+Desktop.ini
+
+# 环境变量文件(包含敏感信息,不应提交)
+.env
+.env.local
+.env.dev
+.env.test
+.env.stage
+.env.prod
+.env.*.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+# 测试覆盖率
+coverage/
+*.lcov
+.nyc_output/
+
+# 临时文件
+*.tmp
+*.temp
+*.swp
+*.swo
+*~
+.cache/
+
+# 自动生成的文件
 auto-*.d.ts
-.idea
-.history
+*.d.ts.map
+
+# 打包文件
+*.zip
+*.tar.gz
+*.rar
+
+# 调试文件
+.vscode/launch.json
+.vscode/settings.json
+
+# 锁文件(项目使用 pnpm,忽略 npm 的锁文件)
+package-lock.json
+yarn.lock
+
+# 其他
+pnpm-debug
+.pnpm-store/
+.turbo/

+ 60 - 271
README.md

@@ -1,295 +1,84 @@
-**严肃声明:现在、未来都不会有商业版本,所有代码全部开源!!**
+# 新大洲车联网CRM系统 - 管理后台
 
-**「我喜欢写代码,乐此不疲」**  
-**「我喜欢做开源,以此为乐」**
+> 基于 Vue3 + Vite4 + Element Plus + TypeScript 开发的企业级车联网CRM管理后台系统
 
-我 🐶 在上海艰苦奋斗,早中晚在 top3 大厂认真搬砖,夜里为开源做贡献。
+## 📋 项目简介
 
-如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持
+新大洲车联网CRM系统管理后台,采用前后端分离架构,提供完整的车联网业务管理功能
 
-## 🐶 新手必读
+## 🚀 技术栈
 
-* nodejs > 16.18.0 && pnpm > 8.6.0 (强制使用pnpm)
-* 演示地址【Vue3 + element-plus】:<http://dashboard-vue3.yudao.iocoder.cn>
-* 演示地址【Vue3 + vben5.0(ant-design-vue)】:<http://dashboard-vben.yudao.iocoder.cn>
-* 演示地址【Vue2 + element-ui】:<http://dashboard.yudao.iocoder.cn>
-* 启动文档:<https://doc.iocoder.cn/quick-start/>
-* 视频教程:<https://doc.iocoder.cn/video/>
+| 框架 | 说明 | 版本 |
+|------|------|------|
+| [Vue](https://staging-cn.vuejs.org/) | Vue 框架 | 3.5.12 |
+| [Vite](https://cn.vitejs.dev/) | 开发与构建工具 | 5.1.4 |
+| [Element Plus](https://element-plus.org/zh-CN/) | UI 组件库 | 2.11.1 |
+| [TypeScript](https://www.typescriptlang.org/docs/) | JavaScript 的超集 | 5.3.3 |
+| [Pinia](https://pinia.vuejs.org/) | 状态管理 | 2.1.7 |
+| [Vue Router](https://router.vuejs.org/) | 路由管理 | 4.4.5 |
+| [Axios](https://axios-http.com/) | HTTP 客户端 | 1.9.0 |
 
-## 🐯 平台简介
+## 📦 环境要求
 
-**芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。
+- Node.js >= 16.0.0
+- pnpm >= 8.6.0
 
-* 采用 [vue-element-plus-admin](https://gitee.com/kailong110120130/vue-element-plus-admin) 实现
-* 改换 saas,自动引入等功能
-* 使用 Element Plus 免费开源的中后台模版,具备如下特性:
+## 🛠️ 快速开始
 
-![首页](.image/demo/vue3-ep.png)
+### 安装依赖
 
-* **最新技术栈**:使用 Vue3、Vite4 等前端前沿技术开发
-* **TypeScript**: 应用程序级 JavaScript 的语言
-* **主题**: 可配置的主题
-* **国际化**:内置完善的国际化方案
-* **权限**:内置完善的动态路由权限生成方案
-* **组件**:二次封装了多个常用的组件
-* **示例**:内置丰富的示例
+```bash
+pnpm install
+```
 
-## 技术栈
+### 启动开发服务器
 
-| 框架                                                                   | 说明               | 版本     |
-|----------------------------------------------------------------------|------------------|--------|
-| [Vue](https://staging-cn.vuejs.org/)                                 | Vue 框架           | 3.3.8  |
-| [Vite](https://cn.vitejs.dev//)                                      | 开发与构建工具          | 4.5.0  |
-| [Element Plus](https://element-plus.org/zh-CN/)                      | Element Plus     | 2.4.2  |
-| [TypeScript](https://www.typescriptlang.org/docs/)                   | JavaScript 的超集   | 5.2.2  |
-| [pinia](https://pinia.vuejs.org/)                                    | Vue 存储库 替代 vuex5 | 2.1.7  |
-| [vueuse](https://vueuse.org/)                                        | 常用工具集            | 10.6.1 |
-| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化              | 9.6.5  |
-| [vue-router](https://router.vuejs.org/)                              | Vue 路由           | 4.2.5  |
-| [unocss](https://uno.antfu.me/)                                      | 原子 css           | 0.57.4 |
-| [iconify](https://icon-sets.iconify.design/)                         | 在线图标库            | 3.1.1  |
-| [wangeditor](https://www.wangeditor.com/)                            | 富文本编辑器           | 5.1.23 |
+```bash
+pnpm dev
+```
 
-## 开发工具
+### 构建生产版本
 
-推荐 VS Code 开发,配合插件如下:
+```bash
+# 本地构建
+pnpm build:local
 
-| 插件名                           | 功能                  |
-|-------------------------------|---------------------|
-| Vue - Official                | Vue 与 TypeScript 支持 |
-| unocss                        | unocss for vscode   |
-| Iconify IntelliSense          | Iconify 预览和搜索       |
-| i18n Ally                     | 国际化智能提示             |
-| Stylelint                     | Css    格式化          |
-| Prettier                      | 代码格式化               |
-| ESLint                        | 脚本代码检查              |
-| DotENV                        | env 文件高亮            |
+# 生产构建
+pnpm build:prod
+```
 
-## 🔥 后端架
+## 📁 项目结构
 
-支持 Spring Boot、Spring Cloud 两种架构:
+```
+src/
+├── api/          # API 接口
+├── assets/       # 静态资源
+├── components/   # 公共组件
+├── router/       # 路由配置
+├── store/        # 状态管理
+├── utils/        # 工具类
+└── views/        # 页面视图
+```
 
-① Spring Boot 单体架构:<https://doc.iocoder.cn>
+## 🔧 开发工具
 
-![架构图](/.image/common/ruoyi-vue-pro-architecture.png)
+推荐使用 VS Code,配合以下插件:
 
-② Spring Cloud 微服务架构:<https://cloud.iocoder.cn>
+- Vue - Official
+- TypeScript Vue Plugin (Volar)
+- ESLint
+- Prettier
+- Stylelint
 
-![架构图](/.image/common/yudao-cloud-architecture.png)
+## 📝 功能模块
 
-## 内置功能
+- ✅ 用户管理
+- ✅ 权限管理
+- ✅ 车辆管理
+- ✅ 消息推送
+- ✅ 业务管理
+- ✅ 平台管理
 
-系统内置多种多种业务功能,可以用于快速你的业务系统:
+## 📄 License
 
-系统内置多种多种业务功能,可以用于快速你的业务系统:
-
-![功能分层](/.image/common/ruoyi-vue-pro-biz.png)
-
-* 通用模块(必选):系统功能、基础设施
-* 通用模块(可选):工作流程、支付系统、数据报表、会员中心
-* 业务系统(按需):ERP 系统、CRM 系统、商城系统、微信公众号、AI 大模型
-
-### 系统功能
-
-|     | 功能    | 描述                              |
-|-----|-------|---------------------------------|
-|     | 用户管理  | 用户是系统操作者,该功能主要完成系统用户配置          |
-| ⭐️  | 在线用户  | 当前系统中活跃用户状态监控,支持手动踢下线           |
-|     | 角色管理  | 角色菜单权限分配、设置角色按机构进行数据范围权限划分      |
-|     | 菜单管理  | 配置系统菜单、操作权限、按钮权限标识等,本地缓存提供性能    |
-|     | 部门管理  | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限  |
-|     | 岗位管理  | 配置系统用户所属担任职务                    |
-| 🚀  | 租户管理  | 配置系统租户,支持 SaaS 场景下的多租户功能        |
-| 🚀  | 租户套餐  | 配置租户套餐,自定每个租户的菜单、操作、按钮的权限       |
-|     | 字典管理  | 对系统中经常使用的一些较为固定的数据进行维护          |
-| 🚀  | 短信管理  | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 |
-| 🚀  | 邮件管理  | 邮箱账号、邮件模版、邮件发送日志,支持所有邮件平台       |
-| 🚀  | 站内信   | 系统内的消息通知,提供站内信模版、站内信消息          |
-| 🚀  | 操作日志  | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 |
-| ⭐️  | 登录日志  | 系统登录日志记录查询,包含登录异常               |
-| 🚀  | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务     |
-|     | 通知公告  | 系统通知公告信息发布维护                    |
-| 🚀  | 敏感词   | 配置系统敏感词,支持标签分组                  |
-| 🚀  | 应用管理  | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 |
-| 🚀  | 地区管理  | 展示省份、城市、区镇等城市信息,支持 IP 对应城市      |
-
-![功能图](/.image/common/system-feature.png)
-
-### 工作流程
-
-![功能图](/.image/common/bpm-feature.png)
-
-基于 Flowable 构建,可支持信创(国产)数据库,满足中国特色流程操作:
-
-| BPMN 设计器                    | 钉钉/飞书设计器                      |
-|-----------------------------|-------------------------------|
-| ![](.image/工作流设计器-bpmn.jpg) | ![](.image/工作流设计器-simple.jpg) |
-
-> 历经头部企业生产验证,工作流引擎须标配仿钉钉/飞书 + BPMN 双设计器!!!
->
-> 前者支持轻量配置简单流程,后者实现复杂场景深度编排
-
-| 功能列表       | 功能描述                                                                                | 是否完成 |
-|------------|-------------------------------------------------------------------------------------|------|
-| SIMPLE 设计器 | 仿钉钉/飞书设计器,支持拖拽搭建表单流程,10 分钟快速完成审批流程配置                                                | ✅    |
-| BPMN 设计器   | 基于 BPMN 标准开发,适配复杂业务场景,满足多层级审批及流程自动化需求                                               | ✅    |
-| 会签         | 同一个审批节点设置多个人(如 A、B、C 三人,三人会同时收到待办任务),需全部同意之后,审批才可到下一审批节点                            | ✅    |
-| 或签         | 同一个审批节点设置多个人,任意一个人处理后,就能进入下一个节点                                                     | ✅    |
-| 依次审批       | (顺序会签)同一个审批节点设置多个人(如 A、B、C 三人),三人按顺序依次收到待办,即 A 先审批,A 提交后 B 才能审批,需全部同意之后,审批才可到下一审批节点 | ✅    |
-| 抄送         | 将审批结果通知给抄送人,同一个审批默认排重,不重复抄送给同一人                                                     | ✅    |
-| 驳回         | (退回)将审批重置发送给某节点,重新审批。可驳回至发起人、上一节点、任意节点                                              | ✅    |
-| 转办         | A 转给其 B 审批,B 审批后,进入下一节点                                                             | ✅    |
-| 委派         | A 转给其 B 审批,B 审批后,转给 A,A 继续审批后进入下一节点                                                 | ✅    |
-| 加签         | 允许当前审批人根据需要,自行增加当前节点的审批人,支持向前、向后加签                                                  | ✅    |
-| 减签         | (取消加签)在当前审批人操作之前,减少审批人                                                              | ✅    |
-| 撤销         | (取消流程)流程发起人,可以对流程进行撤销处理                                                             | ✅    |
-| 终止         | 系统管理员,在任意节点终止流程实例                                                                   | ✅    |
-| 表单权限       | 支持拖拉拽配置表单,每个审批节点可配置只读、编辑、隐藏权限                                                       | ✅    |
-| 超时审批       | 配置超时审批时间,超时后自动触发审批通过、不通过、驳回等操作                                                      | ✅    |
-| 自动提醒       | 配置提醒时间,到达时间后自动触发短信、邮箱、站内信等通知提醒,支持自定义重复提醒频次                                          | ✅    |
-| 父子流程       | 主流程设置子流程节点,子流程节点会自动触发子流程。子流程结束后,主流程才会执行(继续往下下执行),支持同步子流程、异步子流程                      | ✅    |
-| 条件分支       | (排它分支)用于在流程中实现决策,即根据条件选择一个分支执行                                                      | ✅    |
-| 并行分支       | 允许将流程分成多条分支,不进行条件判断,所有分支都会执行                                                        | ✅    |
-| 包容分支       | (条件分支 + 并行分支的结合体)允许基于条件选择多条分支执行,但如果没有任何一个分支满足条件,则可以选择默认分支                           | ✅    |
-| 路由分支       | 根据条件选择一个分支执行(重定向到指定配置节点),也可以选择默认分支执行(继续往下执行)                                        | ✅    |
-| 触发节点       | 执行到该节点,触发 HTTP 请求、HTTP 回调、更新数据、删除数据等                                                | ✅    |
-| 延迟节点       | 执行到该节点,审批等待一段时间再执行,支持固定时长、固定日期等                                                     | ✅    |
-| 拓展设置       | 流程前置/后置通知,节点(任务)前置、后置通知,流程报表,自动审批去重,自定流程编号、标题、摘要,流程报表等                              | ✅    |
-
-### 支付系统
-
-|     | 功能   | 描述                        |
-|-----|------|---------------------------|
-| 🚀  | 应用信息 | 配置商户的应用信息,对接支付宝、微信等多个支付渠道 |
-| 🚀  | 支付订单 | 查看用户发起的支付宝、微信等的【支付】订单     |
-| 🚀  | 退款订单 | 查看用户发起的支付宝、微信等的【退款】订单     |
-| 🚀  | 回调通知 | 查看支付回调业务的【支付】【退款】的通知结果    |
-| 🚀  | 接入示例 | 提供接入支付系统的【支付】【退款】的功能实战    |
-
-### 基础设施
-
-|     | 功能        | 描述                                           |
-|-----|-----------|----------------------------------------------|
-| 🚀  | 代码生成      | 前后端代码的生成(Java、Vue、SQL、单元测试),支持 CRUD 下载       |
-| 🚀  | 系统接口      | 基于 Swagger 自动生成相关的 RESTful API 接口文档          |
-| 🚀  | 数据库文档     | 基于 Screw 自动生成数据库文档,支持导出 Word、HTML、MD 格式      |
-|     | 表单构建      | 拖动表单元素生成相应的 HTML 代码,支持导出 JSON、Vue 文件         |
-| 🚀  | 配置管理      | 对系统动态配置常用参数,支持 SpringBoot 加载                 |
-| ⭐️  | 定时任务      | 在线(添加、修改、删除)任务调度包含执行结果日志                     |
-| 🚀  | 文件服务      | 支持将文件存储到 S3(MinIO、阿里云、腾讯云、七牛云)、本地、FTP、数据库等   | 
-| 🚀  | WebSocket | 提供 WebSocket 接入示例,支持一对一、一对多发送方式              | 
-| 🚀  | API 日志    | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题   |
-|     | MySQL 监控  | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈              |
-|     | Redis 监控  | 监控 Redis 数据库的使用情况,使用的 Redis Key 管理           |
-| 🚀  | 消息队列      | 基于 Redis 实现消息队列,Stream 提供集群消费,Pub/Sub 提供广播消费 |
-| 🚀  | Java 监控   | 基于 Spring Boot Admin 实现 Java 应用的监控           |
-| 🚀  | 链路追踪      | 接入 SkyWalking 组件,实现链路追踪                      |
-| 🚀  | 日志中心      | 接入 SkyWalking 组件,实现日志中心                      |
-| 🚀  | 服务保障      | 基于 Redis 实现分布式锁、幂等、限流功能,满足高并发场景              |
-| 🚀  | 日志服务      | 轻量级日志中心,查看远程服务器的日志                           |
-| 🚀  | 单元测试      | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等    |
-
-![功能图](/.image/common/infra-feature.png)
-
-### 数据报表
-
-|     | 功能    | 描述                 |
-|-----|-------|--------------------|
-| 🚀  | 报表设计器 | 支持数据报表、图形报表、打印设计等  |
-| 🚀  | 大屏设计器 | 拖拽生成数据大屏,内置几十种图表组件 |
-
-### 微信公众号
-
-|    | 功能     | 描述                            |
-|----|--------|-------------------------------|
-| 🚀 | 账号管理   | 配置接入的微信公众号,可支持多个公众号           |
-| 🚀 | 数据统计   | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据  |
-| 🚀 | 粉丝管理   | 查看已关注、取关的粉丝列表,可对粉丝进行同步、打标签等操作 |
-| 🚀 | 消息管理   | 查看粉丝发送的消息列表,可主动回复粉丝消息         |
-| 🚀 | 模版消息   | 配置和发送模版消息,用于向粉丝推送通知类消息        |
-| 🚀 | 自动回复   | 自动回复粉丝发送的消息,支持关注回复、消息回复、关键字回复 |
-| 🚀 | 标签管理   | 对公众号的标签进行创建、查询、修改、删除等操作       |
-| 🚀 | 菜单管理   | 自定义公众号的菜单,也可以从公众号同步菜单         |
-| 🚀 | 素材管理   | 管理公众号的图片、语音、视频等素材,支持在线播放语音、视频 |
-| 🚀 | 图文草稿箱  | 新增常用的图文素材到草稿箱,可发布到公众号         |
-| 🚀 | 图文发表记录 | 查看已发布成功的图文素材,支持删除操作           |
-
-### 商城系统
-
-演示地址:<https://doc.iocoder.cn/mall-preview/>
-
-![功能图](/.image/common/mall-feature.png)
-
-![功能图](/.image/common/mall-preview.png)
-
-### ERP 系统
-
-演示地址:<https://doc.iocoder.cn/erp-preview/>
-
-![功能图](/.image/common/erp-feature.png)
-
-### CRM 系统
-
-演示地址:<https://doc.iocoder.cn/crm-preview/>
-
-![功能图](/.image/common/crm-feature.png)
-
-### AI 大模型
-
-演示地址:<https://doc.iocoder.cn/ai-preview/>
-
-![功能图](/.image/common/ai-feature.png)
-
-![功能图](/.image/common/ai-preview.gif)
-
-## 🐷 演示图
-
-### 系统功能
-
-| 模块       | biu                         | biu                       | biu                      |
-|----------|-----------------------------|---------------------------|--------------------------|
-| 登录 & 首页  | ![登录](/.image/登录.jpg)       | ![首页](/.image/首页.jpg)     | ![个人中心](/.image/个人中心.jpg) |
-| 用户 & 应用  | ![用户管理](/.image/用户管理.jpg)   | ![令牌管理](/.image/令牌管理.jpg) | ![应用管理](/.image/应用管理.jpg) |
-| 租户 & 套餐  | ![租户管理](/.image/租户管理.jpg)   | ![租户套餐](/.image/租户套餐.png) | -                        |
-| 部门 & 岗位  | ![部门管理](/.image/部门管理.jpg)   | ![岗位管理](/.image/岗位管理.jpg) | -                        |
-| 菜单 & 角色  | ![菜单管理](/.image/菜单管理.jpg)   | ![角色管理](/.image/角色管理.jpg) | -                        |
-| 审计日志     | ![操作日志](/.image/操作日志.jpg)   | ![登录日志](/.image/登录日志.jpg) | -                        |
-| 短信       | ![短信渠道](/.image/短信渠道.jpg)   | ![短信模板](/.image/短信模板.jpg) | ![短信日志](/.image/短信日志.jpg) |
-| 字典 & 敏感词 | ![字典类型](/.image/字典类型.jpg)   | ![字典数据](/.image/字典数据.jpg) | ![敏感词](/.image/敏感词.jpg)  |
-| 错误码 & 通知 | ![错误码管理](/.image/错误码管理.jpg) | ![通知公告](/.image/通知公告.jpg) | -                        |
-
-### 工作流程
-
-| 模块      | biu                             | biu                             | biu                             |
-|---------|---------------------------------|---------------------------------|---------------------------------|
-| 流程模型    | ![流程模型-列表](/.image/流程模型-列表.jpg) | ![流程模型-设计](/.image/流程模型-设计.jpg) | ![流程模型-定义](/.image/流程模型-定义.jpg) |
-| 表单 & 分组 | ![流程表单](/.image/流程表单.jpg)       | ![用户分组](/.image/用户分组.jpg)       | -                               |
-| 我的流程    | ![我的流程-列表](/.image/我的流程-列表.jpg) | ![我的流程-发起](/.image/我的流程-发起.jpg) | ![我的流程-详情](/.image/我的流程-详情.jpg) |
-| 待办 & 已办 | ![任务列表-审批](/.image/任务列表-审批.jpg) | ![任务列表-待办](/.image/任务列表-待办.jpg) | ![任务列表-已办](/.image/任务列表-已办.jpg) |
-| OA 请假   | ![OA请假-列表](/.image/OA请假-列表.jpg) | ![OA请假-发起](/.image/OA请假-发起.jpg) | ![OA请假-详情](/.image/OA请假-详情.jpg) |
-
-### 基础设施
-
-| 模块            | biu                           | biu                         | biu                       |
-|---------------|-------------------------------|-----------------------------|---------------------------|
-| 代码生成          | ![代码生成](/.image/代码生成.jpg)     | ![生成效果](/.image/生成效果.jpg)   | -                         |
-| 文档            | ![系统接口](/.image/系统接口.jpg)     | ![数据库文档](/.image/数据库文档.jpg) | -                         |
-| 文件 & 配置       | ![文件配置](/.image/文件配置.jpg)     | ![文件管理](/.image/文件管理2.jpg)  | ![配置管理](/.image/配置管理.jpg) |
-| 定时任务          | ![定时任务](/.image/定时任务.jpg)     | ![任务日志](/.image/任务日志.jpg)   | -                         |
-| API 日志        | ![访问日志](/.image/访问日志.jpg)     | ![错误日志](/.image/错误日志.jpg)   | -                         |
-| MySQL & Redis | ![MySQL](/.image/MySQL.jpg)   | ![Redis](/.image/Redis.jpg) | -                         |
-| 监控平台          | ![Java监控](/.image/Java监控.jpg) | ![链路追踪](/.image/链路追踪.jpg)   | ![日志中心](/.image/日志中心.jpg) |
-
-### 支付系统
-
-| 模块      | biu                       | biu                             | biu                             |
-|---------|---------------------------|---------------------------------|---------------------------------|
-| 商家 & 应用 | ![商户信息](/.image/商户信息.jpg) | ![应用信息-列表](/.image/应用信息-列表.jpg) | ![应用信息-编辑](/.image/应用信息-编辑.jpg) |
-| 支付 & 退款 | ![支付订单](/.image/支付订单.jpg) | ![退款订单](/.image/退款订单.jpg)       | ---                             |
-
-### 数据报表
-
-| 模块    | biu                             | biu                             | biu                                   |
-|-------|---------------------------------|---------------------------------|---------------------------------------|
-| 报表设计器 | ![数据报表](/.image/报表设计器-数据报表.jpg) | ![图形报表](/.image/报表设计器-图形报表.jpg) | ![报表设计器-打印设计](/.image/报表设计器-打印设计.jpg) |
-| 大屏设计器 | ![大屏列表](/.image/大屏设计器-列表.jpg)   | ![大屏预览](/.image/大屏设计器-预览.jpg)   | ![大屏编辑](/.image/大屏设计器-编辑.jpg)         |
+PROPRIETARY - 新大洲专用

+ 2 - 2
index.html

@@ -7,11 +7,11 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <meta
       name="keywords"
-      content="芋道管理系统 基于 vue3 + CompositionAPI + typescript + vite3 + element plus 的后台开源免费管理系统!"
+      content="新大洲车联网CRM系统 基于 vue3 + CompositionAPI + typescript + vite4 + element plus 的车联网管理后台系统"
     />
     <meta
       name="description"
-      content="芋道管理系统 基于 vue3 + CompositionAPI + typescript + vite3 + element plus 的后台开源免费管理系统!"
+      content="新大洲车联网CRM系统管理后台 - 基于 vue3 + CompositionAPI + typescript + vite4 + element plus 开发"
     />
     <title>%VITE_APP_TITLE%</title>
   </head>

File diff suppressed because it is too large
+ 0 - 17486
package-lock.json


+ 11 - 10
package.json

@@ -1,9 +1,9 @@
 {
-  "name": "yudao-ui-admin-vue3",
-  "version": "2025.12-snapshot",
-  "description": "基于vue3、vite4、element-plus、typesScript",
-  "author": "xingyu",
-  "private": false,
+  "name": "xdz-admin",
+  "version": "1.0.0-SNAPSHOT",
+  "description": "新大洲车联网CRM系统管理后台 - 基于vue3、vite4、element-plus、typescript",
+  "author": "xindazhou",
+  "private": true,
   "scripts": {
     "i": "pnpm install",
     "dev": "vite --mode env.local",
@@ -142,18 +142,19 @@
     "vue-eslint-parser": "^9.3.2",
     "vue-tsc": "^1.8.27"
   },
-  "license": "MIT",
+  "license": "PROPRIETARY",
   "repository": {
     "type": "git",
-    "url": "git+https://gitee.com/yudaocode/yudao-ui-admin-vue3"
+    "url": ""
   },
   "bugs": {
-    "url": "https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues"
+    "url": ""
   },
-  "homepage": "https://gitee.com/yudaocode/yudao-ui-admin-vue3",
+  "homepage": "",
   "web-types": "./web-types.json",
   "engines": {
     "node": ">= 16.0.0",
     "pnpm": ">=8.6.0"
-  }
+  },
+  "packageManager": "pnpm@10.22.0+sha512.bf049efe995b28f527fd2b41ae0474ce29186f7edcb3bf545087bd61fbbebb2bf75362d1307fda09c2d288e1e499787ac12d4fcb617a974718a6051f2eee741c"
 }

+ 38 - 0
src/api/community/comment/index.ts

@@ -0,0 +1,38 @@
+import request from '@/config/axios'
+
+export interface CommentVO {
+  id?: number // 评论ID
+  postId?: number // 帖子ID
+  postTitle?: string // 帖子标题
+  userId?: number // 用户ID
+  userNickname?: string // 用户昵称
+  parentId?: number // 父评论ID,0表示顶级评论
+  content?: string // 评论内容
+  likeCount?: number // 点赞数
+  status?: number // 状态:1-正常,2-删除
+  createTime?: Date // 创建时间
+  updateTime?: Date // 更新时间
+}
+
+export interface CommentPageReqVO extends PageParam {
+  postId?: number // 帖子ID
+  userId?: number // 用户ID
+  status?: number // 状态:1-正常,2-删除
+  createTime?: Date[] // 创建时间
+}
+
+// 查询评论分页列表
+export const getCommentPage = (params: CommentPageReqVO) => {
+  return request.get({ url: '/community/comment/page', params })
+}
+
+// 删除评论
+export const deleteComment = (id: number) => {
+  return request.delete({ url: '/community/comment/delete?id=' + id })
+}
+
+// 批量删除评论
+export const deleteCommentList = (ids: number[]) => {
+  return request.delete({ url: '/community/comment/delete-list', params: { ids: ids.join(',') } })
+}
+

+ 72 - 0
src/api/community/post/index.ts

@@ -0,0 +1,72 @@
+import request from '@/config/axios'
+
+export interface PostMediaVO {
+  mediaType?: number // 媒体类型:1-图片,2-视频
+  mediaUrl?: string // 媒体URL
+  coverUrl?: string // 视频封面URL(仅视频类型)
+  duration?: number // 视频时长(秒,仅视频类型)
+  fileSize?: number // 文件大小(字节)
+  sort?: number // 排序
+}
+
+export interface PostVO {
+  id?: number // 帖子ID
+  userId?: number // 用户ID
+  userNickname?: string // 用户昵称
+  title?: string // 标题
+  content?: string // 内容
+  contentType?: number // 内容类型:1-纯文本,2-图片,3-视频,4-图文混合
+  mediaList?: PostMediaVO[] // 媒体列表(图片/视频)
+  viewCount?: number // 浏览数
+  likeCount?: number // 点赞数
+  commentCount?: number // 评论数
+  collectCount?: number // 收藏数
+  status?: number // 状态:1-正常,2-删除,3-审核中
+  isTop?: number // 是否置顶:0-否,1-是
+  isEssence?: number // 是否精华:0-否,1-是
+  tags?: string // 标签,逗号分隔
+  createTime?: Date // 创建时间
+  updateTime?: Date // 更新时间
+}
+
+export interface PostPageReqVO extends PageParam {
+  userId?: number // 用户ID
+  title?: string // 标题,模糊匹配
+  status?: number // 状态:1-正常,2-删除,3-审核中
+  isTop?: number // 是否置顶:0-否,1-是
+  isEssence?: number // 是否精华:0-否,1-是
+  createTime?: Date[] // 创建时间
+}
+
+export interface PostUpdateStatusReqVO {
+  id: number // 帖子ID
+  status?: number // 状态:1-正常,2-删除,3-审核中
+  isTop?: number // 是否置顶:0-否,1-是
+  isEssence?: number // 是否精华:0-否,1-是
+}
+
+// 查询帖子分页列表
+export const getPostPage = (params: PostPageReqVO) => {
+  return request.get({ url: '/community/post/page', params })
+}
+
+// 查询帖子详情
+export const getPost = (id: number) => {
+  return request.get({ url: '/community/post/get?id=' + id })
+}
+
+// 更新帖子状态
+export const updatePostStatus = (data: PostUpdateStatusReqVO) => {
+  return request.put({ url: '/community/post/update-status', data })
+}
+
+// 删除帖子
+export const deletePost = (id: number) => {
+  return request.delete({ url: '/community/post/delete?id=' + id })
+}
+
+// 批量删除帖子
+export const deletePostList = (ids: number[]) => {
+  return request.delete({ url: '/community/post/delete-list', params: { ids: ids.join(',') } })
+}
+

+ 51 - 0
src/api/community/sensitive/index.ts

@@ -0,0 +1,51 @@
+import request from '@/config/axios'
+
+export interface SensitiveWordVO {
+  id?: number // 敏感词ID
+  word?: string // 敏感词
+  type?: number // 类型:1-禁用词,2-替换词
+  replaceWord?: string // 替换词(仅替换词类型)
+  status?: number // 状态:1-启用,2-禁用
+  createTime?: Date // 创建时间
+  updateTime?: Date // 更新时间
+}
+
+export interface SensitiveWordPageReqVO extends PageParam {
+  word?: string // 敏感词,模糊匹配
+  type?: number // 类型:1-禁用词,2-替换词
+  status?: number // 状态:1-启用,2-禁用
+}
+
+export interface SensitiveWordSaveReqVO {
+  id?: number // 敏感词ID
+  word: string // 敏感词
+  type: number // 类型:1-禁用词,2-替换词
+  replaceWord?: string // 替换词(仅替换词类型)
+  status: number // 状态:1-启用,2-禁用
+}
+
+// 查询敏感词分页列表
+export const getSensitiveWordPage = (params: SensitiveWordPageReqVO) => {
+  return request.get({ url: '/community/sensitive-word/page', params })
+}
+
+// 查询敏感词详情
+export const getSensitiveWord = (id: number) => {
+  return request.get({ url: '/community/sensitive-word/get?id=' + id })
+}
+
+// 新增敏感词
+export const createSensitiveWord = (data: SensitiveWordSaveReqVO) => {
+  return request.post({ url: '/community/sensitive-word/create', data })
+}
+
+// 修改敏感词
+export const updateSensitiveWord = (data: SensitiveWordSaveReqVO) => {
+  return request.put({ url: '/community/sensitive-word/update', data })
+}
+
+// 删除敏感词
+export const deleteSensitiveWord = (id: number) => {
+  return request.delete({ url: '/community/sensitive-word/delete?id=' + id })
+}
+

+ 55 - 0
src/api/community/topic/index.ts

@@ -0,0 +1,55 @@
+import request from '@/config/axios'
+
+export interface TopicVO {
+  id?: number // 话题ID
+  name?: string // 话题名称
+  description?: string // 话题描述
+  postCount?: number // 帖子数
+  sort?: number // 排序
+  status?: number // 状态:1-正常,2-禁用
+  createTime?: Date // 创建时间
+}
+
+export interface TopicPageReqVO extends PageParam {
+  name?: string // 话题名称,模糊匹配
+  status?: number // 状态:1-正常,2-禁用
+}
+
+export interface TopicSaveReqVO {
+  id?: number // 话题ID
+  name: string // 话题名称
+  description?: string // 话题描述
+  sort?: number // 排序
+  status?: number // 状态:1-正常,2-禁用
+}
+
+// 查询话题分页列表
+export const getTopicPage = (params: TopicPageReqVO) => {
+  return request.get({ url: '/community/topic/page', params })
+}
+
+// 查询话题详情
+export const getTopic = (id: number) => {
+  return request.get({ url: '/community/topic/get?id=' + id })
+}
+
+// 新增话题
+export const createTopic = (data: TopicSaveReqVO) => {
+  return request.post({ url: '/community/topic/create', data })
+}
+
+// 修改话题
+export const updateTopic = (data: TopicSaveReqVO) => {
+  return request.put({ url: '/community/topic/update', data })
+}
+
+// 删除话题
+export const deleteTopic = (id: number) => {
+  return request.delete({ url: '/community/topic/delete?id=' + id })
+}
+
+// 获得启用的话题列表
+export const getEnabledTopicList = () => {
+  return request.get({ url: '/community/topic/list-enabled' })
+}
+

+ 78 - 0
src/api/system/version/index.ts

@@ -0,0 +1,78 @@
+import request from '@/config/axios'
+
+/**
+ * 平台类型枚举
+ */
+export enum PlatformTypeEnum {
+  ANDROID = 1, // 安卓
+  HARMONYOS = 2, // 鸿蒙
+  IOS = 3 // iOS
+}
+
+export interface VersionVO {
+  id?: number // 版本ID
+  platform: number // 平台类型:1-安卓,2-鸿蒙,3-iOS
+  versionCode: number // 版本号(内部版本号,用于比较)
+  versionName: string // 版本名称(如:1.0.0)
+  versionDesc?: string // 版本描述
+  downloadUrl: string // 下载地址/包地址
+  packageSize?: number // 包大小(单位:字节)
+  forceUpdate: boolean // 是否强制更新
+  updateLog?: string // 更新日志/更新说明
+  status: number // 状态:0-启用,1-禁用
+  publishTime?: Date // 发布时间
+  createTime?: Date // 创建时间
+  updateTime?: Date // 更新时间
+  creator?: string // 创建人
+  updater?: string // 更新人
+}
+
+// 查询版本管理列表
+export const getVersionPage = (params: PageParam) => {
+  return request.get({ url: '/system/version/page', params })
+}
+
+// 查询版本管理详情
+export const getVersion = (id: number) => {
+  return request.get({ url: '/system/version/get?id=' + id })
+}
+
+// 新增版本管理
+export const createVersion = (data: VersionVO) => {
+  return request.post({ url: '/system/version/create', data })
+}
+
+// 修改版本管理
+export const updateVersion = (data: VersionVO) => {
+  return request.put({ url: '/system/version/update', data })
+}
+
+// 删除版本管理
+export const deleteVersion = (id: number) => {
+  return request.delete({ url: '/system/version/delete?id=' + id })
+}
+
+// 批量删除版本管理
+export const deleteVersionList = (ids: number[]) => {
+  return request.delete({ url: '/system/version/delete-list', params: { ids: ids.join(',') } })
+}
+
+// 导出版本管理
+export const exportVersion = (params: any) => {
+  return request.download({ url: '/system/version/export-excel', params })
+}
+
+// 获取最新版本(用于APP检查更新)
+export const getLatestVersion = (platform: number) => {
+  return request.get({ url: `/system/version/latest?platform=${platform}` })
+}
+
+// 修改版本状态
+export const updateVersionStatus = (id: number, status: number) => {
+  const data = {
+    id,
+    status
+  }
+  return request.put({ url: '/system/version/update-status', data })
+}
+

+ 14 - 0
src/api/vehicle/alarm/index.ts

@@ -0,0 +1,14 @@
+import request from '@/config/axios'
+
+/**
+ * 车辆报警 API
+ */
+
+// 推送报警通知(测试用)
+export const pushAlarm = async (userId: number, vehicleId: number, alarmData?: any) => {
+  return await request.post({ 
+    url: `/vehicle/alarm/push?userId=${userId}&vehicleId=${vehicleId}`, 
+    data: alarmData || {} 
+  })
+}
+

+ 2 - 2
src/components/DiyEditor/components/mobile/Carousel/config.ts

@@ -41,8 +41,8 @@ export const component = {
     interval: 3,
     height: 174,
     items: [
-      { type: 'img', imgUrl: 'https://static.iocoder.cn/mall/banner-01.jpg', videoUrl: '' },
-      { type: 'img', imgUrl: 'https://static.iocoder.cn/mall/banner-02.jpg', videoUrl: '' }
+      { type: 'img', imgUrl: 'https://static.xindazhou.com/mall/banner-01.jpg', videoUrl: '' },
+      { type: 'img', imgUrl: 'https://static.xindazhou.com/mall/banner-02.jpg', videoUrl: '' }
     ] as CarouselItemProperty[],
     style: {
       bgType: 'color',

+ 1 - 1
src/components/DiyEditor/components/mobile/NoticeBar/config.ts

@@ -28,7 +28,7 @@ export const component = {
   name: '公告栏',
   icon: 'ep:bell',
   property: {
-    iconUrl: 'http://mall.yudao.iocoder.cn/static/images/xinjian.png',
+    iconUrl: 'http://mall.yudao.xindazhou.com/static/images/xinjian.png',
     contents: [
       {
         text: '',

+ 8 - 8
src/components/DiyEditor/components/mobile/TabBar/config.ts

@@ -53,26 +53,26 @@ export const component = {
       {
         text: '首页',
         url: '/pages/index/index',
-        iconUrl: 'http://mall.yudao.iocoder.cn/static/images/1-001.png',
-        activeIconUrl: 'http://mall.yudao.iocoder.cn/static/images/1-002.png'
+        iconUrl: 'http://mall.yudao.xindazhou.com/static/images/1-001.png',
+        activeIconUrl: 'http://mall.yudao.xindazhou.com/static/images/1-002.png'
       },
       {
         text: '分类',
         url: '/pages/index/category?id=3',
-        iconUrl: 'http://mall.yudao.iocoder.cn/static/images/2-001.png',
-        activeIconUrl: 'http://mall.yudao.iocoder.cn/static/images/2-002.png'
+        iconUrl: 'http://mall.yudao.xindazhou.com/static/images/2-001.png',
+        activeIconUrl: 'http://mall.yudao.xindazhou.com/static/images/2-002.png'
       },
       {
         text: '购物车',
         url: '/pages/index/cart',
-        iconUrl: 'http://mall.yudao.iocoder.cn/static/images/3-001.png',
-        activeIconUrl: 'http://mall.yudao.iocoder.cn/static/images/3-002.png'
+        iconUrl: 'http://mall.yudao.xindazhou.com/static/images/3-001.png',
+        activeIconUrl: 'http://mall.yudao.xindazhou.com/static/images/3-002.png'
       },
       {
         text: '我的',
         url: '/pages/index/user',
-        iconUrl: 'http://mall.yudao.iocoder.cn/static/images/4-001.png',
-        activeIconUrl: 'http://mall.yudao.iocoder.cn/static/images/4-002.png'
+        iconUrl: 'http://mall.yudao.xindazhou.com/static/images/4-001.png',
+        activeIconUrl: 'http://mall.yudao.xindazhou.com/static/images/4-002.png'
       }
     ]
   }

+ 1 - 1
src/components/DiyEditor/components/mobile/UserCard/index.vue

@@ -5,7 +5,7 @@
         <el-avatar :size="60">
           <Icon icon="ep:avatar" :size="60" />
         </el-avatar>
-        <span class="text-18px font-bold">芋道源码</span>
+        <span class="text-18px font-bold">新大洲源码</span>
       </div>
       <Icon icon="tdesign:qrcode" :size="20" />
     </div>

+ 1 - 1
src/components/DiyEditor/index.vue

@@ -270,7 +270,7 @@ watch(
       return
     }
     // 如果是基础设置页,默认选中的索引改成 -1,为了防止删除组件后切换到此页导致报错
-    // https://gitee.com/yudaocode/yudao-ui-admin-vue3/pulls/792
+    // https://gitee.com/xindazhou/xdz-admin/pulls/792
     if (props.showTabBar) {
       selectedComponentIndex.value = -1
     }

+ 2 - 1
src/components/DocAlert/index.vue

@@ -22,7 +22,8 @@ const goToUrl = () => {
 
 /** 是否开启 */
 const getEnable = () => {
-  return import.meta.env.VITE_APP_DOCALERT_ENABLE !== 'false'
+  // 默认关闭文档提示,因为文档链接不存在
+  return import.meta.env.VITE_APP_DOCALERT_ENABLE === 'true'
 }
 </script>
 <style scoped>

+ 1 - 1
src/components/FormCreate/src/useFormCreateDesigner.ts

@@ -110,7 +110,7 @@ export const useFormCreateDesigner = async (designer: Ref) => {
    * 修复重复的字段 ID 问题
    * 当复制组件时,自动为新组件生成新的字段 ID
    *
-   * 对应 issue:https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues/ICM22X
+   * 对应 issue:https://gitee.com/xindazhou/xdz-admin/issues/ICM22X
    */
   const fixDuplicateFields = () => {
     // 获取当前所有规则

+ 1 - 1
src/components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue

@@ -642,7 +642,7 @@ const previewProcessJson = () => {
   })
 }
 
-/* ------------------------------------------------ 芋道源码 methods ------------------------------------------------------ */
+/* ------------------------------------------------ 新大洲源码 methods ------------------------------------------------------ */
 onMounted(() => {
   initBpmnModeler()
   createNewDiagram(props.value)

+ 1 - 1
src/components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue

@@ -71,7 +71,7 @@
       <!-- 新增的时间事件配置项 -->
       <el-collapse-item v-if="elementType === 'IntermediateCatchEvent'" name="timeEvent">
         <template #title><Icon icon="ep:timer" />时间事件</template>
-        <!-- 相关 issue:https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues/ICNRW2 -->
+        <!-- 相关 issue:https://gitee.com/xindazhou/xdz-admin/issues/ICNRW2 -->
         <TimeEventConfig :businessObject="elementBusinessObject" :key="elementId" />
       </el-collapse-item>
     </el-collapse>

+ 1 - 1
src/components/bpmnProcessDesigner/package/penal/listeners/ElementListeners.vue

@@ -299,7 +299,7 @@ const resetListenersList = () => {
   otherExtensionList.value =
     businessObject?.extensionElements?.values?.filter(
       (ex) => ex.$type !== `${prefix}:ExecutionListener`
-    ) ?? [] // 保留非监听器类型的扩展属性,避免移除监听器时清空其他配置(如审批人等)。相关案例:https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues/ICMSYC
+    ) ?? [] // 保留非监听器类型的扩展属性,避免移除监听器时清空其他配置(如审批人等)。相关案例:https://gitee.com/xindazhou/xdz-admin/issues/ICMSYC
   bpmnElementListeners.value =
     businessObject?.extensionElements?.values?.filter(
       (ex) => ex.$type === `${prefix}:ExecutionListener`

+ 1 - 1
src/components/bpmnProcessDesigner/package/penal/listeners/UserTaskListeners.vue

@@ -348,7 +348,7 @@ const resetListenersList = () => {
   otherExtensionList.value =
     businessObject?.extensionElements?.values?.filter(
       (ex) => ex.$type !== `${prefix}:TaskListener`
-    ) ?? [] // 保留非监听器类型的扩展属性,避免移除监听器时清空其他配置(如审批人等)。相关案例:https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues/ICMSYC
+    ) ?? [] // 保留非监听器类型的扩展属性,避免移除监听器时清空其他配置(如审批人等)。相关案例:https://gitee.com/xindazhou/xdz-admin/issues/ICMSYC
   bpmnElementListeners.value =
     businessObject?.extensionElements?.values?.filter(
       (ex) => ex.$type === `${prefix}:TaskListener`

+ 45 - 10
src/config/axios/service.ts

@@ -32,6 +32,10 @@ export const isRelogin = { show: false }
 let requestList: any[] = []
 // 是否正在刷新中
 let isRefreshToken = false
+// 刷新 token 失败计数,防止无限循环
+let refreshTokenFailCount = 0
+// 最大刷新失败次数
+const MAX_REFRESH_FAIL_COUNT = 3
 // 请求白名单,无须 token 的接口
 const whiteList: string[] = ['/login', '/refresh-token']
 
@@ -56,8 +60,10 @@ service.interceptors.request.use(
         return (isToken = false)
       }
     })
-    if (getAccessToken() && !isToken) {
-      config.headers.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token
+    // 每次请求都重新获取最新的 token,确保使用刷新后的 token
+    const accessToken = getAccessToken()
+    if (accessToken && !isToken) {
+      config.headers.Authorization = 'Bearer ' + accessToken // 让每个请求携带自定义token
     }
     // 设置租户
     if (tenantEnable && tenantEnable === 'true') {
@@ -152,41 +158,70 @@ service.interceptors.response.use(
       // 如果是忽略的错误码,直接返回 msg 异常
       return Promise.reject(msg)
     } else if (code === 401) {
+      console.warn('[401错误] 请求返回 401:', config.url, '当前刷新失败次数:', refreshTokenFailCount)
+      // 如果刷新失败次数过多,直接登出,防止无限循环
+      if (refreshTokenFailCount >= MAX_REFRESH_FAIL_COUNT) {
+        console.error('[401错误] 刷新失败次数过多,直接登出')
+        refreshTokenFailCount = 0
+        return handleAuthorized()
+      }
       // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
       if (!isRefreshToken) {
         isRefreshToken = true
         // 1. 如果获取不到刷新令牌,则只能执行登出操作
         if (!getRefreshToken()) {
+          isRefreshToken = false
           return handleAuthorized()
         }
         // 2. 进行刷新访问令牌
         try {
           const refreshTokenRes = await refreshToken()
-          // 2.1 刷新成功,回放队列的请求 + 当前请求
+          // 2.1 刷新成功,重置失败计数,回放队列的请求 + 当前请求
           setToken((await refreshTokenRes).data.data)
-          config.headers!.Authorization = 'Bearer ' + getAccessToken()
+          refreshTokenFailCount = 0 // 重置失败计数
+          // 确保使用最新刷新的 token
+          const newAccessToken = getAccessToken()
+          if (!newAccessToken) {
+            console.error('[Token刷新] 刷新 token 后获取不到新的 access token')
+            throw new Error('刷新 token 后获取不到新的 access token')
+          }
+          console.log('[Token刷新] 刷新成功,新 token:', newAccessToken.substring(0, 20) + '...')
+          config.headers!.Authorization = 'Bearer ' + newAccessToken
+          // 回放队列中的请求,确保它们也使用新的 token
           requestList.forEach((cb: any) => {
             cb()
           })
           requestList = []
+          isRefreshToken = false
+          // 重新发送当前请求,使用新的 token
+          console.log('[Token刷新] 重新发送请求:', config.url)
           return service(config)
         } catch (e) {
           // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
-          // 2.2 刷新失败,只回放队列的请求
+          // 2.2 刷新失败,增加失败计数
+          refreshTokenFailCount++
+          // 只回放队列的请求
           requestList.forEach((cb: any) => {
             cb()
           })
-          // 提示是否要登出。即不回放当前请求!不然会形成递归
-          return handleAuthorized()
-        } finally {
           requestList = []
           isRefreshToken = false
+          // 提示是否要登出。即不回放当前请求!不然会形成递归
+          if (refreshTokenFailCount >= MAX_REFRESH_FAIL_COUNT) {
+            refreshTokenFailCount = 0
+            return handleAuthorized()
+          }
+          return Promise.reject(e)
         }
       } else {
         // 添加到队列,等待刷新获取到新的令牌
         return new Promise((resolve) => {
           requestList.push(() => {
-            config.headers!.Authorization = 'Bearer ' + getAccessToken() // 让每个请求携带自定义token 请根据实际情况自行修改
+            // 从队列中取出时,重新获取最新的 token
+            const latestToken = getAccessToken()
+            if (latestToken) {
+              config.headers!.Authorization = 'Bearer ' + latestToken
+            }
             resolve(service(config))
           })
         })
@@ -203,7 +238,7 @@ service.interceptors.response.use(
           t('sys.api.errMsg901') +
           '</div>' +
           '<div> &nbsp; </div>' +
-          '<div>参考 https://doc.iocoder.cn/ 教程</div>' +
+          '<div>参考 https://doc.xindazhou.com/ 教程</div>' +
           '<div> &nbsp; </div>' +
           '<div>5 分钟搭建本地环境</div>'
       })

+ 1 - 1
src/layout/components/UserInfo/src/UserInfo.vue

@@ -50,7 +50,7 @@ const toProfile = async () => {
   push('/user/profile')
 }
 const toDocument = () => {
-  window.open('https://doc.iocoder.cn/')
+  window.open('https://doc.xindazhou.com/')
 }
 </script>
 

+ 1 - 1
src/permission.ts

@@ -68,7 +68,7 @@ router.beforeEach(async (to, from, next) => {
       const userStore = useUserStoreWithOut()
       const permissionStore = usePermissionStoreWithOut()
       // 异步加载字典
-      // 另外,间接 issue:https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues/ID9FLI
+      // 另外,间接 issue:https://gitee.com/xindazhou/xdz-admin/issues/ID9FLI
       if (!dictStore.getIsSetDict) {
         dictStore.setDictMap().then()
       }

+ 10 - 0
src/router/modules/remaining.ts

@@ -436,6 +436,16 @@ const remainingRouter: AppRouteRecordRaw[] = [
     meta: { hidden: true },
     children: [
       {
+        path: 'user',
+        name: 'MemberUser',
+        meta: {
+          title: '会员用户',
+          noCache: false,
+          hidden: true
+        },
+        component: () => import('@/views/member/user/index.vue')
+      },
+      {
         path: 'user/detail/:id',
         name: 'MemberUserDetail',
         meta: {

+ 1 - 1
src/store/modules/tagsView.ts

@@ -93,7 +93,7 @@ export const useTagsViewStore = defineStore('tagsView', {
     delCachedView() {
       const route = router.currentRoute.value
       const index = findIndex<string>(this.getCachedViews, (v) => v === route.name)
-      // 需要注释,解决“标签页刷新无效”。相关案例:https://github.com/yudaocode/yudao-ui-admin-vue3/issues/180
+      // 需要注释,解决“标签页刷新无效”。相关案例:https://github.com/xindazhou/xdz-admin/issues/180
       // for (const v of this.visitedViews) {
       //   if (v.name === route.name) {
       //     return

+ 1 - 1
src/utils/constants.ts

@@ -1,5 +1,5 @@
 /**
- * Created by 芋道源码
+ * Created by 新大洲源码
  *
  * 枚举类
  */

+ 1 - 1
src/utils/formCreate.ts

@@ -7,7 +7,7 @@ import formCreate from '@form-create/element-ui'
 /** 编码表单 Conf */
 export const encodeConf = (designerRef: object) => {
   // @ts-ignore
-  // 关联案例:https://gitee.com/yudaocode/yudao-ui-admin-vue3/pulls/834/
+  // 关联案例:https://gitee.com/xindazhou/xdz-admin/pulls/834/
   return formCreate.toJson(designerRef.value.getOption())
 }
 

+ 47 - 379
src/views/Home/Index.vue

@@ -1,194 +1,49 @@
 <template>
   <div>
+    <!-- 欢迎信息 -->
     <el-card shadow="never">
       <el-skeleton :loading="loading" animated>
-        <el-row :gutter="16" justify="space-between">
-          <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
-            <div class="flex items-center">
-              <el-avatar :src="avatar" :size="70" class="mr-16px">
-                <img src="@/assets/imgs/avatar.gif" alt="" />
-              </el-avatar>
-              <div>
-                <div class="text-20px">
-                  {{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }}
-                </div>
-                <div class="mt-10px text-14px text-gray-500">
-                  {{ t('workplace.toady') }},20℃ - 32℃!
-                </div>
-              </div>
+        <div class="flex items-center">
+          <el-avatar :src="avatar" :size="70" class="mr-16px">
+            <img src="@/assets/imgs/avatar.gif" alt="" />
+          </el-avatar>
+          <div>
+            <div class="text-20px">
+              {{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }}
             </div>
-          </el-col>
-          <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
-            <div class="h-70px flex items-center justify-end lt-sm:mt-10px">
-              <div class="px-8px text-right">
-                <div class="mb-16px text-14px text-gray-400">{{ t('workplace.project') }}</div>
-                <CountTo
-                  class="text-20px"
-                  :start-val="0"
-                  :end-val="totalSate.project"
-                  :duration="2600"
-                />
-              </div>
-              <el-divider direction="vertical" />
-              <div class="px-8px text-right">
-                <div class="mb-16px text-14px text-gray-400">{{ t('workplace.toDo') }}</div>
-                <CountTo
-                  class="text-20px"
-                  :start-val="0"
-                  :end-val="totalSate.todo"
-                  :duration="2600"
-                />
-              </div>
-              <el-divider direction="vertical" border-style="dashed" />
-              <div class="px-8px text-right">
-                <div class="mb-16px text-14px text-gray-400">{{ t('workplace.access') }}</div>
-                <CountTo
-                  class="text-20px"
-                  :start-val="0"
-                  :end-val="totalSate.access"
-                  :duration="2600"
-                />
-              </div>
+            <div class="mt-10px text-14px text-gray-500">
+              {{ t('workplace.toady') }}
             </div>
-          </el-col>
-        </el-row>
+          </div>
+        </div>
       </el-skeleton>
     </el-card>
-  </div>
-
-  <el-row class="mt-8px" :gutter="8" justify="space-between">
-    <el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-8px">
-      <el-card shadow="never">
-        <template #header>
-          <div class="h-3 flex justify-between">
-            <span>{{ t('workplace.project') }}</span>
-            <el-link
-              type="primary"
-              :underline="false"
-              href="https://github.com/yudaocode"
-              target="_blank"
-            >
-              {{ t('action.more') }}
-            </el-link>
-          </div>
-        </template>
-        <el-skeleton :loading="loading" animated>
-          <el-row>
-            <el-col
-              v-for="(item, index) in projects"
-              :key="`card-${index}`"
-              :xl="8"
-              :lg="8"
-              :md="8"
-              :sm="24"
-              :xs="24"
-            >
-              <el-card
-                shadow="hover"
-                class="mr-5px mt-5px cursor-pointer"
-                @click="handleProjectClick(item.message)"
-              >
-                <div class="flex items-center">
-                  <Icon
-                    :icon="item.icon"
-                    :size="25"
-                    class="mr-8px"
-                    :style="{ color: item.color }"
-                  />
-                  <span class="text-16px">{{ item.name }}</span>
-                </div>
-                <div class="mt-12px text-12px text-gray-400">{{ t(item.message) }}</div>
-                <div class="mt-12px flex justify-between text-12px text-gray-400">
-                  <span>{{ item.personal }}</span>
-                  <span>{{ formatTime(item.time, 'yyyy-MM-dd') }}</span>
-                </div>
-              </el-card>
-            </el-col>
-          </el-row>
-        </el-skeleton>
-      </el-card>
 
-      <el-card shadow="never" class="mt-8px">
-        <el-skeleton :loading="loading" animated>
-          <el-row :gutter="20" justify="space-between">
-            <el-col :xl="10" :lg="10" :md="24" :sm="24" :xs="24">
-              <el-card shadow="hover" class="mb-8px">
-                <el-skeleton :loading="loading" animated>
-                  <Echart :options="pieOptionsData" :height="280" />
-                </el-skeleton>
-              </el-card>
-            </el-col>
-            <el-col :xl="14" :lg="14" :md="24" :sm="24" :xs="24">
-              <el-card shadow="hover" class="mb-8px">
-                <el-skeleton :loading="loading" animated>
-                  <Echart :options="barOptionsData" :height="280" />
-                </el-skeleton>
-              </el-card>
-            </el-col>
-          </el-row>
-        </el-skeleton>
-      </el-card>
-    </el-col>
-    <el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-8px">
-      <el-card shadow="never">
-        <template #header>
-          <div class="h-3 flex justify-between">
-            <span>{{ t('workplace.shortcutOperation') }}</span>
-          </div>
-        </template>
-        <el-skeleton :loading="loading" animated>
-          <el-row>
-            <el-col v-for="item in shortcut" :key="`team-${item.name}`" :span="8" class="mb-8px">
-              <div class="flex items-center">
-                <Icon :icon="item.icon" class="mr-8px" :style="{ color: item.color }" />
-                <el-link type="default" :underline="false" @click="handleShortcutClick(item.url)">
-                  {{ item.name }}
-                </el-link>
-              </div>
-            </el-col>
-          </el-row>
-        </el-skeleton>
-      </el-card>
-      <el-card shadow="never" class="mt-8px">
-        <template #header>
-          <div class="h-3 flex justify-between">
-            <span>{{ t('workplace.notice') }}</span>
-            <el-link type="primary" :underline="false">{{ t('action.more') }}</el-link>
-          </div>
-        </template>
-        <el-skeleton :loading="loading" animated>
-          <div v-for="(item, index) in notice" :key="`dynamics-${index}`">
+    <!-- 快捷操作 -->
+    <el-card shadow="never" class="mt-8px">
+      <template #header>
+        <div class="h-3 flex justify-between">
+          <span>{{ t('workplace.shortcutOperation') }}</span>
+        </div>
+      </template>
+      <el-skeleton :loading="loading" animated>
+        <el-row>
+          <el-col v-for="item in shortcut" :key="`shortcut-${item.name}`" :span="8" class="mb-8px">
             <div class="flex items-center">
-              <el-avatar :src="avatar" :size="35" class="mr-16px">
-                <img src="@/assets/imgs/avatar.gif" alt="" />
-              </el-avatar>
-              <div>
-                <div class="text-14px">
-                  <Highlight :keys="item.keys.map((v) => t(v))">
-                    {{ item.type }} : {{ item.title }}
-                  </Highlight>
-                </div>
-                <div class="mt-16px text-12px text-gray-400">
-                  {{ formatTime(item.date, 'yyyy-MM-dd') }}
-                </div>
-              </div>
+              <Icon :icon="item.icon" class="mr-8px" :style="{ color: item.color }" />
+              <el-link type="default" :underline="false" @click="handleShortcutClick(item.url)">
+                {{ item.name }}
+              </el-link>
             </div>
-            <el-divider />
-          </div>
-        </el-skeleton>
-      </el-card>
-    </el-col>
-  </el-row>
+          </el-col>
+        </el-row>
+      </el-skeleton>
+    </el-card>
+  </div>
 </template>
 <script lang="ts" setup>
-import { set } from 'lodash-es'
-import { EChartsOption } from 'echarts'
-import { formatTime } from '@/utils'
-
 import { useUserStore } from '@/store/modules/user'
-// import { useWatermark } from '@/hooks/web/useWatermark'
-import type { WorkplaceTotal, Project, Notice, Shortcut } from './types'
-import { pieOptions, barOptions } from './echarts-data'
+import type { Shortcut } from './types'
 import { useRouter } from 'vue-router'
 
 defineOptions({ name: 'Index' })
@@ -196,227 +51,40 @@ defineOptions({ name: 'Index' })
 const { t } = useI18n()
 const router = useRouter()
 const userStore = useUserStore()
-// const { setWatermark } = useWatermark()
 const loading = ref(true)
 const avatar = userStore.getUser.avatar
 const username = userStore.getUser.nickname
-const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
-// 获取统计数
-let totalSate = reactive<WorkplaceTotal>({
-  project: 0,
-  access: 0,
-  todo: 0
-})
-
-const getCount = async () => {
-  const data = {
-    project: 40,
-    access: 2340,
-    todo: 10
-  }
-  totalSate = Object.assign(totalSate, data)
-}
 
-// 获取项目数
-let projects = reactive<Project[]>([])
-const getProject = async () => {
-  const data = [
-    {
-      name: 'ruoyi-vue-pro',
-      icon: 'simple-icons:springboot',
-      message: 'github.com/YunaiV/ruoyi-vue-pro',
-      personal: 'Spring Boot 单体架构',
-      time: new Date('2025-01-02'),
-      color: '#6DB33F'
-    },
-    {
-      name: 'yudao-ui-admin-vue3',
-      icon: 'ep:element-plus',
-      message: 'github.com/yudaocode/yudao-ui-admin-vue3',
-      personal: 'Vue3 + element-plus 管理后台',
-      time: new Date('2025-02-03'),
-      color: '#409EFF'
-    },
-    {
-      name: 'yudao-ui-mall-uniapp',
-      icon: 'icon-park-outline:mall-bag',
-      message: 'github.com/yudaocode/yudao-ui-mall-uniapp',
-      personal: 'Vue3 + uniapp 商城手机端',
-      time: new Date('2025-03-04'),
-      color: '#ff4d4f'
-    },
-    {
-      name: 'yudao-cloud',
-      icon: 'material-symbols:cloud-outline',
-      message: 'github.com/YunaiV/yudao-cloud',
-      personal: 'Spring Cloud 微服务架构',
-      time: new Date('2025-04-05'),
-      color: '#1890ff'
-    },
-    {
-      name: 'yudao-ui-admin-vben',
-      icon: 'devicon:antdesign',
-      message: 'github.com/yudaocode/yudao-ui-admin-vben',
-      personal: 'Vue3 + vben5(antd) 管理后台',
-      time: new Date('2025-05-06'),
-      color: '#e18525'
-    },
-    {
-      name: 'yudao-ui-admin-uniapp',
-      icon: 'ant-design:mobile',
-      message: 'github.com/yudaocode/yudao-ui-admin-uniapp',
-      personal: 'Vue3 + uniapp 管理手机端',
-      time: new Date('2025-06-01'),
-      color: '#2979ff'
-    }
-  ]
-  projects = Object.assign(projects, data)
-}
-
-// 获取通知公告
-let notice = reactive<Notice[]>([])
-const getNotice = async () => {
-  const data = [
-    {
-      title: '系统支持 JDK 8/17/21,Vue 2/3',
-      type: '技术兼容性',
-      keys: ['JDK', 'Vue'],
-      date: new Date()
-    },
-    {
-      title: '后端提供 Spring Boot 2.7/3.2 + Cloud 双架构',
-      type: '架构灵活性',
-      keys: ['Boot', 'Cloud'],
-      date: new Date()
-    },
-    {
-      title: '全部开源,个人与企业可 100% 直接使用,无需授权',
-      type: '开源免授权',
-      keys: ['无需授权'],
-      date: new Date()
-    },
-    {
-      title: '国内使用最广泛的快速开发平台,远超 10w+ 企业使用',
-      type: '广泛企业认可',
-      keys: ['最广泛', '10w+'],
-      date: new Date()
-    }
-  ]
-  notice = Object.assign(notice, data)
-}
-
-// 获取快捷入口
+// 获取快捷入口 - 只保留真实可用的菜单
 let shortcut = reactive<Shortcut[]>([])
 
 const getShortcut = async () => {
   const data = [
     {
-      name: '首页',
-      icon: 'ion:home-outline',
-      url: '/',
-      color: '#1fdaca'
-    },
-    {
-      name: '商城中心',
-      icon: 'ep:shop',
-      url: '/mall/home',
-      color: '#ff6b6b'
-    },
-    {
-      name: 'AI 大模型',
-      icon: 'tabler:ai',
-      url: '/ai/chat',
-      color: '#7c3aed'
-    },
-    {
-      name: 'ERP 系统',
-      icon: 'simple-icons:erpnext',
-      url: '/erp/home',
-      color: '#3fb27f'
-    },
-    {
-      name: 'CRM 系统',
-      icon: 'simple-icons:civicrm',
-      url: '/crm/backlog',
-      color: '#4daf1bc9'
+      name: '系统管理',
+      icon: 'ep:tools',
+      url: '/system',
+      color: '#409EFF'
     },
     {
-      name: 'IoT 物联网',
-      icon: 'fa-solid:hdd',
-      url: '/iot/home',
-      color: '#1a73e8'
+      name: '基础设施',
+      icon: 'ep:monitor',
+      url: '/infra',
+      color: '#67C23A'
     }
   ]
   shortcut = Object.assign(shortcut, data)
 }
 
-// 用户来源
-const getUserAccessSource = async () => {
-  const data = [
-    { value: 335, name: 'analysis.directAccess' },
-    { value: 310, name: 'analysis.mailMarketing' },
-    { value: 234, name: 'analysis.allianceAdvertising' },
-    { value: 135, name: 'analysis.videoAdvertising' },
-    { value: 1548, name: 'analysis.searchEngines' }
-  ]
-  set(
-    pieOptionsData,
-    'legend.data',
-    data.map((v) => t(v.name))
-  )
-  pieOptionsData!.series![0].data = data.map((v) => {
-    return {
-      name: t(v.name),
-      value: v.value
-    }
-  })
-}
-const barOptionsData = reactive<EChartsOption>(barOptions) as EChartsOption
-
-// 周活跃量
-const getWeeklyUserActivity = async () => {
-  const data = [
-    { value: 13253, name: 'analysis.monday' },
-    { value: 34235, name: 'analysis.tuesday' },
-    { value: 26321, name: 'analysis.wednesday' },
-    { value: 12340, name: 'analysis.thursday' },
-    { value: 24643, name: 'analysis.friday' },
-    { value: 1322, name: 'analysis.saturday' },
-    { value: 1324, name: 'analysis.sunday' }
-  ]
-  set(
-    barOptionsData,
-    'xAxis.data',
-    data.map((v) => t(v.name))
-  )
-  set(barOptionsData, 'series', [
-    {
-      name: t('analysis.activeQuantity'),
-      data: data.map((v) => v.value),
-      type: 'bar'
-    }
-  ])
+const handleShortcutClick = (url: string) => {
+  router.push(url)
 }
 
-const getAllApi = async () => {
-  await Promise.all([
-    getCount(),
-    getProject(),
-    getNotice(),
-    getShortcut(),
-    getUserAccessSource(),
-    getWeeklyUserActivity()
-  ])
+// 初始化
+const init = async () => {
+  await getShortcut()
   loading.value = false
 }
 
-const handleProjectClick = (message: string) => {
-  window.open(`https://${message}`, '_blank')
-}
-
-const handleShortcutClick = (url: string) => {
-  router.push(url)
-}
-
-getAllApi()
+init()
 </script>

+ 9 - 10
src/views/Login/Login.vue

@@ -7,23 +7,22 @@
       <div
         :class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden overflow-x-hidden overflow-y-auto`"
       >
-        <!-- 左上角的 logo + 系统标题 -->
-        <div class="relative flex items-center text-white">
-          <img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" />
-          <span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>
-        </div>
         <!-- 左边的背景图 + 欢迎语 -->
-        <div class="h-[calc(100%-60px)] flex items-center justify-center">
+        <div class="h-full flex flex-col items-center justify-center">
           <TransitionGroup
             appear
             enter-active-class="animate__animated animate__bounceInLeft"
             tag="div"
+            class="flex flex-col items-center"
           >
-            <img key="1" alt="" class="w-350px" src="@/assets/svgs/login-box-bg.svg" />
-            <div key="2" class="text-3xl text-white">{{ t('login.welcome') }}</div>
-            <div key="3" class="mt-5 text-14px font-normal text-white">
-              {{ t('login.message') }}
+            <!-- 系统标题 - 突出显示 -->
+            <div key="0" class="mb-30px text-center">
+              <h1 class="text-5xl font-bold text-white mb-10px tracking-wider drop-shadow-lg">
+                新大洲车联网CRM系统
+              </h1>
             </div>
+            <img key="1" alt="" class="w-350px mb-20px" src="@/assets/svgs/login-box-bg.svg" />
+            <div key="2" class="text-2xl text-white font-medium mb-10px">{{ t('login.welcome') }}</div>
           </TransitionGroup>
         </div>
       </div>

+ 1 - 1
src/views/Login/SocialLogin.vue

@@ -199,7 +199,7 @@ const loginData = reactive({
   captchaEnable: import.meta.env.VITE_APP_CAPTCHA_ENABLE !== 'false',
   tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE !== 'false',
   loginForm: {
-    tenantName: '芋道源码',
+    tenantName: '新大洲源码',
     username: 'admin',
     password: 'admin123',
     captchaVerification: '',

+ 6 - 16
src/views/Login/components/LoginForm.vue

@@ -113,6 +113,8 @@
           </el-row>
         </el-form-item>
       </el-col>
+      <!-- 其他登录方式 - 已注释,保留代码 -->
+      <!--
       <el-divider content-position="center">{{ t('login.otherLogin') }}</el-divider>
       <el-col :span="24" class="px-10px">
         <el-form-item>
@@ -129,21 +131,7 @@
           </div>
         </el-form-item>
       </el-col>
-      <el-divider content-position="center">萌新必读</el-divider>
-      <el-col :span="24" class="px-10px">
-        <el-form-item>
-          <div class="w-full flex justify-between">
-            <el-link href="https://doc.iocoder.cn/" target="_blank">📚开发指南</el-link>
-            <el-link href="https://doc.iocoder.cn/video/" target="_blank">🔥视频教程</el-link>
-            <el-link href="https://www.iocoder.cn/Interview/good-collection/" target="_blank">
-              ⚡面试手册
-            </el-link>
-            <el-link href="http://static.yudao.iocoder.cn/mp/Aix9975.jpeg" target="_blank">
-              🤝外包咨询
-            </el-link>
-          </div>
-        </el-form-item>
-      </el-col>
+      -->
     </el-row>
   </el-form>
 </template>
@@ -283,7 +271,9 @@ const handleLogin = async (params: any) => {
     }
   } finally {
     loginLoading.value = false
-    loading.value.close()
+    if (loading.value) {
+      loading.value.close()
+    }
   }
 }
 

+ 1 - 1
src/views/Login/components/MobileForm.vue

@@ -133,7 +133,7 @@ const loginData = reactive({
   },
   loginForm: {
     uuid: '',
-    tenantName: '芋道源码',
+    tenantName: '新大洲源码',
     mobileNumber: '',
     code: ''
   }

+ 2 - 2
src/views/Login/components/SSOLogin.vue

@@ -84,8 +84,8 @@ const init = async () => {
   // 防止在没有登录的情况下循环弹窗
   if (typeof route.query.client_id === 'undefined') return
   // 解析参数
-  // 例如说【自动授权不通过】:client_id=default&redirect_uri=https%3A%2F%2Fwww.iocoder.cn&response_type=code&scope=user.read%20user.write
-  // 例如说【自动授权通过】:client_id=default&redirect_uri=https%3A%2F%2Fwww.iocoder.cn&response_type=code&scope=user.read
+  // 例如说【自动授权不通过】:client_id=default&redirect_uri=https%3A%2F%2Fwww.xindazhou.com&response_type=code&scope=user.read%20user.write
+  // 例如说【自动授权通过】:client_id=default&redirect_uri=https%3A%2F%2Fwww.xindazhou.com&response_type=code&scope=user.read
   queryParams.responseType = route.query.response_type as string
   queryParams.clientId = route.query.client_id as string
   queryParams.redirectUri = route.query.redirect_uri as string

+ 1 - 1
src/views/ai/chat/index/components/message/MessageListEmpty.vue

@@ -3,7 +3,7 @@
   <div class="relative flex flex-row justify-center w-full h-full">
     <!-- title -->
     <div class="flex flex-col justify-center">
-      <div class="text-28px font-bold text-center">芋道 AI</div>
+      <div class="text-28px font-bold text-center">新大洲 AI</div>
       <div class="flex flex-row flex-wrap items-center justify-center w-460px mt-20px">
         <div
           class="flex justify-center w-180px leading-50px border border-solid border-[#e4e4e4] rounded-10px m-10px cursor-pointer hover:bg-[rgba(243,243,243,0.73)]"

+ 1 - 1
src/views/ai/chat/manager/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="AI 对话聊天" url="https://doc.iocoder.cn/ai/chat/" />
+  <doc-alert title="AI 对话聊天" url="https://doc.xindazhou.com/ai/chat/" />
 
   <ContentWrap>
     <el-tabs>

+ 1 - 1
src/views/ai/image/manager/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="AI 绘图创作" url="https://doc.iocoder.cn/ai/image/" />
+  <doc-alert title="AI 绘图创作" url="https://doc.xindazhou.com/ai/image/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/ai/knowledge/knowledge/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="AI 知识库" url="https://doc.iocoder.cn/ai/knowledge/" />
+  <doc-alert title="AI 知识库" url="https://doc.xindazhou.com/ai/knowledge/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/ai/mindmap/manager/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="AI 思维导图" url="https://doc.iocoder.cn/ai/mindmap/" />
+  <doc-alert title="AI 思维导图" url="https://doc.xindazhou.com/ai/mindmap/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/ai/model/apiKey/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="AI 手册" url="https://doc.iocoder.cn/ai/build/" />
+  <doc-alert title="AI 手册" url="https://doc.xindazhou.com/ai/build/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/ai/model/chatRole/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="AI 对话聊天" url="https://doc.iocoder.cn/ai/chat/" />
+  <doc-alert title="AI 对话聊天" url="https://doc.xindazhou.com/ai/chat/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/ai/model/model/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="AI 手册" url="https://doc.iocoder.cn/ai/build/" />
+  <doc-alert title="AI 手册" url="https://doc.xindazhou.com/ai/build/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/ai/model/tool/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="AI 工具调用(function calling)" url="https://doc.iocoder.cn/ai/tool/" />
+  <doc-alert title="AI 工具调用(function calling)" url="https://doc.xindazhou.com/ai/tool/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/ai/music/manager/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="AI 音乐创作" url="https://doc.iocoder.cn/ai/music/" />
+  <doc-alert title="AI 音乐创作" url="https://doc.xindazhou.com/ai/music/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/ai/utils/constants.ts

@@ -1,5 +1,5 @@
 /**
- * Created by 芋道源码
+ * Created by 新大洲源码
  *
  * AI 枚举类
  *

+ 1 - 1
src/views/ai/utils/utils.ts

@@ -1,5 +1,5 @@
 /**
- * Created by 芋道源码
+ * Created by 新大洲源码
  *
  * AI 枚举类
  *

+ 1 - 1
src/views/ai/write/manager/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="AI 写作助手" url="https://doc.iocoder.cn/ai/write/" />
+  <doc-alert title="AI 写作助手" url="https://doc.xindazhou.com/ai/write/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/bpm/category/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
+  <doc-alert title="工作流手册" url="https://doc.xindazhou.com/bpm/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/bpm/form/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="审批接入(流程表单)" url="https://doc.iocoder.cn/bpm/use-bpm-form/" />
+  <doc-alert title="审批接入(流程表单)" url="https://doc.xindazhou.com/bpm/use-bpm-form/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/bpm/group/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
+  <doc-alert title="工作流手册" url="https://doc.xindazhou.com/bpm/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/bpm/model/definition/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
+  <doc-alert title="工作流手册" url="https://doc.xindazhou.com/bpm/" />
 
   <ContentWrap>
     <el-table v-loading="loading" :data="list">

+ 1 - 1
src/views/bpm/oa/leave/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="审批接入(业务表单)" url="https://doc.iocoder.cn/bpm/use-business-form/" />
+  <doc-alert title="审批接入(业务表单)" url="https://doc.xindazhou.com/bpm/use-business-form/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/bpm/processExpression/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="流程表达式" url="https://doc.iocoder.cn/bpm/expression/" />
+  <doc-alert title="流程表达式" url="https://doc.xindazhou.com/bpm/expression/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/bpm/processInstance/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="流程发起、取消、重新发起" url="https://doc.iocoder.cn/bpm/process-instance/" />
+  <doc-alert title="流程发起、取消、重新发起" url="https://doc.xindazhou.com/bpm/process-instance/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/bpm/processInstance/manager/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
+  <doc-alert title="工作流手册" url="https://doc.xindazhou.com/bpm/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/bpm/processInstance/report/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
+  <doc-alert title="工作流手册" url="https://doc.xindazhou.com/bpm/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/bpm/processListener/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="执行监听器、任务监听器" url="https://doc.iocoder.cn/bpm/listener/" />
+  <doc-alert title="执行监听器、任务监听器" url="https://doc.xindazhou.com/bpm/listener/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/bpm/task/copy/index.vue

@@ -2,7 +2,7 @@
 <template>
   <doc-alert
     title="审批转办、委派、抄送"
-    url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/"
+    url="https://doc.xindazhou.com/bpm/task-delegation-and-cc/"
   />
 
   <ContentWrap>

+ 4 - 4
src/views/bpm/task/done/index.vue

@@ -1,11 +1,11 @@
 <template>
-  <doc-alert title="审批通过、不通过、驳回" url="https://doc.iocoder.cn/bpm/task-todo-done/" />
-  <doc-alert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" />
+  <doc-alert title="审批通过、不通过、驳回" url="https://doc.xindazhou.com/bpm/task-todo-done/" />
+  <doc-alert title="审批加签、减签" url="https://doc.xindazhou.com/bpm/sign/" />
   <doc-alert
     title="审批转办、委派、抄送"
-    url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/"
+    url="https://doc.xindazhou.com/bpm/task-delegation-and-cc/"
   />
-  <doc-alert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" />
+  <doc-alert title="审批加签、减签" url="https://doc.xindazhou.com/bpm/sign/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/bpm/task/manager/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
+  <doc-alert title="工作流手册" url="https://doc.xindazhou.com/bpm/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 4 - 4
src/views/bpm/task/todo/index.vue

@@ -1,11 +1,11 @@
 <template>
-  <doc-alert title="审批通过、不通过、驳回" url="https://doc.iocoder.cn/bpm/task-todo-done/" />
-  <doc-alert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" />
+  <doc-alert title="审批通过、不通过、驳回" url="https://doc.xindazhou.com/bpm/task-todo-done/" />
+  <doc-alert title="审批加签、减签" url="https://doc.xindazhou.com/bpm/sign/" />
   <doc-alert
     title="审批转办、委派、抄送"
-    url="https://doc.iocoder.cn/bpm/task-delegation-and-cc/"
+    url="https://doc.xindazhou.com/bpm/task-delegation-and-cc/"
   />
-  <doc-alert title="审批加签、减签" url="https://doc.iocoder.cn/bpm/sign/" />
+  <doc-alert title="审批加签、减签" url="https://doc.xindazhou.com/bpm/sign/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 238 - 0
src/views/community/comment/index.vue

@@ -0,0 +1,238 @@
+<template>
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form
+      ref="queryFormRef"
+      :inline="true"
+      :model="queryParams"
+      class="-mb-15px"
+      label-width="68px"
+    >
+      <el-form-item label="帖子ID" prop="postId">
+        <el-input
+          v-model="queryParams.postId"
+          class="!w-240px"
+          clearable
+          placeholder="请输入帖子ID"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="用户ID" prop="userId">
+        <el-input
+          v-model="queryParams.userId"
+          class="!w-240px"
+          clearable
+          placeholder="请输入用户ID"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select
+          v-model="queryParams.status"
+          class="!w-240px"
+          clearable
+          placeholder="请选择状态"
+        >
+          <el-option label="正常" :value="1" />
+          <el-option label="删除" :value="2" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery">
+          <Icon class="mr-5px" icon="ep:search" />
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon class="mr-5px" icon="ep:refresh" />
+          重置
+        </el-button>
+        <el-button
+          v-hasPermi="['community:comment:delete']"
+          :disabled="checkedIds.length === 0"
+          plain
+          type="danger"
+          @click="handleDeleteBatch"
+        >
+          <Icon class="mr-5px" icon="ep:delete" />
+          批量删除
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table
+      v-loading="loading"
+      :data="list"
+      @selection-change="handleRowCheckboxChange"
+    >
+      <el-table-column type="selection" width="55" />
+      <el-table-column label="评论ID" align="center" prop="id" width="80" />
+      <el-table-column label="帖子" align="left" width="200">
+        <template #default="scope">
+          <div>
+            <el-link
+              type="primary"
+              @click="openPostDetail(scope.row.postId)"
+            >
+              {{ scope.row.postTitle || '帖子ID: ' + scope.row.postId }}
+            </el-link>
+            <div class="text-xs text-gray-500">ID: {{ scope.row.postId }}</div>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="用户" align="center" width="150">
+        <template #default="scope">
+          <div>
+            <div>{{ scope.row.userNickname || '-' }}</div>
+            <div class="text-xs text-gray-500">ID: {{ scope.row.userId }}</div>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="评论内容"
+        align="left"
+        prop="content"
+        :show-overflow-tooltip="true"
+        min-width="300"
+      />
+      <el-table-column label="父评论" align="center" prop="parentId" width="100">
+        <template #default="scope">
+          <el-tag v-if="scope.row.parentId === 0" type="success">顶级评论</el-tag>
+          <el-tag v-else type="info">回复 #{{ scope.row.parentId }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="点赞数" align="center" prop="likeCount" width="100">
+        <template #default="scope">
+          <el-tag type="success">{{ scope.row.likeCount || 0 }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="status" width="100">
+        <template #default="scope">
+          <el-tag v-if="scope.row.status === 1" type="success">正常</el-tag>
+          <el-tag v-else-if="scope.row.status === 2" type="danger">删除</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        :formatter="dateFormatter"
+        width="180"
+      />
+      <el-table-column label="操作" align="center" width="150" fixed="right">
+        <template #default="scope">
+          <el-button
+            v-hasPermi="['community:comment:delete']"
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+</template>
+
+<script setup lang="ts">
+import { dateFormatter } from '@/utils/formatTime'
+import * as CommentApi from '@/api/community/comment'
+import * as PostApi from '@/api/community/post'
+
+defineOptions({ name: 'CommunityComment' })
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const loading = ref(true) // 列表的加载中
+const list = ref([]) // 列表的数据
+const total = ref(0) // 列表的总页数
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  postId: undefined,
+  userId: undefined,
+  status: undefined
+})
+const queryFormRef = ref() // 搜索的表单
+const checkedIds = ref<number[]>([]) // 选中的列表
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await CommentApi.getCommentPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 打开帖子详情 */
+const openPostDetail = (postId: number) => {
+  // 这里可以跳转到帖子详情页,或者打开弹窗
+  // 暂时使用消息提示
+  message.info('帖子ID: ' + postId)
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await CommentApi.deleteComment(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 批量删除按钮操作 */
+const handleDeleteBatch = async () => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await CommentApi.deleteCommentList(checkedIds.value)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+    checkedIds.value = []
+  } catch {}
+}
+
+/** 表格选中行变化 */
+const handleRowCheckboxChange = (selection: any[]) => {
+  checkedIds.value = selection.map((item) => item.id)
+}
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
+</script>
+

+ 536 - 0
src/views/community/post/index.vue

@@ -0,0 +1,536 @@
+<template>
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form
+      ref="queryFormRef"
+      :inline="true"
+      :model="queryParams"
+      class="-mb-15px"
+      label-width="68px"
+    >
+      <el-form-item label="用户ID" prop="userId">
+        <el-input
+          v-model="queryParams.userId"
+          class="!w-240px"
+          clearable
+          placeholder="请输入用户ID"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="标题" prop="title">
+        <el-input
+          v-model="queryParams.title"
+          class="!w-240px"
+          clearable
+          placeholder="请输入标题"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select
+          v-model="queryParams.status"
+          class="!w-240px"
+          clearable
+          placeholder="请选择状态"
+        >
+          <el-option label="正常" :value="1" />
+          <el-option label="删除" :value="2" />
+          <el-option label="审核中" :value="3" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="置顶" prop="isTop">
+        <el-select
+          v-model="queryParams.isTop"
+          class="!w-240px"
+          clearable
+          placeholder="请选择是否置顶"
+        >
+          <el-option label="是" :value="1" />
+          <el-option label="否" :value="0" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="精华" prop="isEssence">
+        <el-select
+          v-model="queryParams.isEssence"
+          class="!w-240px"
+          clearable
+          placeholder="请选择是否精华"
+        >
+          <el-option label="是" :value="1" />
+          <el-option label="否" :value="0" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery">
+          <Icon class="mr-5px" icon="ep:search" />
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon class="mr-5px" icon="ep:refresh" />
+          重置
+        </el-button>
+        <el-button
+          v-hasPermi="['community:post:delete']"
+          :disabled="checkedIds.length === 0"
+          plain
+          type="danger"
+          @click="handleDeleteBatch"
+        >
+          <Icon class="mr-5px" icon="ep:delete" />
+          批量删除
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table
+      v-loading="loading"
+      :data="list"
+      @selection-change="handleRowCheckboxChange"
+    >
+      <el-table-column type="selection" width="55" />
+      <el-table-column label="帖子ID" align="center" prop="id" width="80" />
+      <el-table-column label="用户" align="center" width="150">
+        <template #default="scope">
+          <div>
+            <div>{{ scope.row.userNickname || '-' }}</div>
+            <div class="text-xs text-gray-500">ID: {{ scope.row.userId }}</div>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="标题"
+        align="left"
+        prop="title"
+        :show-overflow-tooltip="true"
+        min-width="200"
+      >
+        <template #default="scope">
+          <el-link
+            type="primary"
+            @click="openDetail(scope.row.id)"
+          >
+            {{ scope.row.title }}
+          </el-link>
+        </template>
+      </el-table-column>
+      <el-table-column label="内容类型" align="center" prop="contentType" width="100">
+        <template #default="scope">
+          <el-tag v-if="scope.row.contentType === 1" type="info">普通文本</el-tag>
+          <el-tag v-else-if="scope.row.contentType === 2" type="warning">Markdown</el-tag>
+          <el-tag v-else-if="scope.row.contentType === 3" type="success">图片</el-tag>
+          <el-tag v-else-if="scope.row.contentType === 4" type="primary">视频</el-tag>
+          <el-tag v-else-if="scope.row.contentType === 5" type="danger">图文混合</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="统计" align="center" width="200">
+        <template #default="scope">
+          <div class="flex gap-2 justify-center">
+            <el-tag size="small" type="info">浏览: {{ scope.row.viewCount || 0 }}</el-tag>
+            <el-tag size="small" type="success">点赞: {{ scope.row.likeCount || 0 }}</el-tag>
+            <el-tag size="small" type="warning">评论: {{ scope.row.commentCount || 0 }}</el-tag>
+            <el-tag size="small" type="primary">收藏: {{ scope.row.collectCount || 0 }}</el-tag>
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="status" width="100">
+        <template #default="scope">
+          <el-tag v-if="scope.row.status === 1" type="success">正常</el-tag>
+          <el-tag v-else-if="scope.row.status === 2" type="danger">删除</el-tag>
+          <el-tag v-else-if="scope.row.status === 3" type="warning">审核中</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="置顶" align="center" prop="isTop" width="80">
+        <template #default="scope">
+          <el-switch
+            v-model="scope.row.isTop"
+            :active-value="1"
+            :inactive-value="0"
+            @change="handleTopChange(scope.row)"
+            :disabled="!checkPermi(['community:post:update'])"
+          />
+        </template>
+      </el-table-column>
+      <el-table-column label="精华" align="center" prop="isEssence" width="80">
+        <template #default="scope">
+          <el-switch
+            v-model="scope.row.isEssence"
+            :active-value="1"
+            :inactive-value="0"
+            @change="handleEssenceChange(scope.row)"
+            :disabled="!checkPermi(['community:post:update'])"
+          />
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        :formatter="dateFormatter"
+        width="180"
+      />
+      <el-table-column label="操作" align="center" width="200" fixed="right">
+        <template #default="scope">
+          <el-button
+            v-hasPermi="['community:post:query']"
+            link
+            type="primary"
+            @click="openDetail(scope.row.id)"
+          >
+            详情
+          </el-button>
+          <el-button
+            v-hasPermi="['community:post:update']"
+            link
+            type="warning"
+            @click="openStatusForm(scope.row)"
+          >
+            状态
+          </el-button>
+          <el-button
+            v-hasPermi="['community:post:delete']"
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 详情弹窗 -->
+  <Dialog v-model="detailVisible" title="帖子详情" width="900px">
+    <el-descriptions :column="2" border v-if="detailData">
+      <el-descriptions-item label="帖子ID">{{ detailData.id }}</el-descriptions-item>
+      <el-descriptions-item label="用户">
+        {{ detailData.userNickname }} (ID: {{ detailData.userId }})
+      </el-descriptions-item>
+      <el-descriptions-item label="标题" :span="2">{{ detailData.title }}</el-descriptions-item>
+      <el-descriptions-item label="内容类型">
+        <el-tag v-if="detailData.contentType === 1" type="info">普通文本</el-tag>
+        <el-tag v-else-if="detailData.contentType === 2" type="warning">Markdown</el-tag>
+        <el-tag v-else-if="detailData.contentType === 3" type="success">图片</el-tag>
+        <el-tag v-else-if="detailData.contentType === 4" type="primary">视频</el-tag>
+        <el-tag v-else-if="detailData.contentType === 5" type="danger">图文混合</el-tag>
+      </el-descriptions-item>
+      <el-descriptions-item label="状态">
+        <el-tag v-if="detailData.status === 1" type="success">正常</el-tag>
+        <el-tag v-else-if="detailData.status === 2" type="danger">删除</el-tag>
+        <el-tag v-else-if="detailData.status === 3" type="warning">审核中</el-tag>
+      </el-descriptions-item>
+      <el-descriptions-item label="浏览数">{{ detailData.viewCount || 0 }}</el-descriptions-item>
+      <el-descriptions-item label="点赞数">{{ detailData.likeCount || 0 }}</el-descriptions-item>
+      <el-descriptions-item label="评论数">{{ detailData.commentCount || 0 }}</el-descriptions-item>
+      <el-descriptions-item label="收藏数">{{ detailData.collectCount || 0 }}</el-descriptions-item>
+      <el-descriptions-item label="是否置顶">
+        <el-tag v-if="detailData.isTop === 1" type="success">是</el-tag>
+        <el-tag v-else type="info">否</el-tag>
+      </el-descriptions-item>
+      <el-descriptions-item label="是否精华">
+        <el-tag v-if="detailData.isEssence === 1" type="warning">是</el-tag>
+        <el-tag v-else type="info">否</el-tag>
+      </el-descriptions-item>
+      <el-descriptions-item label="标签">{{ detailData.tags || '-' }}</el-descriptions-item>
+      <el-descriptions-item label="创建时间" :span="2">
+        {{ formatDate(detailData.createTime) }}
+      </el-descriptions-item>
+      <el-descriptions-item label="内容" :span="2">
+        <div class="max-h-200px overflow-auto">
+          <pre class="whitespace-pre-wrap">{{ detailData.content }}</pre>
+        </div>
+      </el-descriptions-item>
+      <el-descriptions-item label="媒体内容" :span="2" v-if="detailData.mediaList && detailData.mediaList.length > 0">
+        <div class="flex flex-wrap gap-4">
+          <div
+            v-for="(media, index) in detailData.mediaList"
+            :key="index"
+            class="relative"
+          >
+            <!-- 图片 -->
+            <div v-if="media.mediaType === 1" class="relative">
+              <el-image
+                :src="media.mediaUrl"
+                :preview-src-list="detailData.mediaList.filter(m => m.mediaType === 1).map(m => m.mediaUrl)"
+                :initial-index="detailData.mediaList.filter(m => m.mediaType === 1).findIndex(m => m.mediaUrl === media.mediaUrl)"
+                fit="cover"
+                class="w-200px h-200px border border-gray-200 rounded cursor-pointer"
+                :preview-teleported="true"
+              />
+              <div class="text-xs text-gray-500 mt-1 text-center">
+                图片 {{ index + 1 }}
+                <span v-if="media.fileSize">({{ formatFileSize(media.fileSize) }})</span>
+              </div>
+            </div>
+            <!-- 视频 -->
+            <div v-else-if="media.mediaType === 2" class="relative">
+              <div class="relative w-200px h-200px border border-gray-200 rounded overflow-hidden">
+                <img
+                  v-if="media.coverUrl"
+                  :src="media.coverUrl"
+                  class="w-full h-full object-cover"
+                  alt="视频封面"
+                />
+                <div v-else class="w-full h-full bg-gray-100 flex items-center justify-center">
+                  <Icon icon="ep:video-play" class="text-4xl text-gray-400" />
+                </div>
+                <div class="absolute inset-0 flex items-center justify-center bg-black bg-opacity-30">
+                  <Icon icon="ep:video-play" class="text-4xl text-white" />
+                </div>
+                <div v-if="media.duration" class="absolute bottom-2 right-2 bg-black bg-opacity-60 text-white text-xs px-2 py-1 rounded">
+                  {{ formatDuration(media.duration) }}
+                </div>
+              </div>
+              <div class="text-xs text-gray-500 mt-1 text-center">
+                视频 {{ index + 1 }}
+                <span v-if="media.fileSize">({{ formatFileSize(media.fileSize) }})</span>
+              </div>
+              <el-link
+                :href="media.mediaUrl"
+                target="_blank"
+                type="primary"
+                class="text-xs mt-1 block text-center"
+              >
+                查看视频
+              </el-link>
+            </div>
+          </div>
+        </div>
+      </el-descriptions-item>
+    </el-descriptions>
+  </Dialog>
+
+  <!-- 状态修改弹窗 -->
+  <Dialog v-model="statusFormVisible" title="修改帖子状态" width="500px">
+    <el-form
+      ref="statusFormRef"
+      :model="statusFormData"
+      :rules="statusFormRules"
+      label-width="100px"
+    >
+      <el-form-item label="状态" prop="status">
+        <el-radio-group v-model="statusFormData.status">
+          <el-radio :value="1" border>正常</el-radio>
+          <el-radio :value="2" border>删除</el-radio>
+          <el-radio :value="3" border>审核中</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="是否置顶" prop="isTop">
+        <el-radio-group v-model="statusFormData.isTop">
+          <el-radio :value="1" border>是</el-radio>
+          <el-radio :value="0" border>否</el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="是否精华" prop="isEssence">
+        <el-radio-group v-model="statusFormData.isEssence">
+          <el-radio :value="1" border>是</el-radio>
+          <el-radio :value="0" border>否</el-radio>
+        </el-radio-group>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button :disabled="statusFormLoading" type="primary" @click="submitStatusForm">确 定</el-button>
+      <el-button @click="statusFormVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+
+<script setup lang="ts">
+import { dateFormatter } from '@/utils/formatTime'
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import * as PostApi from '@/api/community/post'
+import { checkPermi } from '@/utils/permission'
+import { formatDate } from '@/utils/formatTime'
+
+defineOptions({ name: 'CommunityPost' })
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const loading = ref(true) // 列表的加载中
+const list = ref([]) // 列表的数据
+const total = ref(0) // 列表的总页数
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  userId: undefined,
+  title: undefined,
+  status: undefined,
+  isTop: undefined,
+  isEssence: undefined
+})
+const queryFormRef = ref() // 搜索的表单
+const checkedIds = ref<number[]>([]) // 选中的列表
+
+// 详情相关
+const detailVisible = ref(false) // 详情弹窗
+const detailData = ref<PostApi.PostVO>() // 详情数据
+
+// 状态表单相关
+const statusFormVisible = ref(false) // 状态表单弹窗
+const statusFormLoading = ref(false) // 状态表单的加载中
+const statusFormData = ref<PostApi.PostUpdateStatusReqVO>({
+  id: undefined,
+  status: 1,
+  isTop: 0,
+  isEssence: 0
+})
+const statusFormRef = ref() // 状态表单 Ref
+const statusFormRules = reactive({
+  status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
+})
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await PostApi.getPostPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 打开详情 */
+const openDetail = async (id: number) => {
+  detailVisible.value = true
+  try {
+    detailData.value = await PostApi.getPost(id)
+  } catch (error) {
+    console.error('获取帖子详情失败', error)
+  }
+}
+
+/** 打开状态表单 */
+const openStatusForm = (row: any) => {
+  statusFormVisible.value = true
+  statusFormData.value = {
+    id: row.id,
+    status: row.status,
+    isTop: row.isTop,
+    isEssence: row.isEssence
+  }
+}
+
+/** 提交状态表单 */
+const submitStatusForm = async () => {
+  if (!statusFormRef.value) return
+  const valid = await statusFormRef.value.validate()
+  if (!valid) return
+  statusFormLoading.value = true
+  try {
+    await PostApi.updatePostStatus(statusFormData.value)
+    message.success('更新成功')
+    statusFormVisible.value = false
+    await getList()
+  } finally {
+    statusFormLoading.value = false
+  }
+}
+
+/** 置顶状态修改 */
+const handleTopChange = async (row: any) => {
+  try {
+    await PostApi.updatePostStatus({
+      id: row.id,
+      isTop: row.isTop
+    })
+    message.success('更新成功')
+  } catch (error) {
+    // 取消操作后,进行回退
+    row.isTop = row.isTop === 1 ? 0 : 1
+  }
+}
+
+/** 精华状态修改 */
+const handleEssenceChange = async (row: any) => {
+  try {
+    await PostApi.updatePostStatus({
+      id: row.id,
+      isEssence: row.isEssence
+    })
+    message.success('更新成功')
+  } catch (error) {
+    // 取消操作后,进行回退
+    row.isEssence = row.isEssence === 1 ? 0 : 1
+  }
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await PostApi.deletePost(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 批量删除按钮操作 */
+const handleDeleteBatch = async () => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await PostApi.deletePostList(checkedIds.value)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+    checkedIds.value = []
+  } catch {}
+}
+
+/** 表格选中行变化 */
+const handleRowCheckboxChange = (selection: any[]) => {
+  checkedIds.value = selection.map((item) => item.id)
+}
+
+/** 格式化文件大小 */
+const formatFileSize = (bytes: number): string => {
+  if (!bytes || bytes === 0) return '0 B'
+  const k = 1024
+  const sizes = ['B', 'KB', 'MB', 'GB']
+  const i = Math.floor(Math.log(bytes) / Math.log(k))
+  return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]
+}
+
+/** 格式化视频时长 */
+const formatDuration = (seconds: number): string => {
+  if (!seconds) return '00:00'
+  const mins = Math.floor(seconds / 60)
+  const secs = seconds % 60
+  return `${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`
+}
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
+</script>
+

+ 142 - 0
src/views/community/sensitive/SensitiveWordForm.vue

@@ -0,0 +1,142 @@
+<template>
+  <Dialog v-model="dialogVisible" :title="dialogTitle" width="600px">
+    <el-form
+      ref="formRef"
+      v-loading="formLoading"
+      :model="formData"
+      :rules="formRules"
+      label-width="100px"
+    >
+      <el-form-item label="敏感词" prop="word">
+        <el-input
+          v-model="formData.word"
+          placeholder="请输入敏感词"
+          clearable
+        />
+      </el-form-item>
+      <el-form-item label="类型" prop="type">
+        <el-radio-group v-model="formData.type" @change="handleTypeChange">
+          <el-radio :value="1" border>禁用词</el-radio>
+          <el-radio :value="2" border>替换词</el-radio>
+        </el-radio-group>
+        <div class="text-xs text-gray-500 mt-1">
+          <div>禁用词:包含该词的帖子/评论将无法发布</div>
+          <div>替换词:包含该词的帖子/评论会自动替换为指定词</div>
+        </div>
+      </el-form-item>
+      <el-form-item
+        v-if="formData.type === 2"
+        label="替换词"
+        prop="replaceWord"
+      >
+        <el-input
+          v-model="formData.replaceWord"
+          placeholder="请输入替换词,留空则替换为 ***"
+          clearable
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-radio-group v-model="formData.status">
+          <el-radio :value="1" border>启用</el-radio>
+          <el-radio :value="2" border>禁用</el-radio>
+        </el-radio-group>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+
+<script setup lang="ts">
+import * as SensitiveWordApi from '@/api/community/sensitive'
+import { SensitiveWordSaveReqVO } from '@/api/community/sensitive'
+
+defineOptions({ name: 'CommunitySensitiveWordForm' })
+
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const dialogTitle = ref('') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formType = ref('') // 表单的类型:create - 新增;update - 修改
+const formData = ref<SensitiveWordSaveReqVO>({
+  id: undefined,
+  word: '',
+  type: 1,
+  replaceWord: '',
+  status: 1
+})
+const formRules = reactive({
+  word: [{ required: true, message: '敏感词不能为空', trigger: 'blur' }],
+  type: [{ required: true, message: '类型不能为空', trigger: 'change' }],
+  status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
+})
+const formRef = ref() // 表单 Ref
+
+/** 类型变化 */
+const handleTypeChange = () => {
+  if (formData.value.type === 1) {
+    formData.value.replaceWord = ''
+  }
+}
+
+/** 打开弹窗 */
+const open = async (type: string, id?: number) => {
+  dialogVisible.value = true
+  dialogTitle.value = t('action.' + type)
+  formType.value = type
+  resetForm()
+  // 修改时,设置数据
+  if (id) {
+    formLoading.value = true
+    try {
+      formData.value = await SensitiveWordApi.getSensitiveWord(id)
+    } finally {
+      formLoading.value = false
+    }
+  }
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = formData.value as unknown as SensitiveWordSaveReqVO
+    if (formType.value === 'create') {
+      await SensitiveWordApi.createSensitiveWord(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await SensitiveWordApi.updateSensitiveWord(data)
+      message.success(t('common.updateSuccess'))
+    }
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: undefined,
+    word: '',
+    type: 1,
+    replaceWord: '',
+    status: 1
+  }
+  formRef.value?.resetFields()
+}
+</script>
+

+ 241 - 0
src/views/community/sensitive/index.vue

@@ -0,0 +1,241 @@
+<template>
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form
+      ref="queryFormRef"
+      :inline="true"
+      :model="queryParams"
+      class="-mb-15px"
+      label-width="68px"
+    >
+      <el-form-item label="敏感词" prop="word">
+        <el-input
+          v-model="queryParams.word"
+          class="!w-240px"
+          clearable
+          placeholder="请输入敏感词"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="类型" prop="type">
+        <el-select
+          v-model="queryParams.type"
+          class="!w-240px"
+          clearable
+          placeholder="请选择类型"
+        >
+          <el-option label="禁用词" :value="1" />
+          <el-option label="替换词" :value="2" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select
+          v-model="queryParams.status"
+          class="!w-240px"
+          clearable
+          placeholder="请选择状态"
+        >
+          <el-option label="启用" :value="1" />
+          <el-option label="禁用" :value="2" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery">
+          <Icon class="mr-5px" icon="ep:search" />
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon class="mr-5px" icon="ep:refresh" />
+          重置
+        </el-button>
+        <el-button
+          v-hasPermi="['community:sensitive-word:create']"
+          type="primary"
+          @click="openForm('create')"
+        >
+          <Icon class="mr-5px" icon="ep:plus" />
+          新增
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table
+      v-loading="loading"
+      :data="list"
+    >
+      <el-table-column label="敏感词ID" align="center" prop="id" width="80" />
+      <el-table-column
+        label="敏感词"
+        align="left"
+        prop="word"
+        :show-overflow-tooltip="true"
+        min-width="200"
+      />
+      <el-table-column label="类型" align="center" prop="type" width="100">
+        <template #default="scope">
+          <el-tag v-if="scope.row.type === 1" type="danger">禁用词</el-tag>
+          <el-tag v-else-if="scope.row.type === 2" type="warning">替换词</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="替换词"
+        align="left"
+        prop="replaceWord"
+        :show-overflow-tooltip="true"
+        min-width="150"
+      >
+        <template #default="scope">
+          <span v-if="scope.row.type === 2">{{ scope.row.replaceWord || '***' }}</span>
+          <span v-else class="text-gray-400">-</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="status" width="100">
+        <template #default="scope">
+          <el-switch
+            v-model="scope.row.status"
+            :active-value="1"
+            :inactive-value="2"
+            @change="handleStatusChange(scope.row)"
+            :disabled="!checkPermi(['community:sensitive-word:update'])"
+          />
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        :formatter="dateFormatter"
+        width="180"
+      />
+      <el-table-column label="操作" align="center" width="200" fixed="right">
+        <template #default="scope">
+          <el-button
+            v-hasPermi="['community:sensitive-word:update']"
+            link
+            type="primary"
+            @click="openForm('update', scope.row.id)"
+          >
+            编辑
+          </el-button>
+          <el-button
+            v-hasPermi="['community:sensitive-word:delete']"
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <SensitiveWordForm ref="formRef" @success="getList" />
+</template>
+
+<script setup lang="ts">
+import { dateFormatter } from '@/utils/formatTime'
+import * as SensitiveWordApi from '@/api/community/sensitive'
+import SensitiveWordForm from './SensitiveWordForm.vue'
+import { checkPermi } from '@/utils/permission'
+
+defineOptions({ name: 'CommunitySensitiveWord' })
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const loading = ref(true) // 列表的加载中
+const list = ref([]) // 列表的数据
+const total = ref(0) // 列表的总页数
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  word: undefined,
+  type: undefined,
+  status: undefined
+})
+const queryFormRef = ref() // 搜索的表单
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await SensitiveWordApi.getSensitiveWordPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id)
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await SensitiveWordApi.deleteSensitiveWord(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 状态修改 */
+const handleStatusChange = async (row: any) => {
+  try {
+    // 修改状态的二次确认
+    const text = row.status === 1 ? '启用' : '禁用'
+    await message.confirm('确认要"' + text + '""' + row.word + '"敏感词吗?')
+    // 发起修改状态
+    await SensitiveWordApi.updateSensitiveWord({
+      id: row.id,
+      word: row.word,
+      type: row.type,
+      replaceWord: row.replaceWord,
+      status: row.status
+    })
+    // 刷新列表
+    await getList()
+    message.success(text + '成功')
+  } catch {
+    // 取消操作后,进行回退
+    row.status = row.status === 1 ? 2 : 1
+  }
+}
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
+</script>
+

+ 133 - 0
src/views/community/topic/TopicForm.vue

@@ -0,0 +1,133 @@
+<template>
+  <Dialog v-model="dialogVisible" :title="dialogTitle" width="600px">
+    <el-form
+      ref="formRef"
+      v-loading="formLoading"
+      :model="formData"
+      :rules="formRules"
+      label-width="100px"
+    >
+      <el-form-item label="话题名称" prop="name">
+        <el-input
+          v-model="formData.name"
+          placeholder="请输入话题名称"
+          clearable
+        />
+      </el-form-item>
+      <el-form-item label="话题描述" prop="description">
+        <el-input
+          v-model="formData.description"
+          type="textarea"
+          :rows="3"
+          placeholder="请输入话题描述"
+          maxlength="200"
+          show-word-limit
+        />
+      </el-form-item>
+      <el-form-item label="排序" prop="sort">
+        <el-input-number
+          v-model="formData.sort"
+          :min="0"
+          :max="9999"
+          placeholder="请输入排序"
+          class="!w-full"
+          controls-position="right"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-radio-group v-model="formData.status">
+          <el-radio :value="1" border>正常</el-radio>
+          <el-radio :value="2" border>禁用</el-radio>
+        </el-radio-group>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+
+<script setup lang="ts">
+import * as TopicApi from '@/api/community/topic'
+import { TopicSaveReqVO } from '@/api/community/topic'
+
+defineOptions({ name: 'CommunityTopicForm' })
+
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const dialogTitle = ref('') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formType = ref('') // 表单的类型:create - 新增;update - 修改
+const formData = ref<TopicSaveReqVO>({
+  id: undefined,
+  name: '',
+  description: '',
+  sort: 0,
+  status: 1
+})
+const formRules = reactive({
+  name: [{ required: true, message: '话题名称不能为空', trigger: 'blur' }],
+  status: [{ required: true, message: '状态不能为空', trigger: 'change' }]
+})
+const formRef = ref() // 表单 Ref
+
+/** 打开弹窗 */
+const open = async (type: string, id?: number) => {
+  dialogVisible.value = true
+  dialogTitle.value = t('action.' + type)
+  formType.value = type
+  resetForm()
+  // 修改时,设置数据
+  if (id) {
+    formLoading.value = true
+    try {
+      formData.value = await TopicApi.getTopic(id)
+    } finally {
+      formLoading.value = false
+    }
+  }
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = formData.value as unknown as TopicSaveReqVO
+    if (formType.value === 'create') {
+      await TopicApi.createTopic(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await TopicApi.updateTopic(data)
+      message.success(t('common.updateSuccess'))
+    }
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: undefined,
+    name: '',
+    description: '',
+    sort: 0,
+    status: 1
+  }
+  formRef.value?.resetFields()
+}
+</script>
+

+ 224 - 0
src/views/community/topic/index.vue

@@ -0,0 +1,224 @@
+<template>
+  <ContentWrap>
+    <!-- 搜索工作栏 -->
+    <el-form
+      ref="queryFormRef"
+      :inline="true"
+      :model="queryParams"
+      class="-mb-15px"
+      label-width="68px"
+    >
+      <el-form-item label="话题名称" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          class="!w-240px"
+          clearable
+          placeholder="请输入话题名称"
+          @keyup.enter="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select
+          v-model="queryParams.status"
+          class="!w-240px"
+          clearable
+          placeholder="请选择状态"
+        >
+          <el-option label="正常" :value="1" />
+          <el-option label="禁用" :value="2" />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery">
+          <Icon class="mr-5px" icon="ep:search" />
+          搜索
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon class="mr-5px" icon="ep:refresh" />
+          重置
+        </el-button>
+        <el-button
+          v-hasPermi="['community:topic:create']"
+          type="primary"
+          @click="openForm('create')"
+        >
+          <Icon class="mr-5px" icon="ep:plus" />
+          新增
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table
+      v-loading="loading"
+      :data="list"
+    >
+      <el-table-column label="话题ID" align="center" prop="id" width="80" />
+      <el-table-column
+        label="话题名称"
+        align="left"
+        prop="name"
+        :show-overflow-tooltip="true"
+        min-width="200"
+      />
+      <el-table-column
+        label="话题描述"
+        align="left"
+        prop="description"
+        :show-overflow-tooltip="true"
+        min-width="300"
+      />
+      <el-table-column label="帖子数" align="center" prop="postCount" width="100">
+        <template #default="scope">
+          <el-tag type="info">{{ scope.row.postCount || 0 }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="排序" align="center" prop="sort" width="100" />
+      <el-table-column label="状态" align="center" prop="status" width="100">
+        <template #default="scope">
+          <el-switch
+            v-model="scope.row.status"
+            :active-value="1"
+            :inactive-value="2"
+            @change="handleStatusChange(scope.row)"
+            :disabled="!checkPermi(['community:topic:update'])"
+          />
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        :formatter="dateFormatter"
+        width="180"
+      />
+      <el-table-column label="操作" align="center" width="200" fixed="right">
+        <template #default="scope">
+          <el-button
+            v-hasPermi="['community:topic:update']"
+            link
+            type="primary"
+            @click="openForm('update', scope.row.id)"
+          >
+            编辑
+          </el-button>
+          <el-button
+            v-hasPermi="['community:topic:delete']"
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <TopicForm ref="formRef" @success="getList" />
+</template>
+
+<script setup lang="ts">
+import { dateFormatter } from '@/utils/formatTime'
+import * as TopicApi from '@/api/community/topic'
+import TopicForm from './TopicForm.vue'
+import { checkPermi } from '@/utils/permission'
+
+defineOptions({ name: 'CommunityTopic' })
+
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+
+const loading = ref(true) // 列表的加载中
+const list = ref([]) // 列表的数据
+const total = ref(0) // 列表的总页数
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  name: undefined,
+  status: undefined
+})
+const queryFormRef = ref() // 搜索的表单
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await TopicApi.getTopicPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id)
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await TopicApi.deleteTopic(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 状态修改 */
+const handleStatusChange = async (row: any) => {
+  try {
+    // 修改状态的二次确认
+    const text = row.status === 1 ? '启用' : '禁用'
+    await message.confirm('确认要"' + text + '""' + row.name + '"话题吗?')
+    // 发起修改状态
+    await TopicApi.updateTopic({
+      id: row.id,
+      name: row.name,
+      description: row.description,
+      sort: row.sort,
+      status: row.status
+    })
+    // 刷新列表
+    await getList()
+    message.success(text + '成功')
+  } catch {
+    // 取消操作后,进行回退
+    row.status = row.status === 1 ? 2 : 1
+  }
+}
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
+</script>
+

+ 1 - 1
src/views/crm/backlog/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="【通用】跟进记录、待办事项" url="https://doc.iocoder.cn/crm/follow-up/" />
+  <doc-alert title="【通用】跟进记录、待办事项" url="https://doc.xindazhou.com/crm/follow-up/" />
 
   <el-row :gutter="20">
     <el-col :span="4" class="min-w-[200px]">

+ 2 - 2
src/views/crm/business/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <doc-alert title="【商机】商机管理、商机状态" url="https://doc.iocoder.cn/crm/business/" />
-  <doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
+  <doc-alert title="【商机】商机管理、商机状态" url="https://doc.xindazhou.com/crm/business/" />
+  <doc-alert title="【通用】数据权限" url="https://doc.xindazhou.com/crm/permission/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 2 - 2
src/views/crm/business/status/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <doc-alert title="【商机】商机管理、商机状态" url="https://doc.iocoder.cn/crm/business/" />
-  <doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
+  <doc-alert title="【商机】商机管理、商机状态" url="https://doc.xindazhou.com/crm/business/" />
+  <doc-alert title="【通用】数据权限" url="https://doc.xindazhou.com/crm/permission/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 2 - 2
src/views/crm/clue/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <doc-alert title="【线索】线索管理" url="https://doc.iocoder.cn/crm/clue/" />
-  <doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
+  <doc-alert title="【线索】线索管理" url="https://doc.xindazhou.com/crm/clue/" />
+  <doc-alert title="【通用】数据权限" url="https://doc.xindazhou.com/crm/permission/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 2 - 2
src/views/crm/contact/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" />
-  <doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
+  <doc-alert title="【客户】客户管理、公海客户" url="https://doc.xindazhou.com/crm/customer/" />
+  <doc-alert title="【通用】数据权限" url="https://doc.xindazhou.com/crm/permission/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 2 - 2
src/views/crm/contract/config/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <doc-alert title="【合同】合同管理、合同提醒" url="https://doc.iocoder.cn/crm/contract/" />
-  <doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
+  <doc-alert title="【合同】合同管理、合同提醒" url="https://doc.xindazhou.com/crm/contract/" />
+  <doc-alert title="【通用】数据权限" url="https://doc.xindazhou.com/crm/permission/" />
 
   <ContentWrap>
     <el-form

+ 2 - 2
src/views/crm/contract/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <doc-alert title="【合同】合同管理、合同提醒" url="https://doc.iocoder.cn/crm/contract/" />
-  <doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
+  <doc-alert title="【合同】合同管理、合同提醒" url="https://doc.xindazhou.com/crm/contract/" />
+  <doc-alert title="【通用】数据权限" url="https://doc.xindazhou.com/crm/permission/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 2 - 2
src/views/crm/customer/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" />
-  <doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
+  <doc-alert title="【客户】客户管理、公海客户" url="https://doc.xindazhou.com/crm/customer/" />
+  <doc-alert title="【通用】数据权限" url="https://doc.xindazhou.com/crm/permission/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 2 - 2
src/views/crm/customer/limitConfig/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" />
-  <doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
+  <doc-alert title="【客户】客户管理、公海客户" url="https://doc.xindazhou.com/crm/customer/" />
+  <doc-alert title="【通用】数据权限" url="https://doc.xindazhou.com/crm/permission/" />
 
   <!-- 列表 -->
   <ContentWrap>

+ 2 - 2
src/views/crm/customer/pool/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" />
-  <doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
+  <doc-alert title="【客户】客户管理、公海客户" url="https://doc.xindazhou.com/crm/customer/" />
+  <doc-alert title="【通用】数据权限" url="https://doc.xindazhou.com/crm/permission/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 2 - 2
src/views/crm/customer/poolConfig/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <doc-alert title="【客户】客户管理、公海客户" url="https://doc.iocoder.cn/crm/customer/" />
-  <doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
+  <doc-alert title="【客户】客户管理、公海客户" url="https://doc.xindazhou.com/crm/customer/" />
+  <doc-alert title="【通用】数据权限" url="https://doc.xindazhou.com/crm/permission/" />
 
   <ContentWrap>
     <el-form

+ 1 - 1
src/views/crm/product/category/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="【产品】产品管理、产品分类" url="https://doc.iocoder.cn/crm/product/" />
+  <doc-alert title="【产品】产品管理、产品分类" url="https://doc.xindazhou.com/crm/product/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/crm/product/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="【产品】产品管理、产品分类" url="https://doc.iocoder.cn/crm/product/" />
+  <doc-alert title="【产品】产品管理、产品分类" url="https://doc.xindazhou.com/crm/product/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 2 - 2
src/views/crm/receivable/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <doc-alert title="【回款】回款管理、回款计划" url="https://doc.iocoder.cn/crm/receivable/" />
-  <doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
+  <doc-alert title="【回款】回款管理、回款计划" url="https://doc.xindazhou.com/crm/receivable/" />
+  <doc-alert title="【通用】数据权限" url="https://doc.xindazhou.com/crm/permission/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 2 - 2
src/views/crm/receivable/plan/index.vue

@@ -1,6 +1,6 @@
 <template>
-  <doc-alert title="【回款】回款管理、回款计划" url="https://doc.iocoder.cn/crm/receivable/" />
-  <doc-alert title="【通用】数据权限" url="https://doc.iocoder.cn/crm/permission/" />
+  <doc-alert title="【回款】回款管理、回款计划" url="https://doc.xindazhou.com/crm/receivable/" />
+  <doc-alert title="【通用】数据权限" url="https://doc.xindazhou.com/crm/permission/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/erp/finance/account/index.vue

@@ -1,7 +1,7 @@
 <template>
   <doc-alert
     title="【财务】采购付款、销售收款"
-    url="https://doc.iocoder.cn/sale/finance-payment-receipt/"
+    url="https://doc.xindazhou.com/sale/finance-payment-receipt/"
   />
 
   <ContentWrap>

+ 1 - 1
src/views/erp/finance/payment/index.vue

@@ -1,7 +1,7 @@
 <template>
   <doc-alert
     title="【财务】采购付款、销售收款"
-    url="https://doc.iocoder.cn/sale/finance-payment-receipt/"
+    url="https://doc.xindazhou.com/sale/finance-payment-receipt/"
   />
 
   <ContentWrap>

+ 1 - 1
src/views/erp/finance/receipt/index.vue

@@ -1,7 +1,7 @@
 <template>
   <doc-alert
     title="【财务】采购付款、销售收款"
-    url="https://doc.iocoder.cn/sale/finance-payment-receipt/"
+    url="https://doc.xindazhou.com/sale/finance-payment-receipt/"
   />
 
   <ContentWrap>

+ 1 - 1
src/views/erp/home/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="ERP 手册(功能开启)" url="https://doc.iocoder.cn/erp/build/" />
+  <doc-alert title="ERP 手册(功能开启)" url="https://doc.xindazhou.com/erp/build/" />
 
   <div class="flex flex-col">
     <!-- 销售/采购的全局统计 -->

+ 1 - 1
src/views/erp/product/category/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="【产品】产品信息、分类、单位" url="https://doc.iocoder.cn/erp/product/" />
+  <doc-alert title="【产品】产品信息、分类、单位" url="https://doc.xindazhou.com/erp/product/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/erp/product/product/index.vue

@@ -1,6 +1,6 @@
 <!-- ERP 产品列表 -->
 <template>
-  <doc-alert title="【产品】产品信息、分类、单位" url="https://doc.iocoder.cn/erp/product/" />
+  <doc-alert title="【产品】产品信息、分类、单位" url="https://doc.xindazhou.com/erp/product/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/erp/product/unit/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="【产品】产品信息、分类、单位" url="https://doc.iocoder.cn/erp/product/" />
+  <doc-alert title="【产品】产品信息、分类、单位" url="https://doc.xindazhou.com/erp/product/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/erp/purchase/in/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="【采购】采购订单、入库、退货" url="https://doc.iocoder.cn/erp/purchase/" />
+  <doc-alert title="【采购】采购订单、入库、退货" url="https://doc.xindazhou.com/erp/purchase/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/erp/purchase/order/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="【采购】采购订单、入库、退货" url="https://doc.iocoder.cn/erp/purchase/" />
+  <doc-alert title="【采购】采购订单、入库、退货" url="https://doc.xindazhou.com/erp/purchase/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 1 - 1
src/views/erp/purchase/return/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <doc-alert title="【采购】采购订单、入库、退货" url="https://doc.iocoder.cn/erp/purchase/" />
+  <doc-alert title="【采购】采购订单、入库、退货" url="https://doc.xindazhou.com/erp/purchase/" />
 
   <ContentWrap>
     <!-- 搜索工作栏 -->

+ 0 - 0
src/views/erp/purchase/supplier/index.vue


Some files were not shown because too many files changed in this diff