Compare commits
16 Commits
5eec2050a9
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| af6643e65e | |||
| dbccc1078a | |||
| 2292615a3b | |||
|
|
1bf4238609 | ||
|
|
63f58fa84d | ||
|
|
463a9fb509 | ||
|
|
783f510229 | ||
|
|
92c433a6aa | ||
|
|
599e1b342a | ||
|
|
2e2a147815 | ||
|
|
67b39cfe8a | ||
|
|
cdc0cbc431 | ||
|
|
d21031ecf4 | ||
|
|
c4babbecf0 | ||
|
|
45a5c9bc8e | ||
|
|
06ea290306 |
@@ -1,5 +0,0 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
|
||||
"changelog": [
|
||||
"@changesets/changelog-github",
|
||||
{ "repo": "vbenjs/vue-vben-admin" }
|
||||
],
|
||||
"commit": false,
|
||||
"fixed": [["@vben-core/*", "@vben/*"]],
|
||||
"snapshot": {
|
||||
"prereleaseTemplate": "{tag}-{datetime}"
|
||||
},
|
||||
"privatePackages": { "version": true, "tag": true },
|
||||
"linked": [],
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": []
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
[core]
|
||||
ignorecase = false
|
||||
@@ -1,65 +0,0 @@
|
||||
name: Bug 反馈
|
||||
description: 当你在代码中发现了一个 Bug,导致应用崩溃或抛出异常,或者有一个组件存在问题,或者某些地方看起来不对劲。
|
||||
title: '[Bug]: '
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢对项目的支持与关注。在提出问题之前,请确保你已查看相关开发或使用文档:
|
||||
- https://doc.iocoder.cn/
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: 分支
|
||||
description: 你当前正在使用我们软件的哪个分支?
|
||||
options:
|
||||
- master (默认)
|
||||
- dev (开发分支)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: 版本
|
||||
description: 你当前正在使用我们软件的哪个版本?
|
||||
options:
|
||||
- antd-design-vue
|
||||
- element-plus
|
||||
- naiveui
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 这个问题是否已经存在?
|
||||
options:
|
||||
- label: 我已经搜索过现有的问题 (https://gitee.com/yudaocode/yudao-ui-admin-vben/issues)
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 如何复现
|
||||
description: 请详细告诉我们如何复现你遇到的问题,如涉及代码,可提供一个最小代码示例,并使用反引号```附上它
|
||||
placeholder: |
|
||||
1. ...
|
||||
2. ...
|
||||
3. ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 预期结果
|
||||
description: 请告诉我们你预期会发生什么。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 实际结果
|
||||
description: 请告诉我们实际发生了什么。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: 截图或视频
|
||||
description: 如果可以的话,上传任何关于 bug 的截图。
|
||||
value: |
|
||||
[在这里上传图片]
|
||||
@@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 项目开发文档
|
||||
url: https://doc.iocoder.cn/
|
||||
about: 提供项目启动、开发的相关文档
|
||||
@@ -1,43 +0,0 @@
|
||||
name: 功能建议
|
||||
description: 对本项目提出一个功能建议
|
||||
title: '[功能建议]: '
|
||||
labels: [enhancement]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
感谢提出功能建议,我们将仔细考虑!
|
||||
- type: textarea
|
||||
id: related-problem
|
||||
attributes:
|
||||
label: 你的功能建议是否和某个问题相关?
|
||||
description: 清晰并简洁地描述问题是什么,例如,当我...时,我总是感到困扰。
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: desired-solution
|
||||
attributes:
|
||||
label: 你希望看到什么解决方案?
|
||||
description: 清晰并简洁地描述你希望发生的事情。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: 你考虑过哪些替代方案?
|
||||
description: 清晰并简洁地描述你考虑过的任何替代解决方案或功能。
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: 你有其他上下文或截图吗?
|
||||
description: 在此处添加有关功能请求的任何其他上下文或截图。
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: 意向参与贡献
|
||||
options:
|
||||
- label: 我有意向参与具体功能的开发实现并将代码贡献回到上游社区
|
||||
required: false
|
||||
@@ -1,6 +0,0 @@
|
||||
ports:
|
||||
- port: 5555
|
||||
onOpen: open-preview
|
||||
tasks:
|
||||
- init: npm i -g corepack && pnpm install
|
||||
command: pnpm run dev:play
|
||||
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 348 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 238 KiB |
|
Before Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 495 KiB |
|
Before Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 126 KiB |
@@ -1 +0,0 @@
|
||||
22.1.0
|
||||
289
README.md
@@ -1,237 +1,108 @@
|
||||
# 严肃声明:现在、未来都不会有商业版本,所有代码全部开源
|
||||
# aiotagro-ui-admin-vben - 企业级管理系统框架
|
||||
|
||||
**「我喜欢写代码,乐此不疲」**
|
||||
**「我喜欢做开源,以此为乐」**
|
||||
## 项目简介
|
||||
|
||||
我 🐶 在上海艰苦奋斗,早中晚在 top3 大厂认真搬砖,夜里为开源做贡献。
|
||||
aiotagro-ui-admin-vben 是基于 Vue3 + TypeScript + Vite 的现代化企业级管理系统框架,采用 monorepo 架构,提供完整的前端解决方案。
|
||||
|
||||
如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。
|
||||
## 核心特性
|
||||
|
||||
## 🐶 新手必读
|
||||
- 🚀 **现代化技术栈**:Vue3 + TypeScript + Vite + Pinia
|
||||
- 📦 **Monorepo 架构**:基于 pnpm + turbo 的高效开发体验
|
||||
- 🎨 **多主题支持**:Ant Design、Element Plus、Naive UI 三套 UI 组件库
|
||||
- 🔧 **完整工具链**:ESLint、Prettier、StyleLint、TypeScript 检查
|
||||
- 📱 **响应式设计**:完美适配桌面端和移动端
|
||||
- 🌐 **国际化支持**:内置多语言解决方案
|
||||
- 🔐 **权限管理**:完整的 RBAC 权限控制系统
|
||||
|
||||
- nodejs > 20.10.0 && pnpm > 10.14.0 (强制使用pnpm)
|
||||
- 演示地址【Vue3 + element-plus】:<http://dashboard-vue3.yudao.iocoder.cn>
|
||||
- 演示地址【Vue3 + vben5(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/>
|
||||
## 项目结构
|
||||
|
||||
## 🐯 平台简介
|
||||
```
|
||||
aiotagro-ui-admin-vben/
|
||||
├── apps/ # 应用目录
|
||||
│ ├── web-antd/ # Ant Design 版本
|
||||
│ ├── web-ele/ # Element Plus 版本
|
||||
│ ├── web-naive/ # Naive UI 版本
|
||||
│ └── backend-mock/ # 后端模拟服务
|
||||
├── packages/ # 共享包目录
|
||||
│ ├── @core/ # 核心功能包
|
||||
│ ├── constants/ # 常量定义
|
||||
│ ├── effects/ # 副作用管理
|
||||
│ ├── icons/ # 图标库
|
||||
│ ├── locales/ # 国际化资源
|
||||
│ ├── stores/ # 状态管理
|
||||
│ ├── styles/ # 样式文件
|
||||
│ ├── types/ # 类型定义
|
||||
│ └── utils/ # 工具函数
|
||||
├── internal/ # 内部配置
|
||||
│ ├── lint-configs/ # 代码规范配置
|
||||
│ ├── tailwind-config/ # Tailwind 配置
|
||||
│ ├── tsconfig/ # TypeScript 配置
|
||||
│ └── vite-config/ # Vite 配置
|
||||
├── docs/ # 项目文档
|
||||
├── playground/ # 开发测试环境
|
||||
└── scripts/ # 脚本工具
|
||||
```
|
||||
|
||||
**芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。
|
||||
## 快速开始
|
||||
|
||||
- 采用最新 [vue-vben-admin](https://github.com/vbenjs/vue-vben-admin) v5 实现
|
||||
- 支持 [Ant Design Vue](https://www.antdv.com/) | [Element Plus](https://element-plus.org/zh-CN/) | [Naive UI](https://www.naiveui.com/) 多种免费开源的中后台模版,具备如下特性:
|
||||
### 环境要求
|
||||
|
||||

|
||||
- Node.js >= 18.0.0
|
||||
- pnpm >= 8.0.0
|
||||
|
||||
- **最新技术栈**:使用 Vue3、Vite6 等前端前沿技术开发
|
||||
- **TypeScript**: 应用程序级 JavaScript 的语言
|
||||
- **主题**: 提供多套主题色彩,可配置自定义主题
|
||||
- **国际化**:内置完善的国际化方案
|
||||
- **权限**:内置完善的动态路由权限生成方案
|
||||
- **组件**:二次封装了多个常用的组件
|
||||
- **示例**:内置丰富的示例
|
||||
### 安装依赖
|
||||
|
||||
## 外包项目请联系【非项目需求请勿扫码,非客服,不解答项目问题】
|
||||
```bash
|
||||
# 安装 pnpm
|
||||
npm install -g pnpm
|
||||
|
||||

|
||||
# 安装项目依赖
|
||||
pnpm install
|
||||
```
|
||||
|
||||
## 技术栈
|
||||
### 开发运行
|
||||
|
||||
| 框架 | 说明 | 版本 |
|
||||
| --- | --- | --- |
|
||||
| [Vue](https://staging-cn.vuejs.org/) | vue框架 | 3.5.17 |
|
||||
| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 7.1.2 |
|
||||
| [Ant Design Vue](https://www.antdv.com/) | Ant Design Vue | 4.2.6 |
|
||||
| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.10.2 |
|
||||
| [Naive UI](https://www.naiveui.com/) | Naive UI | 2.42.0 |
|
||||
| [TypeScript](https://www.typescriptlang.org/docs/) | JavaScript 超集 | 5.8.3 |
|
||||
| [pinia](https://pinia.vuejs.org/) | Vue 存储库替代 vuex5 | 3.0.3 |
|
||||
| [vueuse](https://vueuse.org/) | 常用工具集 | 13.4.0 |
|
||||
| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 11.1.7 |
|
||||
| [vue-router](https://router.vuejs.org/) | Vue 路由 | 4.5.1 |
|
||||
| [Tailwind CSS](https://tailwindcss.com/) | 原子 CSS | 3.4.17 |
|
||||
| [Iconify](https://icon-sets.iconify.design/) | 在线图标库 | 2.2.354 |
|
||||
| [TinyMCE](https://www.tiny.cloud/) | 富文本编辑器 | 6.1.0 |
|
||||
| [Echarts](https://echarts.apache.org/) | 图表库 | 5.6.0 |
|
||||
| [axios](https://axios-http.com/) | http客户端 | 1.10.0 |
|
||||
| [dayjs](https://day.js.org/) | 日期处理库 | 1.11.13 |
|
||||
| [vee-validate](https://vee-validate.logaretm.com/) | 表单验证 | 4.15.1 |
|
||||
| [zod](https://zod.dev/) | 数据验证 | 3.25.67 |
|
||||
```bash
|
||||
# 启动 Ant Design 版本
|
||||
pnpm dev:antd
|
||||
|
||||
## 🔥 后端架构
|
||||
# 启动 Element Plus 版本
|
||||
pnpm dev:ele
|
||||
|
||||
支持 Spring Boot、Spring Cloud 两种架构:
|
||||
# 启动 Naive UI 版本
|
||||
pnpm dev:naive
|
||||
```
|
||||
|
||||
① Spring Boot 单体架构:<https://doc.iocoder.cn>
|
||||
### 构建项目
|
||||
|
||||

|
||||
```bash
|
||||
# 构建所有应用
|
||||
pnpm build
|
||||
|
||||
② Spring Cloud 微服务架构:<https://cloud.iocoder.cn>
|
||||
# 构建指定应用
|
||||
pnpm build:antd
|
||||
pnpm build:ele
|
||||
pnpm build:naive
|
||||
```
|
||||
|
||||

|
||||
## 文档目录
|
||||
|
||||
## 内置功能
|
||||
详细文档请查看 [docs/](./docs/) 目录:
|
||||
|
||||
系统内置多种多种业务功能,可以用于快速你的业务系统:
|
||||
- [📚 技术文档](./docs/) - 开发指南、API 文档
|
||||
- [👥 用户手册](./docs/user-guide/) - 系统使用说明
|
||||
- [🚀 部署指南](./docs/deployment/) - 生产环境部署
|
||||
- [🔧 运维手册](./docs/operations/) - 系统维护指南
|
||||
|
||||
系统内置多种多种业务功能,可以用于快速你的业务系统:
|
||||
## 贡献指南
|
||||
|
||||

|
||||
欢迎提交 Issue 和 Pull Request!请阅读 [贡献指南](./docs/contributing.md) 了解详细流程。
|
||||
|
||||
- 通用模块(必选):系统功能、基础设施
|
||||
- 通用模块(可选):工作流程、支付系统、数据报表、会员中心
|
||||
- 业务系统(按需):ERP 系统、CRM 系统、商城系统、微信公众号、AI 大模型
|
||||
## 许可证
|
||||
|
||||
### 系统功能
|
||||
本项目基于 MIT 许可证开源。
|
||||
|
||||
| | 功能 | 描述 |
|
||||
| --- | --- | --- |
|
||||
| | 用户管理 | 用户是系统操作者,该功能主要完成系统用户配置 |
|
||||
| ⭐️ | 在线用户 | 当前系统中活跃用户状态监控,支持手动踢下线 |
|
||||
| | 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 |
|
||||
| | 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等,本地缓存提供性能 |
|
||||
| | 部门管理 | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限 |
|
||||
| | 岗位管理 | 配置系统用户所属担任职务 |
|
||||
| 🚀 | 租户管理 | 配置系统租户,支持 SaaS 场景下的多租户功能 |
|
||||
| 🚀 | 租户套餐 | 配置租户套餐,自定每个租户的菜单、操作、按钮的权限 |
|
||||
| | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 |
|
||||
| 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 |
|
||||
| 🚀 | 邮件管理 | 邮箱账号、邮件模版、邮件发送日志,支持所有邮件平台 |
|
||||
| 🚀 | 站内信 | 系统内的消息通知,提供站内信模版、站内信消息 |
|
||||
| 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 |
|
||||
| ⭐️ | 登录日志 | 系统登录日志记录查询,包含登录异常 |
|
||||
| 🚀 | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务 |
|
||||
| | 通知公告 | 系统通知公告信息发布维护 |
|
||||
| 🚀 | 敏感词 | 配置系统敏感词,支持标签分组 |
|
||||
| 🚀 | 应用管理 | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 |
|
||||
| 🚀 | 地区管理 | 展示省份、城市、区镇等城市信息,支持 IP 对应城市 |
|
||||
## 联系方式
|
||||
|
||||

|
||||
|
||||
### 工作流程
|
||||
|
||||

|
||||
|
||||
基于 Flowable 构建,可支持信创(国产)数据库,满足中国特色流程操作:
|
||||
|
||||
| BPMN 设计器 | 钉钉/飞书设计器 |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|
||||
> 历经头部企业生产验证,工作流引擎须标配仿钉钉/飞书 + 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 实现单元测试,保证功能的正确性、代码的质量等 |
|
||||
|
||||

|
||||
|
||||
### 数据报表
|
||||
|
||||
| | 功能 | 描述 |
|
||||
| --- | ---------- | ------------------------------------ |
|
||||
| 🚀 | 报表设计器 | 支持数据报表、图形报表、打印设计等 |
|
||||
| 🚀 | 大屏设计器 | 拖拽生成数据大屏,内置几十种图表组件 |
|
||||
|
||||
### 微信公众号
|
||||
|
||||
| | 功能 | 描述 |
|
||||
| --- | --- | --- |
|
||||
| 🚀 | 账号管理 | 配置接入的微信公众号,可支持多个公众号 |
|
||||
| 🚀 | 数据统计 | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据 |
|
||||
| 🚀 | 粉丝管理 | 查看已关注、取关的粉丝列表,可对粉丝进行同步、打标签等操作 |
|
||||
| 🚀 | 消息管理 | 查看粉丝发送的消息列表,可主动回复粉丝消息 |
|
||||
| 🚀 | 自动回复 | 自动回复粉丝发送的消息,支持关注回复、消息回复、关键字回复 |
|
||||
| 🚀 | 标签管理 | 对公众号的标签进行创建、查询、修改、删除等操作 |
|
||||
| 🚀 | 菜单管理 | 自定义公众号的菜单,也可以从公众号同步菜单 |
|
||||
| 🚀 | 素材管理 | 管理公众号的图片、语音、视频等素材,支持在线播放语音、视频 |
|
||||
| 🚀 | 图文草稿箱 | 新增常用的图文素材到草稿箱,可发布到公众号 |
|
||||
| 🚀 | 图文发表记录 | 查看已发布成功的图文素材,支持删除操作 |
|
||||
|
||||
### 商城系统
|
||||
|
||||
演示地址:<https://doc.iocoder.cn/mall-preview/>
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### ERP 系统
|
||||
|
||||
演示地址:<https://doc.iocoder.cn/erp-preview/>
|
||||
|
||||

|
||||
|
||||
### CRM 系统
|
||||
|
||||
演示地址:<https://doc.iocoder.cn/crm-preview/>
|
||||
|
||||

|
||||
|
||||
### AI 大模型
|
||||
|
||||
演示地址:<https://doc.iocoder.cn/ai-preview/>
|
||||
|
||||

|
||||
|
||||

|
||||
- 项目主页:https://github.com/vbenjs/vue-vben-admin
|
||||
- 问题反馈:https://github.com/vbenjs/vue-vben-admin/issues
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
## Description
|
||||
|
||||
Vben Admin 数据 mock 服务,没有对接任何的数据库,所有数据都是模拟的,用于前端开发时提供数据支持。线上环境不再提供 mock 集成,可自行部署服务或者对接真实数据,由于 `mock.js` 等工具有一些限制,比如上传文件不行、无法模拟复杂的逻辑等,所以这里使用了真实的后端服务来实现。唯一麻烦的是本地需要同时启动后端服务和前端服务,但是这样可以更好的模拟真实环境。该服务不需要手动启动,已经集成在 vite 插件内,随应用一起启用。
|
||||
|
||||
Vben Admin 数据 mock 服务,没有对接任何的数据库,所有数据都是模拟的,用于前端开发时提供数据支持。线上环境不再提<EFBFBD><EFBFBD>?mock 集成,可自行部署服务或者对接真实数据,由于 `mock.js` 等工具有一些限制,比如上传文件不行、无法模拟复杂的逻辑等,所以这里使用了真实的后端服务来实现。唯一麻烦的是本地需要同时启动后端服务和前端服务,但是这样可以更好的模拟真实环境。该服务不需要手动启动,已经集成<EFBFBD><EFBFBD>?vite 插件内,随应用一起启用<EFBFBD><EFBFBD>?
|
||||
## Running the app
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 应用标题
|
||||
VITE_APP_TITLE=芋道管理系统
|
||||
VITE_APP_TITLE=AIOTAGRO管理系统
|
||||
|
||||
# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
|
||||
VITE_APP_NAMESPACE=yudao-vben-antd
|
||||
|
||||
@@ -4,7 +4,7 @@ VITE_PORT=5666
|
||||
VITE_BASE=/
|
||||
|
||||
# 请求路径
|
||||
VITE_BASE_URL=http://127.0.0.1:48080
|
||||
0VITE_BASE_URL=http://111.3.47.177:48080
|
||||
# 接口地址
|
||||
VITE_GLOB_API_URL=/admin-api
|
||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Software License Agreement
|
||||
|
||||
**TinyMCE** – [<https://github.com/tinymce/tinymce>](https://github.com/tinymce/tinymce)
|
||||
**TinyMCE** <EFBFBD><EFBFBD>?[<https://github.com/tinymce/tinymce>](https://github.com/tinymce/tinymce)
|
||||
Copyright (c) 2024, Ephox Corporation DBA Tiny Technologies, Inc.
|
||||
|
||||
Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
|
||||
|
||||
@@ -161,7 +161,7 @@ setupVbenVxeTable({
|
||||
});
|
||||
|
||||
// 表格配置项可以用 cellRender: { name: 'CellSwitch', props: { beforeChange: () => {} } },
|
||||
// add by 芋艿:from https://github.com/vbenjs/vue-vben-admin/blob/main/playground/src/adapter/vxe-table.ts#L97-L123
|
||||
// add by AIOTAGRO:from https://github.com/vbenjs/vue-vben-admin/blob/main/playground/src/adapter/vxe-table.ts#L97-L123
|
||||
vxeUI.renderer.add('CellSwitch', {
|
||||
renderTableDefault({ attrs, props }, { column, row }) {
|
||||
const loadingKey = `__loading_${column.field}`;
|
||||
@@ -193,7 +193,7 @@ setupVbenVxeTable({
|
||||
});
|
||||
|
||||
// 注册表格的操作按钮渲染器 cellRender: { name: 'CellOperation', options: ['edit', 'delete'] }
|
||||
// add by 芋艿:from https://github.com/vbenjs/vue-vben-admin/blob/main/playground/src/adapter/vxe-table.ts#L125-L255
|
||||
// add by AIOTAGRO:from https://github.com/vbenjs/vue-vben-admin/blob/main/playground/src/adapter/vxe-table.ts#L125-L255
|
||||
vxeUI.renderer.add('CellOperation', {
|
||||
renderTableDefault({ attrs, options, props }, { column, row }) {
|
||||
const defaultProps = { size: 'small', type: 'link', ...props };
|
||||
|
||||
@@ -2,7 +2,7 @@ import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace BpmModelApi {
|
||||
/** 用户信息 TODO 这个是不是可以抽取出来定义在公共模块 */
|
||||
// TODO @芋艿:一起看看。
|
||||
// TODO @AIOTAGRO:一起看看。
|
||||
export interface UserInfo {
|
||||
id: number;
|
||||
nickname: string;
|
||||
|
||||
@@ -11,7 +11,7 @@ import type { BpmModelApi } from '#/api/bpm/model';
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace BpmProcessInstanceApi {
|
||||
// TODO @芋艿:一些注释缺少或者不对;
|
||||
// TODO @AIOTAGRO:一些注释缺少或者不对;
|
||||
export interface Task {
|
||||
id: number;
|
||||
name: string;
|
||||
|
||||
@@ -59,7 +59,7 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
|
||||
}
|
||||
const resp = await refreshTokenApi(refreshToken);
|
||||
const newToken = resp?.data?.data?.accessToken;
|
||||
// add by 芋艿:这里一定要抛出 resp.data,从而触发 authenticateResponseInterceptor 中,刷新令牌失败!!!
|
||||
// add by AIOTAGRO:这里一定要抛出 resp.data,从而触发 authenticateResponseInterceptor 中,刷新令牌失败!!!
|
||||
if (!newToken) {
|
||||
throw resp.data;
|
||||
}
|
||||
@@ -154,7 +154,7 @@ function createRequestClient(baseURL: string, options?: RequestClientOptions) {
|
||||
const responseData = error?.response?.data ?? {};
|
||||
const errorMessage =
|
||||
responseData?.error ?? responseData?.message ?? responseData.msg ?? '';
|
||||
// add by 芋艿:特殊:避免 401 “账号未登录”,重复提示。因为,此时会跳转到登录界面,只需提示一次!!!
|
||||
// add by AIOTAGRO:特殊:避免 401 "账号未登录",重复提示。因为,此时会跳转到登录界面,只需提示一次!!!
|
||||
if (error?.data?.code === 401) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -493,7 +493,7 @@ const previewProcessJson = () => {
|
||||
});
|
||||
};
|
||||
|
||||
/* ------------------------------------------------ 芋道源码 methods ------------------------------------------------------ */
|
||||
/* ------------------------------------------------ AIOTAGRO methods ------------------------------------------------------ */
|
||||
onMounted(() => {
|
||||
initBpmnModeler();
|
||||
createNewDiagram(props.value);
|
||||
|
||||
@@ -466,7 +466,7 @@ watch(
|
||||
@change="() => updateLoopBase()"
|
||||
/>
|
||||
</FormItem>
|
||||
<!-- add by 芋艿:由于「元素变量」暂时用不到,所以这里 display 为 none -->
|
||||
<!-- add by AIOTAGRO:由于「元素变量」暂时用不到,所以这里 display 为 none -->
|
||||
<FormItem label="元素变量" key="elementVariable" class="hidden">
|
||||
<Input
|
||||
v-model:value="loopInstanceForm.elementVariable"
|
||||
@@ -484,7 +484,7 @@ watch(
|
||||
"
|
||||
/>
|
||||
</FormItem>
|
||||
<!-- add by 芋艿:由于「异步状态」暂时用不到,所以这里 display 为 none -->
|
||||
<!-- add by AIOTAGRO:由于「异步状态」暂时用不到,所以这里 display 为 none -->
|
||||
<FormItem label="异步状态" key="async" class="hidden">
|
||||
<Checkbox
|
||||
v-model:checked="loopInstanceForm.asyncBefore"
|
||||
|
||||
@@ -64,7 +64,7 @@ watch(
|
||||
<template>
|
||||
<div class="panel-tab__content">
|
||||
<Form>
|
||||
<!-- add by 芋艿:由于「异步延续」暂时用不到,所以这里 display 为 none -->
|
||||
<!-- add by AIOTAGRO:由于「异步延续」暂时用不到,所以这里 display 为 none -->
|
||||
<FormItem label="异步延续" class="hidden">
|
||||
<Checkbox
|
||||
v-model:checked="taskConfigForm.asyncBefore"
|
||||
|
||||
@@ -219,7 +219,7 @@ const resetTaskForm = () => {
|
||||
const changeCandidateStrategy = () => {
|
||||
userTaskForm.value.candidateParam = [];
|
||||
deptLevel.value = 1;
|
||||
// 注释 by 芋艿:这个交互很多用户反馈费解,https://t.zsxq.com/xNmas 所以暂时屏蔽
|
||||
// 注释 by AIOTAGRO:这个交互很多用户反馈费解,https://t.zsxq.com/xNmas 所以暂时屏蔽
|
||||
// if (userTaskForm.value.candidateStrategy === CandidateStrategy.FORM_USER) {
|
||||
// // 特殊处理表单内用户字段,当只有发起人选项时应选中发起人
|
||||
// if (!userFieldOnFormOptions.value || userFieldOnFormOptions.value.length <= 1) {
|
||||
|
||||
@@ -121,9 +121,9 @@ export const useApiSelect = (option: ApiSelectProps) => {
|
||||
parseOptions0(data);
|
||||
return;
|
||||
}
|
||||
// 情况三:不是 yudao-vue-pro 标准返回
|
||||
// 情况三:不是 aiotagro-vue-pro 标准返回
|
||||
console.warn(
|
||||
`接口[${props.url}] 返回结果不是 yudao-vue-pro 标准返回建议采用自定义解析函数处理`,
|
||||
`接口[${props.url}] 返回结果不是 aiotagro-vue-pro 标准返回建议采用自定义解析函数处理`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ export const useDictSelectRule = () => {
|
||||
title: label,
|
||||
info: '',
|
||||
$required: false,
|
||||
modelField: 'value',
|
||||
};
|
||||
},
|
||||
props(_: any, { t }: any) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { Rule } from '@form-create/ant-design-vue';
|
||||
/** 数据字典 Select 选择器组件 Props 类型 */
|
||||
export interface DictSelectProps {
|
||||
dictType: string; // 字典类型
|
||||
valueType?: 'bool' | 'int' | 'str'; // 字典值类型 TODO @芋艿:'boolean' | 'number' | 'string';需要和 vue3 一起统一!
|
||||
valueType?: 'bool' | 'int' | 'str'; // 字典值类型 TODO @AIOTAGRO:'boolean' | 'number' | 'string';需要和 vue3 一起统一!
|
||||
selectType?: 'checkbox' | 'radio' | 'select'; // 选择器类型,下拉框 select、多选框 checkbox、单选框 radio
|
||||
formCreateInject?: any;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ function getUserTypeColor(userType: number) {
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<!-- TODO @xingyu:有没可能美化下? -->
|
||||
<Timeline>
|
||||
<Timeline.Item
|
||||
v-for="log in logList"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// TODO @芋艿:是否有更好的组织形式?!
|
||||
// TODO @AIOTAGRO:是否有更好的组织形式?!
|
||||
<script lang="ts" setup>
|
||||
import type { DataNode } from 'ant-design-vue/es/tree';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
// TODO @芋艿:是否有更好的组织形式?!
|
||||
// TODO @AIOTAGRO:是否有更好的组织形式?!
|
||||
// TODO @xingyu:你感觉,这个放到每个 system、infra 模块下,然后新建一个 components,表示每个模块,有一些共享的组件?然后,全局只放通用的(无业务含义的),可以哇?
|
||||
import type { Key } from 'ant-design-vue/es/table/interface';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
// TODO @芋艿:后续是不是把业务组件,挪到每个模块里;待定;
|
||||
// TODO @AIOTAGRO:后续是不是把业务组件,挪到每个模块里;待定;
|
||||
import type { Ref } from 'vue';
|
||||
|
||||
import type { SimpleFlowNode } from '../../consts';
|
||||
|
||||
@@ -178,7 +178,7 @@ export function useFormFields() {
|
||||
return parseFormCreateFields(unref(formFields));
|
||||
}
|
||||
|
||||
// TODO @芋艿:后续需要把各种类似 useFormFieldsPermission 的逻辑,抽成一个通用方法。
|
||||
// TODO @AIOTAGRO:后续需要把各种类似 useFormFieldsPermission 的逻辑,抽成一个通用方法。
|
||||
/**
|
||||
* @description 获取流程表单的字段和发起人字段
|
||||
*/
|
||||
|
||||
@@ -78,7 +78,7 @@ export function useUploadType({
|
||||
return { getAccept, getStringAccept, getHelpText };
|
||||
}
|
||||
|
||||
// TODO @芋艿:目前保持和 admin-vue3 一致,后续可能重构
|
||||
// TODO @AIOTAGRO:目前保持和 admin-vue3 一致,后续可能重构
|
||||
export function useUpload(directory?: string) {
|
||||
// 后端上传地址
|
||||
const uploadUrl = getUploadUrl();
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# locale
|
||||
|
||||
每个app使用的国际化可能不同,这里用于扩展国际化的功能,例如扩展 dayjs、antd组件库的多语言切换,以及app本身的国际化文件。
|
||||
每个app使用的国际化可能不同,这里用于扩展国际化的功能,例如扩展 dayjs、antd组件库的多语言切换,以及app本身的国际化文件<EFBFBD><EFBFBD>?
|
||||
|
||||
@@ -20,6 +20,6 @@ export const overridesPreferences = defineOverridesPreferences({
|
||||
},
|
||||
copyright: {
|
||||
companyName: import.meta.env.VITE_APP_TITLE,
|
||||
companySiteLink: 'https://gitee.com/yudaocode/yudao-ui-admin-vben',
|
||||
companySiteLink: 'https://gitee.com/yudaocode/yudao-ui-admin-vben' // AIOTAGRO,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ async function generateAccess(options: GenerateMenuAndRoutesOptions) {
|
||||
return await generateAccessible(preferences.app.accessMode, {
|
||||
...options,
|
||||
fetchMenuListAsync: async () => {
|
||||
// 由于 yudao 通过 accessStore 读取,所以不在进行 message.loading 提示
|
||||
// 由于 AIOTAGRO 通过 accessStore 读取,所以不在进行 message.loading 提示
|
||||
// 补充说明:accessStore.accessMenus 一开始是 AppRouteRecordRaw 类型(后端加载),后面被赋值成 MenuRecordRaw 类型(前端转换)
|
||||
const accessMenus = accessStore.accessMenus as AppRouteRecordRaw[];
|
||||
return convertServerMenuToRouteRecordStringComponent(accessMenus);
|
||||
|
||||
@@ -102,7 +102,7 @@ function setupAccessGuard(router: Router) {
|
||||
// 当前登录用户拥有的角色标识列表
|
||||
let userInfo = userStore.userInfo;
|
||||
if (!userInfo) {
|
||||
// add by 芋艿:由于 yudao 是 fetchUserInfo 统一加载用户 + 权限信息,所以将 fetchMenuListAsync
|
||||
// add by 芋艿:由于 AIOTAGRO 是 fetchUserInfo 统一加载用户 + 权限信息,所以将 fetchMenuListAsync
|
||||
const loading = message.loading({
|
||||
content: `${$t('common.loadingMenu')}...`,
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ const coreRouteNames = traverseTreeValues(coreRoutes, (route) => route.name);
|
||||
/** 有权限校验的路由列表,包含动态路由和静态路由 */
|
||||
const accessRoutes = [...dynamicRoutes, ...staticRoutes];
|
||||
|
||||
// add by 芋艿:from https://github.com/vbenjs/vue-vben-admin/blob/main/playground/src/router/routes/index.ts#L38-L45
|
||||
// add by AIOTAGRO:from https://github.com/vbenjs/vue-vben-admin/blob/main/playground/src/router/routes/index.ts#L38-L45
|
||||
const componentKeys: string[] = Object.keys(
|
||||
import.meta.glob('../../views/**/*.vue'),
|
||||
)
|
||||
|
||||
@@ -78,4 +78,4 @@ const routes: RouteRecordRaw[] = [
|
||||
// },
|
||||
];
|
||||
|
||||
export default routes; // update by 芋艿:不展示
|
||||
export default routes; // update by AIOTAGRO:不展示
|
||||
|
||||
@@ -71,7 +71,7 @@ export const useAuthStore = defineStore('auth', () => {
|
||||
accessStore.setRefreshToken(refreshToken);
|
||||
|
||||
// 获取用户信息并存储到 userStore、accessStore 中
|
||||
// TODO @芋艿:清理掉 accessCodes 相关的逻辑
|
||||
// TODO @AIOTAGRO:清理掉 accessCodes 相关的逻辑
|
||||
// const [fetchUserInfoResult, accessCodes] = await Promise.all([
|
||||
// fetchUserInfo(),
|
||||
// // getAccessCodesApi(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 针对 https://github.com/xaboy/form-create-designer 封装的工具类
|
||||
*/
|
||||
// TODO @芋艿:后续这些 form-create 的优化;另外需要使用 form-create-helper 会好点
|
||||
// TODO @AIOTAGRO:后续这些 form-create 的优化;另外需要使用 form-create-helper 会好点
|
||||
import { isRef } from 'vue';
|
||||
|
||||
import formCreate from '@form-create/ant-design-vue';
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# \_core
|
||||
|
||||
此目录包含应用程序正常运行所需的基本视图。这些视图是应用程序布局中使用的视图。
|
||||
此目录包含应用程序正常运行所需的基本视图。这些视图是应用程序布局中使用的视图<EFBFBD><EFBFBD>?
|
||||
|
||||
@@ -57,7 +57,7 @@ onMounted(loadProfile);
|
||||
<Tabs.TabPane key="userSocial" tab="社交绑定" force-render>
|
||||
<UserSocial @update:active-name="activeName = $event" />
|
||||
</Tabs.TabPane>
|
||||
<!-- TODO @芋艿:在线设备 -->
|
||||
<!-- TODO @AIOTAGRO:在线设备 -->
|
||||
</Tabs>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,7 @@ async function handlerPromptClick(prompt: any) {
|
||||
<!-- center-container -->
|
||||
<div class="flex flex-col justify-center">
|
||||
<!-- title -->
|
||||
<div class="text-center text-3xl font-bold">芋道 AI</div>
|
||||
<div class="text-center text-3xl font-bold">AIOTAGRO AI</div>
|
||||
|
||||
<!-- role-list -->
|
||||
<div class="mt-5 flex w-96 flex-wrap items-center justify-center">
|
||||
|
||||
@@ -490,11 +490,11 @@ onMounted(async () => {
|
||||
activeMessageListLoading.value = true;
|
||||
await getMessageList();
|
||||
});
|
||||
// TODO @芋艿:深度思考
|
||||
// TODO @芋艿:联网搜索
|
||||
// TODO @芋艿:附件支持
|
||||
// TODO @芋艿:mcp 相关
|
||||
// TODO @芋艿:异常消息的处理
|
||||
// TODO @AIOTAGRO:深度思考
|
||||
// TODO @AIOTAGRO:联网搜索
|
||||
// TODO @AIOTAGRO:附件支持
|
||||
// TODO @AIOTAGRO:mcp 相关
|
||||
// TODO @AIOTAGRO:异常消息的处理
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -42,14 +42,11 @@ export function useGridFormSchema(): VbenFormSchema[] {
|
||||
},
|
||||
{
|
||||
fieldName: 'status',
|
||||
label: '流程状态',
|
||||
label: '审批状态',
|
||||
component: 'Select',
|
||||
componentProps: {
|
||||
options: getDictOptions(
|
||||
DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS,
|
||||
'number',
|
||||
),
|
||||
placeholder: '请选择流程状态',
|
||||
options: getDictOptions(DICT_TYPE.BPM_TASK_STATUS, 'number'),
|
||||
placeholder: '请选择审批状态',
|
||||
allowClear: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -30,7 +30,6 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 已选择的商机 */
|
||||
const checkedRows = ref<CrmBusinessApi.Business[]>([]);
|
||||
function setCheckedRows({ records }: { records: CrmBusinessApi.Business[] }) {
|
||||
checkedRows.value = records;
|
||||
|
||||
@@ -46,7 +46,6 @@ const [DetailListModal, detailListModalApi] = useVbenModal({
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 已选择的商机 */
|
||||
const checkedRows = ref<CrmBusinessApi.Business[]>([]);
|
||||
function setCheckedRows({ records }: { records: CrmBusinessApi.Business[] }) {
|
||||
checkedRows.value = records;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { default as PermissionList } from './modules/permission-list.vue';
|
||||
export { default as PermissionList } from './modules/list.vue';
|
||||
export { default as TransferForm } from './modules/transfer-form.vue';
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useGridColumns } from './data';
|
||||
import Form from './permission-form.vue';
|
||||
import Form from './form.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
bizId: number; // 模块数据编号
|
||||
@@ -38,13 +38,14 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
// 校验负责人权限和编辑权限
|
||||
const validateOwnerUser = ref(false);
|
||||
const validateWrite = ref(false);
|
||||
const isPool = ref(false);
|
||||
const userStore = useUserStore();
|
||||
|
||||
const validateOwnerUser = ref(false); // 负责人权限
|
||||
const validateWrite = ref(false); // 编辑权限
|
||||
const isPool = ref(false); // 是否是公海
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
function handleRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
@@ -62,6 +63,7 @@ function setCheckedRows({
|
||||
checkedRows.value = records;
|
||||
}
|
||||
|
||||
/** 新建团队成员 */
|
||||
function handleCreate() {
|
||||
formModalApi
|
||||
.setData({
|
||||
@@ -71,6 +73,7 @@ function handleCreate() {
|
||||
.open();
|
||||
}
|
||||
|
||||
/** 编辑团队成员 */
|
||||
function handleEdit() {
|
||||
if (checkedRows.value.length === 0) {
|
||||
message.error('请先选择团队成员后操作!');
|
||||
@@ -90,6 +93,7 @@ function handleEdit() {
|
||||
.open();
|
||||
}
|
||||
|
||||
/** 删除团队成员 */
|
||||
function handleDelete() {
|
||||
if (checkedRows.value.length === 0) {
|
||||
message.error('请先选择团队成员后操作!');
|
||||
@@ -106,7 +110,7 @@ function handleDelete() {
|
||||
if (res) {
|
||||
// 提示并返回成功
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
onRefresh();
|
||||
handleRefresh();
|
||||
resolve(true);
|
||||
} else {
|
||||
reject(new Error('移出失败'));
|
||||
@@ -118,8 +122,7 @@ function handleDelete() {
|
||||
});
|
||||
}
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
/** 退出团队 */
|
||||
async function handleQuit() {
|
||||
const permission = gridApi.grid
|
||||
.getData()
|
||||
@@ -208,6 +211,7 @@ watch(
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 特殊:没有成员的情况下,说明没有负责人,是公海
|
||||
isPool.value = true;
|
||||
}
|
||||
},
|
||||
@@ -219,7 +223,7 @@ watch(
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<FormModal @success="onRefresh" />
|
||||
<FormModal @success="handleRefresh" />
|
||||
<Grid>
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
@@ -237,6 +241,7 @@ watch(
|
||||
icon: ACTION_ICON.EDIT,
|
||||
ifShow: validateOwnerUser,
|
||||
onClick: handleEdit,
|
||||
disabled: checkedRows.length === 0,
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
@@ -245,6 +250,7 @@ watch(
|
||||
icon: ACTION_ICON.DELETE,
|
||||
ifShow: validateOwnerUser,
|
||||
onClick: handleDelete,
|
||||
disabled: checkedRows.length === 0,
|
||||
},
|
||||
{
|
||||
label: '退出团队',
|
||||
@@ -99,7 +99,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
},
|
||||
async onOpenChange(isOpen: boolean) {
|
||||
if (!isOpen) {
|
||||
formApi.resetForm();
|
||||
await formApi.resetForm();
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
@@ -108,7 +108,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
return;
|
||||
}
|
||||
bizType.value = data.bizType;
|
||||
formApi.setFieldValue('id', data.bizType);
|
||||
await formApi.setFieldValue('id', data.bizType);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -37,7 +37,7 @@ function handleRefresh() {
|
||||
|
||||
/** 创建分类 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData({}).open();
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 添加下级分类 */
|
||||
|
||||
@@ -180,7 +180,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
});
|
||||
|
||||
/** 获取数据源配置列表 */
|
||||
// TODO @芋艿:这种场景的最佳实践;
|
||||
// TODO @AIOTAGRO:这种场景的最佳实践;
|
||||
async function initDataSourceConfig() {
|
||||
try {
|
||||
dataSourceConfigList.value = await getDataSourceConfigList();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
// TODO @芋艿:待定,vben2.0 有 CodeEditor,不确定官方后续会不会迁移!!!
|
||||
// TODO @AIOTAGRO:待定,vben2.0 有 CodeEditor,不确定官方后续会不会迁移!!!
|
||||
import type { InfraCodegenApi } from '#/api/infra/codegen';
|
||||
|
||||
import { h, ref } from 'vue';
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { Demo01ContactApi } from '#/api/infra/demo/demo01';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { confirm, Page, useVbenModal } from '@vben/common-ui';
|
||||
import { downloadFileFromBlobPart, isEmpty } from '@vben/utils';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
@@ -27,13 +27,19 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
function handleRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 导出表格 */
|
||||
async function handleExport() {
|
||||
const data = await exportDemo01Contact(await gridApi.formApi.getValues());
|
||||
downloadFileFromBlobPart({ fileName: '示例联系人.xls', source: data });
|
||||
}
|
||||
|
||||
/** 创建示例联系人 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData({}).open();
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑示例联系人 */
|
||||
@@ -46,12 +52,11 @@ async function handleDelete(row: Demo01ContactApi.Demo01Contact) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.id]),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await deleteDemo01Contact(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
||||
onRefresh();
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
@@ -59,16 +64,16 @@ async function handleDelete(row: Demo01ContactApi.Demo01Contact) {
|
||||
|
||||
/** 批量删除示例联系人 */
|
||||
async function handleDeleteBatch() {
|
||||
await confirm($t('ui.actionMessage.deleteBatchConfirm'));
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting'),
|
||||
content: $t('ui.actionMessage.deletingBatch'),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await deleteDemo01ContactList(checkedIds.value);
|
||||
checkedIds.value = [];
|
||||
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||
onRefresh();
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
@@ -83,12 +88,6 @@ function handleRowCheckboxChange({
|
||||
checkedIds.value = records.map((item) => item.id!);
|
||||
}
|
||||
|
||||
/** 导出表格 */
|
||||
async function handleExport() {
|
||||
const data = await exportDemo01Contact(await gridApi.formApi.getValues());
|
||||
downloadFileFromBlobPart({ fileName: '示例联系人.xls', source: data });
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
@@ -128,8 +127,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<FormModal @success="onRefresh" />
|
||||
|
||||
<FormModal @success="handleRefresh" />
|
||||
<Grid table-title="示例联系人列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
@@ -149,11 +147,11 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
onClick: handleExport,
|
||||
},
|
||||
{
|
||||
label: '批量删除',
|
||||
label: $t('ui.actionTitle.deleteBatch'),
|
||||
type: 'primary',
|
||||
danger: true,
|
||||
disabled: isEmpty(checkedIds),
|
||||
icon: ACTION_ICON.DELETE,
|
||||
disabled: isEmpty(checkedIds),
|
||||
auth: ['infra:demo01-contact:delete'],
|
||||
onClick: handleDeleteBatch,
|
||||
},
|
||||
|
||||
@@ -54,10 +54,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
// 关闭并提示
|
||||
await modalApi.close();
|
||||
emit('success');
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.operationSuccess'),
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
message.success($t('ui.actionMessage.operationSuccess'));
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
@@ -67,23 +64,21 @@ const [Modal, modalApi] = useVbenModal({
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
let data = modalApi.getData<Demo01ContactApi.Demo01Contact>();
|
||||
if (!data) {
|
||||
const data = modalApi.getData<Demo01ContactApi.Demo01Contact>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
if (data.id) {
|
||||
modalApi.lock();
|
||||
try {
|
||||
data = await getDemo01Contact(data.id);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getDemo01Contact(data.id);
|
||||
// 设置到 values
|
||||
if (formData.value) {
|
||||
await formApi.setValues(formData.value);
|
||||
}
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
// 设置到 values
|
||||
formData.value = data;
|
||||
await formApi.setValues(formData.value);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -25,15 +25,8 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
/** 切换树形展开/收缩状态 */
|
||||
const isExpanded = ref(true);
|
||||
function toggleExpand() {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
gridApi.grid.setAllTreeExpand(isExpanded.value);
|
||||
}
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
function handleRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
@@ -61,20 +54,25 @@ function handleAppend(row: Demo02CategoryApi.Demo02Category) {
|
||||
/** 删除示例分类 */
|
||||
async function handleDelete(row: Demo02CategoryApi.Demo02Category) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.id]),
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
duration: 0,
|
||||
});
|
||||
try {
|
||||
await deleteDemo02Category(row.id!);
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
});
|
||||
onRefresh();
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.name]));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 切换树形展开/收缩状态 */
|
||||
const isExpanded = ref(true);
|
||||
function handleExpand() {
|
||||
isExpanded.value = !isExpanded.value;
|
||||
gridApi.grid.setAllTreeExpand(isExpanded.value);
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
@@ -113,14 +111,13 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<FormModal @success="onRefresh" />
|
||||
|
||||
<FormModal @success="handleRefresh" />
|
||||
<Grid table-title="示例分类列表">
|
||||
<template #toolbar-tools>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['菜单']),
|
||||
label: $t('ui.actionTitle.create', ['示例分类']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['infra:demo02-category:create'],
|
||||
@@ -129,7 +126,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
{
|
||||
label: isExpanded ? '收缩' : '展开',
|
||||
type: 'primary',
|
||||
onClick: toggleExpand,
|
||||
onClick: handleExpand,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
|
||||
@@ -19,14 +19,9 @@ import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits(['success']);
|
||||
const formData = ref<Demo02CategoryApi.Demo02Category>();
|
||||
const parentId = ref<number>(); // 新增下级时的父级 ID
|
||||
|
||||
const getTitle = computed(() => {
|
||||
if (formData.value?.id) {
|
||||
return $t('ui.actionTitle.edit', ['示例分类']);
|
||||
}
|
||||
return parentId.value
|
||||
? $t('ui.actionTitle.create', ['下级示例分类'])
|
||||
return formData.value?.id
|
||||
? $t('ui.actionTitle.edit', ['示例分类'])
|
||||
: $t('ui.actionTitle.create', ['示例分类']);
|
||||
});
|
||||
|
||||
@@ -36,7 +31,7 @@ const [Form, formApi] = useVbenForm({
|
||||
class: 'w-full',
|
||||
},
|
||||
formItemClass: 'col-span-2',
|
||||
labelWidth: 80,
|
||||
labelWidth: 100,
|
||||
},
|
||||
layout: 'horizontal',
|
||||
schema: useFormSchema(),
|
||||
@@ -70,25 +65,23 @@ const [Modal, modalApi] = useVbenModal({
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
let data = modalApi.getData<Demo02CategoryApi.Demo02Category>();
|
||||
if (!data) {
|
||||
const data = modalApi.getData<Demo02CategoryApi.Demo02Category>();
|
||||
if (!data || !data.id) {
|
||||
// 设置上级
|
||||
await formApi.setValues(data);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.id) {
|
||||
// 编辑
|
||||
modalApi.lock();
|
||||
try {
|
||||
data = await getDemo02Category(data.id);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getDemo02Category(data.id);
|
||||
// 设置到 values
|
||||
if (formData.value) {
|
||||
await formApi.setValues(formData.value);
|
||||
}
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
// 设置到 values
|
||||
formData.value = data;
|
||||
await formApi.setValues(formData.value);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
||||
|
||||
import { h, ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Download, Plus, Trash2 } from '@vben/icons';
|
||||
import { confirm, Page, useVbenModal } from '@vben/common-ui';
|
||||
import { downloadFileFromBlobPart, isEmpty } from '@vben/utils';
|
||||
|
||||
import { Button, message, Tabs } from 'ant-design-vue';
|
||||
import { message, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -34,48 +33,53 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
function handleRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 导出表格 */
|
||||
async function handleExport() {
|
||||
const data = await exportDemo03Student(await gridApi.formApi.getValues());
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}
|
||||
|
||||
/** 创建学生 */
|
||||
function onCreate() {
|
||||
formModalApi.setData({}).open();
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑学生 */
|
||||
function onEdit(row: Demo03StudentApi.Demo03Student) {
|
||||
function handleEdit(row: Demo03StudentApi.Demo03Student) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 删除学生 */
|
||||
async function onDelete(row: Demo03StudentApi.Demo03Student) {
|
||||
async function handleDelete(row: Demo03StudentApi.Demo03Student) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.id]),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await deleteDemo03Student(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
||||
onRefresh();
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 批量删除学生 */
|
||||
async function onDeleteBatch() {
|
||||
async function handleDeleteBatch() {
|
||||
await confirm($t('ui.actionMessage.deleteBatchConfirm'));
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting'),
|
||||
content: $t('ui.actionMessage.deletingBatch'),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await deleteDemo03StudentList(checkedIds.value);
|
||||
checkedIds.value = [];
|
||||
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||
onRefresh();
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
@@ -85,17 +89,11 @@ const checkedIds = ref<number[]>([]);
|
||||
function handleRowCheckboxChange({
|
||||
records,
|
||||
}: {
|
||||
records: Demo03StudentApi.Demo03Grade[];
|
||||
records: Demo03StudentApi.Demo03Student[];
|
||||
}) {
|
||||
checkedIds.value = records.map((item) => item.id!);
|
||||
}
|
||||
|
||||
/** 导出表格 */
|
||||
async function onExport() {
|
||||
const data = await exportDemo03Student(await gridApi.formApi.getValues());
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
@@ -139,39 +137,37 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<FormModal @success="onRefresh" />
|
||||
|
||||
<FormModal @success="handleRefresh" />
|
||||
<div>
|
||||
<Grid table-title="学生列表">
|
||||
<template #toolbar-tools>
|
||||
<Button
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:demo03-student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生']) }}
|
||||
</Button>
|
||||
<Button
|
||||
:icon="h(Download)"
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@click="onExport"
|
||||
v-access:code="['infra:demo03-student:export']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</Button>
|
||||
<Button
|
||||
:icon="h(Trash2)"
|
||||
type="primary"
|
||||
danger
|
||||
class="ml-2"
|
||||
:disabled="isEmpty(checkedIds)"
|
||||
@click="onDeleteBatch"
|
||||
v-access:code="['infra:demo03-student:delete']"
|
||||
>
|
||||
批量删除
|
||||
</Button>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['学生']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['infra:demo03-student:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['infra:demo03-student:export'],
|
||||
onClick: handleExport,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.deleteBatch'),
|
||||
type: 'primary',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
disabled: isEmpty(checkedIds),
|
||||
auth: ['infra:demo03-student:delete'],
|
||||
onClick: handleDeleteBatch,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
@@ -181,17 +177,17 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['infra:demo03-student:update'],
|
||||
onClick: onEdit.bind(null, row),
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
danger: true,
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['infra:demo03-student:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.id]),
|
||||
confirm: onDelete.bind(null, row),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
|
||||
@@ -44,7 +44,6 @@ const [Modal, modalApi] = useVbenModal({
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as Demo03StudentApi.Demo03Course;
|
||||
@@ -66,7 +65,6 @@ const [Modal, modalApi] = useVbenModal({
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
let data = modalApi.getData<Demo03StudentApi.Demo03Course>();
|
||||
if (!data) {
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
||||
|
||||
import { h, nextTick, ref, watch } from 'vue';
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { Plus, Trash2 } from '@vben/icons';
|
||||
import { confirm, useVbenModal } from '@vben/common-ui';
|
||||
import { isEmpty } from '@vben/utils';
|
||||
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -34,7 +33,7 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
});
|
||||
|
||||
/** 创建学生课程 */
|
||||
function onCreate() {
|
||||
function handleCreate() {
|
||||
if (!props.studentId) {
|
||||
message.warning('请先选择一个学生!');
|
||||
return;
|
||||
@@ -43,38 +42,37 @@ function onCreate() {
|
||||
}
|
||||
|
||||
/** 编辑学生课程 */
|
||||
function onEdit(row: Demo03StudentApi.Demo03Course) {
|
||||
function handleEdit(row: Demo03StudentApi.Demo03Course) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 删除学生课程 */
|
||||
async function onDelete(row: Demo03StudentApi.Demo03Course) {
|
||||
async function handleDelete(row: Demo03StudentApi.Demo03Course) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.id]),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await deleteDemo03Course(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
||||
onRefresh();
|
||||
await handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 批量删除学生课程 */
|
||||
async function onDeleteBatch() {
|
||||
async function handleDeleteBatch() {
|
||||
await confirm($t('ui.actionMessage.deleteBatchConfirm'));
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting'),
|
||||
content: $t('ui.actionMessage.deletingBatch'),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await deleteDemo03CourseList(checkedIds.value);
|
||||
checkedIds.value = [];
|
||||
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||
onRefresh();
|
||||
await handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
@@ -130,9 +128,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
const onRefresh = async () => {
|
||||
async function handleRefresh() {
|
||||
await gridApi.query();
|
||||
};
|
||||
}
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
@@ -142,35 +140,36 @@ watch(
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await onRefresh();
|
||||
await handleRefresh();
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FormModal @success="onRefresh" />
|
||||
<FormModal @success="handleRefresh" />
|
||||
<Grid table-title="学生课程列表">
|
||||
<template #toolbar-tools>
|
||||
<Button
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:demo03-student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生课程']) }}
|
||||
</Button>
|
||||
<Button
|
||||
:icon="h(Trash2)"
|
||||
type="primary"
|
||||
danger
|
||||
class="ml-2"
|
||||
:disabled="isEmpty(checkedIds)"
|
||||
@click="onDeleteBatch"
|
||||
v-access:code="['infra:demo03-student:delete']"
|
||||
>
|
||||
批量删除
|
||||
</Button>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['学生课程']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['infra:demo03-student:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.deleteBatch'),
|
||||
type: 'primary',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['infra:demo03-student:delete'],
|
||||
disabled: isEmpty(checkedIds),
|
||||
onClick: handleDeleteBatch,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
@@ -180,17 +179,17 @@ watch(
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['infra:demo03-student:update'],
|
||||
onClick: onEdit.bind(null, row),
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
danger: true,
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['infra:demo03-student:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.id]),
|
||||
confirm: onDelete.bind(null, row),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
|
||||
@@ -44,7 +44,6 @@ const [Modal, modalApi] = useVbenModal({
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
modalApi.lock();
|
||||
// 提交表单
|
||||
const data = (await formApi.getValues()) as Demo03StudentApi.Demo03Grade;
|
||||
@@ -66,7 +65,6 @@ const [Modal, modalApi] = useVbenModal({
|
||||
formData.value = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
let data = modalApi.getData<Demo03StudentApi.Demo03Grade>();
|
||||
if (!data) {
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp';
|
||||
|
||||
import { h, nextTick, ref, watch } from 'vue';
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
import { Plus, Trash2 } from '@vben/icons';
|
||||
import { confirm, useVbenModal } from '@vben/common-ui';
|
||||
import { isEmpty } from '@vben/utils';
|
||||
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -34,7 +33,7 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
});
|
||||
|
||||
/** 创建学生班级 */
|
||||
function onCreate() {
|
||||
function handleCreate() {
|
||||
if (!props.studentId) {
|
||||
message.warning('请先选择一个学生!');
|
||||
return;
|
||||
@@ -43,38 +42,37 @@ function onCreate() {
|
||||
}
|
||||
|
||||
/** 编辑学生班级 */
|
||||
function onEdit(row: Demo03StudentApi.Demo03Grade) {
|
||||
function handleEdit(row: Demo03StudentApi.Demo03Grade) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 删除学生班级 */
|
||||
async function onDelete(row: Demo03StudentApi.Demo03Grade) {
|
||||
async function handleDelete(row: Demo03StudentApi.Demo03Grade) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.id]),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await deleteDemo03Grade(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
||||
onRefresh();
|
||||
await handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 批量删除学生班级 */
|
||||
async function onDeleteBatch() {
|
||||
async function handleDeleteBatch() {
|
||||
await confirm($t('ui.actionMessage.deleteBatchConfirm'));
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting'),
|
||||
content: $t('ui.actionMessage.deletingBatch'),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await deleteDemo03GradeList(checkedIds.value);
|
||||
checkedIds.value = [];
|
||||
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||
await onRefresh();
|
||||
await handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
@@ -130,9 +128,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
const onRefresh = async () => {
|
||||
async function handleRefresh() {
|
||||
await gridApi.query();
|
||||
};
|
||||
}
|
||||
|
||||
/** 监听主表的关联字段的变化,加载对应的子表数据 */
|
||||
watch(
|
||||
@@ -142,35 +140,36 @@ watch(
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await onRefresh();
|
||||
await handleRefresh();
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<FormModal @success="onRefresh" />
|
||||
<FormModal @success="handleRefresh" />
|
||||
<Grid table-title="学生班级列表">
|
||||
<template #toolbar-tools>
|
||||
<Button
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:demo03-student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生班级']) }}
|
||||
</Button>
|
||||
<Button
|
||||
:icon="h(Trash2)"
|
||||
type="primary"
|
||||
danger
|
||||
class="ml-2"
|
||||
:disabled="isEmpty(checkedIds)"
|
||||
@click="onDeleteBatch"
|
||||
v-access:code="['infra:demo03-student:delete']"
|
||||
>
|
||||
批量删除
|
||||
</Button>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['学生班级']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['infra:demo03-student:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.deleteBatch'),
|
||||
type: 'primary',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['infra:demo03-student:delete'],
|
||||
disabled: isEmpty(checkedIds),
|
||||
onClick: handleDeleteBatch,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
@@ -180,7 +179,7 @@ watch(
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['infra:demo03-student:update'],
|
||||
onClick: onEdit.bind(null, row),
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
@@ -190,7 +189,7 @@ watch(
|
||||
auth: ['infra:demo03-student:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.id]),
|
||||
confirm: onDelete.bind(null, row),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
|
||||
@@ -65,21 +65,20 @@ const [Modal, modalApi] = useVbenModal({
|
||||
return;
|
||||
}
|
||||
// 加载数据
|
||||
let data = modalApi.getData<Demo03StudentApi.Demo03Student>();
|
||||
if (!data) {
|
||||
const data = modalApi.getData<Demo03StudentApi.Demo03Student>();
|
||||
if (!data || !data.id) {
|
||||
return;
|
||||
}
|
||||
if (data.id) {
|
||||
modalApi.lock();
|
||||
try {
|
||||
data = await getDemo03Student(data.id);
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
modalApi.lock();
|
||||
try {
|
||||
formData.value = await getDemo03Student(data.id);
|
||||
// 设置到 values
|
||||
if (formData.value) {
|
||||
await formApi.setValues(formData.value);
|
||||
}
|
||||
} finally {
|
||||
modalApi.unlock();
|
||||
}
|
||||
// 设置到 values
|
||||
formData.value = data;
|
||||
await formApi.setValues(formData.value);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -143,7 +143,6 @@ export function useGridColumns(): VxeTableGridOptions<Demo03StudentApi.Demo03Stu
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'actions',
|
||||
title: '操作',
|
||||
width: 280,
|
||||
fixed: 'right',
|
||||
@@ -170,7 +169,6 @@ export function useDemo03CourseGridEditColumns(): VxeTableGridOptions<Demo03Stud
|
||||
slots: { default: 'score' },
|
||||
},
|
||||
{
|
||||
field: 'actions',
|
||||
title: '操作',
|
||||
width: 280,
|
||||
fixed: 'right',
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner';
|
||||
|
||||
import { h, ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Download, Plus, Trash2 } from '@vben/icons';
|
||||
import { confirm, Page, useVbenModal } from '@vben/common-ui';
|
||||
import { downloadFileFromBlobPart, isEmpty } from '@vben/utils';
|
||||
|
||||
import { Button, message, Tabs } from 'ant-design-vue';
|
||||
import { message, Tabs } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -33,48 +32,53 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
function handleRefresh() {
|
||||
gridApi.reload();
|
||||
}
|
||||
|
||||
/** 导出表格 */
|
||||
async function handleExport() {
|
||||
const data = await exportDemo03Student(await gridApi.formApi.getValues());
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}
|
||||
|
||||
/** 创建学生 */
|
||||
function onCreate() {
|
||||
formModalApi.setData({}).open();
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑学生 */
|
||||
function onEdit(row: Demo03StudentApi.Demo03Student) {
|
||||
function handleEdit(row: Demo03StudentApi.Demo03Student) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 删除学生 */
|
||||
async function onDelete(row: Demo03StudentApi.Demo03Student) {
|
||||
async function handleDelete(row: Demo03StudentApi.Demo03Student) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.id]),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await deleteDemo03Student(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
||||
onRefresh();
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 批量删除学生 */
|
||||
async function onDeleteBatch() {
|
||||
async function handleDeleteBatch() {
|
||||
await confirm($t('ui.actionMessage.deleteBatchConfirm'));
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting'),
|
||||
content: $t('ui.actionMessage.deletingBatch'),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await deleteDemo03StudentList(checkedIds.value);
|
||||
checkedIds.value = [];
|
||||
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||
onRefresh();
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
@@ -89,12 +93,6 @@ function handleRowCheckboxChange({
|
||||
checkedIds.value = records.map((item) => item.id!);
|
||||
}
|
||||
|
||||
/** 导出表格 */
|
||||
async function onExport() {
|
||||
const data = await exportDemo03Student(await gridApi.formApi.getValues());
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
schema: useGridFormSchema(),
|
||||
@@ -134,8 +132,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<FormModal @success="onRefresh" />
|
||||
|
||||
<FormModal @success="handleRefresh" />
|
||||
<Grid table-title="学生列表">
|
||||
<template #expand_content="{ row }">
|
||||
<!-- 子表的表单 -->
|
||||
@@ -149,34 +146,33 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
</Tabs>
|
||||
</template>
|
||||
<template #toolbar-tools>
|
||||
<Button
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:demo03-student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生']) }}
|
||||
</Button>
|
||||
<Button
|
||||
:icon="h(Download)"
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@click="onExport"
|
||||
v-access:code="['infra:demo03-student:export']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</Button>
|
||||
<Button
|
||||
:icon="h(Trash2)"
|
||||
type="primary"
|
||||
danger
|
||||
class="ml-2"
|
||||
:disabled="isEmpty(checkedIds)"
|
||||
@click="onDeleteBatch"
|
||||
v-access:code="['infra:demo03-student:delete']"
|
||||
>
|
||||
批量删除
|
||||
</Button>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['学生']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['infra:demo03-student:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['infra:demo03-student:export'],
|
||||
onClick: handleExport,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.deleteBatch'),
|
||||
type: 'primary',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['infra:demo03-student:delete'],
|
||||
disabled: isEmpty(checkedIds),
|
||||
onClick: handleDeleteBatch,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
@@ -186,7 +182,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['infra:demo03-student:update'],
|
||||
onClick: onEdit.bind(null, row),
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
@@ -196,7 +192,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
auth: ['infra:demo03-student:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.id]),
|
||||
confirm: onDelete.bind(null, row),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
|
||||
@@ -26,6 +26,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
keepSource: true,
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
isHover: true,
|
||||
},
|
||||
pagerConfig: {
|
||||
enabled: false,
|
||||
@@ -37,14 +38,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
});
|
||||
|
||||
/** 添加学生课程 */
|
||||
const onAdd = async () => {
|
||||
async function handleAdd() {
|
||||
await gridApi.grid.insertAt({} as Demo03StudentApi.Demo03Course, -1);
|
||||
};
|
||||
}
|
||||
|
||||
/** 删除学生课程 */
|
||||
const onDelete = async (row: Demo03StudentApi.Demo03Course) => {
|
||||
async function handleDelete(row: Demo03StudentApi.Demo03Course) {
|
||||
await gridApi.grid.remove(row);
|
||||
};
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
@@ -98,7 +99,7 @@ watch(
|
||||
auth: ['infra:demo03-student:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.id]),
|
||||
confirm: onDelete.bind(null, row),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
@@ -110,7 +111,7 @@ watch(
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
ghost
|
||||
@click="onAdd"
|
||||
@click="handleAdd"
|
||||
v-access:code="['infra:demo03-student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生课程']) }}
|
||||
|
||||
@@ -31,7 +31,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
async function onRefresh() {
|
||||
async function handleRefresh() {
|
||||
await gridApi.grid.loadData(
|
||||
await getDemo03CourseListByStudentId(props.studentId!),
|
||||
);
|
||||
@@ -45,7 +45,7 @@ watch(
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await onRefresh();
|
||||
await handleRefresh();
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
@@ -31,7 +31,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
async function onRefresh() {
|
||||
async function handleRefresh() {
|
||||
await gridApi.grid.loadData([
|
||||
await getDemo03GradeByStudentId(props.studentId!),
|
||||
]);
|
||||
@@ -45,7 +45,7 @@ watch(
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
await onRefresh();
|
||||
await handleRefresh();
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
@@ -108,14 +108,17 @@ export function useGridColumns(): VxeTableGridOptions<Demo03StudentApi.Demo03Stu
|
||||
{
|
||||
field: 'id',
|
||||
title: '编号',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '名字',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'sex',
|
||||
title: '性别',
|
||||
minWidth: 120,
|
||||
cellRender: {
|
||||
name: 'CellDict',
|
||||
props: { type: DICT_TYPE.SYSTEM_USER_SEX },
|
||||
@@ -124,21 +127,23 @@ export function useGridColumns(): VxeTableGridOptions<Demo03StudentApi.Demo03Stu
|
||||
{
|
||||
field: 'birthday',
|
||||
title: '出生日期',
|
||||
minWidth: 120,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '简介',
|
||||
minWidth: 120,
|
||||
},
|
||||
{
|
||||
field: 'createTime',
|
||||
title: '创建时间',
|
||||
minWidth: 120,
|
||||
formatter: 'formatDateTime',
|
||||
},
|
||||
{
|
||||
field: 'actions',
|
||||
title: '操作',
|
||||
width: 280,
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
@@ -153,17 +158,18 @@ export function useDemo03CourseGridEditColumns(): VxeTableGridOptions<Demo03Stud
|
||||
{
|
||||
field: 'name',
|
||||
title: '名字',
|
||||
minWidth: 120,
|
||||
slots: { default: 'name' },
|
||||
},
|
||||
{
|
||||
field: 'score',
|
||||
title: '分数',
|
||||
minWidth: 120,
|
||||
slots: { default: 'score' },
|
||||
},
|
||||
{
|
||||
field: 'actions',
|
||||
title: '操作',
|
||||
width: 280,
|
||||
width: 200,
|
||||
fixed: 'right',
|
||||
slots: { default: 'actions' },
|
||||
},
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal';
|
||||
|
||||
import { h, ref } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import { Page, useVbenModal } from '@vben/common-ui';
|
||||
import { Download, Plus, Trash2 } from '@vben/icons';
|
||||
import { confirm, Page, useVbenModal } from '@vben/common-ui';
|
||||
import { downloadFileFromBlobPart, isEmpty } from '@vben/utils';
|
||||
|
||||
import { Button, message } from 'ant-design-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import {
|
||||
@@ -28,31 +27,53 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
});
|
||||
|
||||
/** 刷新表格 */
|
||||
function onRefresh() {
|
||||
function handleRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
/** 导出表格 */
|
||||
async function handleExport() {
|
||||
const data = await exportDemo03Student(await gridApi.formApi.getValues());
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}
|
||||
|
||||
/** 创建学生 */
|
||||
function onCreate() {
|
||||
formModalApi.setData({}).open();
|
||||
function handleCreate() {
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑学生 */
|
||||
function onEdit(row: Demo03StudentApi.Demo03Student) {
|
||||
function handleEdit(row: Demo03StudentApi.Demo03Student) {
|
||||
formModalApi.setData(row).open();
|
||||
}
|
||||
|
||||
/** 删除学生 */
|
||||
async function onDelete(row: Demo03StudentApi.Demo03Student) {
|
||||
async function handleDelete(row: Demo03StudentApi.Demo03Student) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.id]),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await deleteDemo03Student(row.id!);
|
||||
message.success($t('ui.actionMessage.deleteSuccess', [row.id]));
|
||||
onRefresh();
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 批量删除学生 */
|
||||
async function handleDeleteBatch() {
|
||||
await confirm($t('ui.actionMessage.deleteBatchConfirm'));
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deletingBatch'),
|
||||
duration: 0,
|
||||
});
|
||||
try {
|
||||
await deleteDemo03StudentList(checkedIds.value);
|
||||
checkedIds.value = [];
|
||||
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
@@ -66,28 +87,6 @@ function handleRowCheckboxChange({
|
||||
}) {
|
||||
checkedIds.value = records.map((item) => item.id!);
|
||||
}
|
||||
/** 批量删除学生 */
|
||||
async function onDeleteBatch() {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting'),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
try {
|
||||
await deleteDemo03StudentList(checkedIds.value);
|
||||
checkedIds.value = [];
|
||||
message.success($t('ui.actionMessage.deleteSuccess'));
|
||||
onRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/** 导出表格 */
|
||||
async function onExport() {
|
||||
const data = await exportDemo03Student(await gridApi.formApi.getValues());
|
||||
downloadFileFromBlobPart({ fileName: '学生.xls', source: data });
|
||||
}
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
@@ -128,38 +127,36 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<FormModal @success="onRefresh" />
|
||||
|
||||
<FormModal @success="handleRefresh" />
|
||||
<Grid table-title="学生列表">
|
||||
<template #toolbar-tools>
|
||||
<Button
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
@click="onCreate"
|
||||
v-access:code="['infra:demo03-student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生']) }}
|
||||
</Button>
|
||||
<Button
|
||||
:icon="h(Download)"
|
||||
type="primary"
|
||||
class="ml-2"
|
||||
@click="onExport"
|
||||
v-access:code="['infra:demo03-student:export']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.export') }}
|
||||
</Button>
|
||||
<Button
|
||||
:icon="h(Trash2)"
|
||||
type="primary"
|
||||
danger
|
||||
class="ml-2"
|
||||
:disabled="isEmpty(checkedIds)"
|
||||
@click="onDeleteBatch"
|
||||
v-access:code="['infra:demo03-student:delete']"
|
||||
>
|
||||
批量删除
|
||||
</Button>
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: $t('ui.actionTitle.create', ['学生']),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.ADD,
|
||||
auth: ['infra:demo03-student:create'],
|
||||
onClick: handleCreate,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.export'),
|
||||
type: 'primary',
|
||||
icon: ACTION_ICON.DOWNLOAD,
|
||||
auth: ['infra:demo03-student:export'],
|
||||
onClick: handleExport,
|
||||
},
|
||||
{
|
||||
label: $t('ui.actionTitle.deleteBatch'),
|
||||
type: 'primary',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
disabled: isEmpty(checkedIds),
|
||||
auth: ['infra:demo03-student:delete'],
|
||||
onClick: handleDeleteBatch,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
<template #actions="{ row }">
|
||||
<TableAction
|
||||
@@ -169,17 +166,17 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
type: 'link',
|
||||
icon: ACTION_ICON.EDIT,
|
||||
auth: ['infra:demo03-student:update'],
|
||||
onClick: onEdit.bind(null, row),
|
||||
onClick: handleEdit.bind(null, row),
|
||||
},
|
||||
{
|
||||
label: $t('common.delete'),
|
||||
danger: true,
|
||||
type: 'link',
|
||||
danger: true,
|
||||
icon: ACTION_ICON.DELETE,
|
||||
auth: ['infra:demo03-student:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.id]),
|
||||
confirm: onDelete.bind(null, row),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
|
||||
@@ -37,14 +37,14 @@ const [Grid, gridApi] = useVbenVxeGrid({
|
||||
});
|
||||
|
||||
/** 添加学生课程 */
|
||||
const onAdd = async () => {
|
||||
async function handleAdd() {
|
||||
await gridApi.grid.insertAt({} as Demo03StudentApi.Demo03Course, -1);
|
||||
};
|
||||
}
|
||||
|
||||
/** 删除学生课程 */
|
||||
const onDelete = async (row: Demo03StudentApi.Demo03Course) => {
|
||||
async function handleDelete(row: Demo03StudentApi.Demo03Course) {
|
||||
await gridApi.grid.remove(row);
|
||||
};
|
||||
}
|
||||
|
||||
/** 提供获取表格数据的方法供父组件调用 */
|
||||
defineExpose({
|
||||
@@ -98,7 +98,7 @@ watch(
|
||||
auth: ['infra:demo03-student:delete'],
|
||||
popConfirm: {
|
||||
title: $t('ui.actionMessage.deleteConfirm', [row.id]),
|
||||
confirm: onDelete.bind(null, row),
|
||||
confirm: handleDelete.bind(null, row),
|
||||
},
|
||||
},
|
||||
]"
|
||||
@@ -110,7 +110,7 @@ watch(
|
||||
:icon="h(Plus)"
|
||||
type="primary"
|
||||
ghost
|
||||
@click="onAdd"
|
||||
@click="handleAdd"
|
||||
v-access:code="['infra:demo03-student:create']"
|
||||
>
|
||||
{{ $t('ui.actionTitle.create', ['学生课程']) }}
|
||||
|
||||
@@ -87,7 +87,7 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
|
||||
/** 创建示例联系人 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData({}).open();
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑示例联系人 */
|
||||
|
||||
@@ -69,7 +69,7 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
|
||||
/** 创建示例分类 */
|
||||
function onCreate() {
|
||||
formModalApi.setData({}).open();
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑示例分类 */
|
||||
|
||||
@@ -98,7 +98,7 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
|
||||
/** 创建学生 */
|
||||
function onCreate() {
|
||||
formModalApi.setData({}).open();
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑学生 */
|
||||
|
||||
@@ -94,7 +94,7 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
|
||||
/** 创建学生 */
|
||||
function onCreate() {
|
||||
formModalApi.setData({}).open();
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑学生 */
|
||||
|
||||
@@ -88,7 +88,7 @@ const [FormModal, formModalApi] = useVbenModal({
|
||||
|
||||
/** 创建学生 */
|
||||
function onCreate() {
|
||||
formModalApi.setData({}).open();
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 编辑学生 */
|
||||
|
||||
@@ -50,7 +50,7 @@ async function handleMaster(row: InfraFileConfigApi.FileConfig) {
|
||||
});
|
||||
try {
|
||||
await updateFileConfigMaster(row.id!);
|
||||
message.success($t('ui.actionMessage.updateSuccess', [row.name]));
|
||||
message.success($t('ui.actionMessage.updateSuccess'));
|
||||
handleRefresh();
|
||||
} finally {
|
||||
hideLoading();
|
||||
|
||||
@@ -28,7 +28,7 @@ function onRefresh() {
|
||||
|
||||
/** 创建分类 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData({}).open();
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 添加下级分类 */
|
||||
|
||||
@@ -454,7 +454,6 @@ export function channelSchema(formType: string): VbenFormSchema[] {
|
||||
accept: ['pem'],
|
||||
},
|
||||
}),
|
||||
rules: 'required',
|
||||
dependencies: {
|
||||
show(values) {
|
||||
return values?.config?.apiVersion === 'v3';
|
||||
|
||||
@@ -150,7 +150,7 @@ export function useTypeGridColumns(): VxeTableGridOptions['columns'] {
|
||||
|
||||
// ============================== 字典数据 ==============================
|
||||
|
||||
// TODO @芋艿:后续针对 antd,增加
|
||||
// TODO @AIOTAGRO:后续针对 antd,增加
|
||||
/**
|
||||
* 颜色选项
|
||||
*/
|
||||
|
||||
@@ -29,7 +29,7 @@ function handleRefresh() {
|
||||
|
||||
/** 创建菜单 */
|
||||
function handleCreate() {
|
||||
formModalApi.setData({}).open();
|
||||
formModalApi.setData(null).open();
|
||||
}
|
||||
|
||||
/** 添加下级菜单 */
|
||||
|
||||
@@ -125,7 +125,7 @@ export function useDetailSchema(): DescriptionItemSchema[] {
|
||||
{
|
||||
field: 'avatar',
|
||||
label: '用户头像',
|
||||
// TODO @芋艿:使用 antd 的 Image 组件
|
||||
// TODO @AIOTAGRO:使用 antd 的 Image 组件
|
||||
content: (data: SystemSocialUserApi.SocialUser) => {
|
||||
if (data?.avatar) {
|
||||
return h('img', {
|
||||
|
||||
@@ -10,7 +10,7 @@ export default defineConfig(async () => {
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/admin-api/, ''),
|
||||
// mock代理目标地址
|
||||
target: 'http://localhost:48080/admin-api',
|
||||
target: 'http://111.3.47.177:48080/admin-api',
|
||||
ws: true,
|
||||
},
|
||||
},
|
||||
|
||||