245 Commits

Author SHA1 Message Date
dap
656d53a059 chore: 锁定依赖 2025-07-22 10:33:02 +08:00
dap
190c8c586e Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-06-30 19:35:24 +08:00
dap
5767af5269 update: 注释 2025-06-30 19:34:11 +08:00
dap
d3f4b936fb fix: withDefaultPlaceholder中placeholder 在keepalive & 语言切换 & tab切换 显示不变的问题 2025-06-30 19:32:23 +08:00
chewenye
b78bc65ce7 feat: 组件json-viewer支持bigint数据显示 cwy (#6377)
Co-authored-by: 车文烨 <chewy@china-lehua.com>
2025-06-29 04:32:30 +08:00
Utopia
b1fb623113 feat: 为 auth layout 添加 slot: logo, 提升组件的灵活性和可复用性 (#6442) 2025-06-27 19:23:24 +08:00
Stephen Chang
de14908fd3 fix(icon-picker): 解决icon-picker组件切换分页后,关键词检索失效问题 (#6437)
当icon-picker组件切换分页后,在输入关键词检索,但是分页没有重置,导致检索结果异常
2025-06-27 19:21:23 +08:00
Li Kui
5c3972196a fix: Add $t import to login expired modal (#6429)
closes #6230
2025-06-27 19:20:25 +08:00
CG.gatspy
3230781538 feat: [vben-tree]增加数据disabled (#6343)
* feat: [vben-tree]增加数据disabled

* Update packages/@core/ui-kit/shadcn-ui/src/ui/tree/tree.vue

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: Jin Mao <50581550+jinmao88@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-06-27 19:09:30 +08:00
broBinChen
e7fd0e3b6a feat(hooks): useHoverToggle的入参refElement支持传入响应式数组 (#6333)
* feat(hooks): useHoverToggle的入参refElement支持传入响应式数组

* feat(hooks): 1、增加 useHoverToggle 中 refElement 参数关于传入响应式数组的注释说明。 2、修改 watch 监听深度,仅需浅层监听 refs 变化。 3、使用 effectScope 管理 useElementHover 实例,避免 refs 变化时事件监听器累积导致的内存泄漏问题

* feat(hooks): 在useHoverToggle中增强 updateHovers  的边界处理,优化watch方案,只监听元素数量变化而不是整个数组变化,避免过度依赖收集

---------

Co-authored-by: xiaobin <xiaobin_chen@fzzixun.com>
2025-06-27 19:08:41 +08:00
dap
fb64d9f87a feat: warm-flow iframe主题切换 2025-06-26 12:05:33 +08:00
dap
c4e3ff14b3 refactor: 移除param的正则 2025-06-26 11:01:22 +08:00
dap
04796449da refactor: 个人中心 账号绑定 样式/逻辑重构(回滚了 既要又要的问题) 2025-06-26 09:37:14 +08:00
dap
6ced4a44c8 refactor: 再调一次就回滚 2025-06-26 09:28:21 +08:00
dap
3ebf0ac7df feat: useVbenForm 增加 Cascader(级联选择器) 组件 2025-06-25 18:50:22 +08:00
dap
47ae02c571 refactor: Windows 10 or Windows Server 2016 太长了 分割一下 2025-06-25 11:50:11 +08:00
dap
f16bfe2cd0 refactor: 在线设备 breakpoint 2025-06-25 11:37:14 +08:00
dap
383756c0aa Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-06-25 11:35:17 +08:00
yuhengshen
2f7d1f009d fix: 全屏状态下弹窗圆角优化 (#6413) 2025-06-24 23:41:54 +08:00
yuhengshen
946f91f387 feat: optimize modal dragging range(#6414)
* 当弹窗指定了容器时,拖拽将被限制在容器范围内
2025-06-24 17:05:59 +08:00
dap
445e6011da chore: sse enable 2025-06-24 14:02:55 +08:00
dap
afc2a3de58 chore: sse close 2025-06-23 21:51:16 +08:00
dap
cb83bca12d refactor: 个人中心 在线设备 样式重构 2025-06-20 14:19:22 +08:00
dap
f7ae821dc2 chore: 锁定lefthook版本 自动升级会导致报错 提交不了代码 2025-06-20 11:34:51 +08:00
dap
b737fa940a refactor: vxe升级后一些过时用法 2025-06-20 11:34:06 +08:00
lghuahua
986eacae9a fix: improve request logic in api-component
* 修复某些情况下updateParam设置的参数可能不会提交给api的问题
* 修复在上一个请求尚未完成前如果params发生了变更,将不会再触发新的api请求
---------

Co-authored-by: Netfan <netfan@foxmail.com>
2025-06-20 08:42:45 +08:00
dap
ce6867994a refactor: 个人中心 账号绑定 样式/逻辑重构 2025-06-18 11:16:26 +08:00
dap
e10ddb421c refactor: 未设置邮箱 2025-06-18 10:49:46 +08:00
dap
af0bb9bd66 refactor: 账号超长显示 2025-06-18 10:45:30 +08:00
dap
aec123a834 refactor: 个人中心 下拉卡片 昵称超长省略显示 2025-06-18 10:38:55 +08:00
dap
c09c089265 refactor: 个人中心 账号绑定 样式/逻辑重构 2025-06-17 21:43:31 +08:00
Netfan
97b8e28a2b docs: fix delete request usage (#6389) 2025-06-17 08:52:59 +08:00
dap
4baa0aed8b refactor: 这是iframe页面! iframe页面! iframe页面! 不是我写的真服了 2025-06-16 11:17:02 +08:00
尘墨
b2d3cf10aa !42 perf: 优化顶部栏头像展开用户信息绑定
Merge pull request !42 from 尘墨/main
2025-06-13 07:22:39 +00:00
dap
63d2b38fd1 refactor: tinymce在modal下全屏 2025-06-12 14:58:15 +08:00
dap
78cd6677c3 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-06-11 21:01:42 +08:00
Netfan
c0962fec18 fix: auto close popup on deactivated (#6368)
* 修复挂载到内容区域的弹窗和抽屉被意外关闭的问题
2025-06-11 12:20:52 +08:00
dap
d38093ca7d refactor: Partial 2025-06-10 10:53:08 +08:00
dap
687c33ec29 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-06-10 10:51:00 +08:00
XiaoHetitu
8ba7bdf2bd fix(button): 为按钮添加type属性防止表单提交意外触发表单验证机制 (#6340)
在按钮组件中,按钮元素缺少type="button"属性可能导致在表单中意外提交。添加此属性以确保按钮行为符合预期。

Co-authored-by: yuanwj <ywj6792341@qq.com>
2025-06-08 17:56:24 +08:00
zyy
b015fbc9fc fix: [adapter] 表格配置类型报错 (#6327)
配置toolbarConfig中的search时会有类型报错
2025-06-08 17:53:55 +08:00
broBinChen
b69320c070 feat(hooks): support separate enter/leave delays in useHoverToggle (#6325)
Co-authored-by: xiaobin <xiaobin_chen@fzzixun.com>
2025-06-08 17:53:29 +08:00
zhang
dcccc213ce fix: requestClient.upload会将vbenform中value为undefined的值转为字符串undefined’提… (#6300)
* fix: requestClient.upload会将vbenform中value为undefined的值转为字符串undefined’提交给后台保存

* fix: requestClient.upload会将vbenform中value为undefined的值转为字符串'undefined’提交给后台保存
2025-06-08 17:51:16 +08:00
ali-pay
c0e601c020 fix: menu type is not 'button' (#6277)
Co-authored-by: Jin Mao <50581550+jinmao88@users.noreply.github.com>
2025-06-08 17:50:44 +08:00
RanMaoting
017ed1a9e1 types: 为useVbenVxeGrid添加泛型声明,使grid实例上能正确获取到行数据的类型 (#5653)
Co-authored-by: Jin Mao <50581550+jinmao88@users.noreply.github.com>
2025-06-08 17:43:02 +08:00
dap
598f371568 refactor: 代码生成 导入 hover样式 2025-06-08 12:53:05 +08:00
dap
ca2aadaf4a refactor: 租户套餐 新增 过滤租户相关菜单 2025-06-06 12:02:56 +08:00
dap
616db1c127 docs: changelog 2025-06-05 21:35:59 +08:00
dap
08de1a6f19 refactor: 代码生成 字典下拉加载改为每次都重新加载 2025-06-05 21:33:53 +08:00
dap
006370798b refactor: tinymce+表单 校验失败样式 2025-06-05 21:18:36 +08:00
dap
831700660c refactor: 角色去掉moredropdown 改为平铺 2025-06-04 21:53:11 +08:00
dap
a53b9382f5 refactor: 排序 默认值 2025-06-04 19:49:03 +08:00
dap
703586123a refactor: 字典接口抛出异常(为什么会抛出异常?)无限调用接口 兼容处理 2025-06-04 19:06:18 +08:00
dap
0295418f79 fix: 菜单管理 路由地址的必填项不生效 2025-06-04 17:53:09 +08:00
dap
14b0d9b50f Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-06-04 09:18:54 +08:00
vben
b9aef618fe chore: release 5.5.7 2025-06-04 05:33:06 +08:00
Netfan
4102cc2211 feat: improve vbenCheckButtonGroup (#6329)
* 按钮组支持单选清除和多选限制最大选项数

* 按钮组支持icon插槽来定制图标
2025-06-03 23:11:56 +08:00
chewenye
ea776aa710 types: 扩展user-dropdown组件的menus类型,支持iconify (#6283)
Co-authored-by: 车文烨 <chewy@china-lehua.com>
2025-06-03 06:07:06 +08:00
huanghezhen
feb96dc8ea fix: resolve onClosed method failure in connectedComponent of useVbenModal (#6309) 2025-06-02 08:16:48 +08:00
wyc001122
470fd43b49 fix: 修复使用useVbenVxeGrid配置hasEmptyText、hasEmptyRender不生效的问题 (#6310) 2025-06-02 08:16:26 +08:00
zhang
76d106e474 fix: When defaultHomePage is inconsistent with user.homePath, the pa… (#6299)
* fix:  When defaultHomePage is inconsistent with user.homePath, the page refresh route jump will be abnormal

* fix:  When defaultHomePage is inconsistent with user.homePath, the page refresh route jump will be abnormal
2025-06-02 08:07:06 +08:00
wyc001122
78c3c9da6f docs(settings): 完善'生产环境动态配置'步骤 (#6297) 2025-06-02 08:05:23 +08:00
dap
8b7d717b21 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-05-31 18:53:41 +08:00
Netfan
081d08a7f8 fix: alert width fixed in small screen (#6312) 2025-05-30 19:54:26 +08:00
dap
0da75418d0 refactor: 菜单管理 刷新接口后 记录展开行的情况 (改为vxe配置) 2025-05-30 17:51:06 +08:00
dap
55f0da3085 refactor: tree-search 2025-05-30 14:24:40 +08:00
dap
3849800388 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-05-30 09:20:33 +08:00
dap
f913955259 docs: changelog 2025-05-29 19:45:38 +08:00
dap
8d6ef40d3e refactor: 适配新工作流预览 由logicflow改为iframe 真服了 2025-05-29 19:30:57 +08:00
dap
2141c93399 docs: readme 2025-05-29 11:17:29 +08:00
dap
906502f49b refactor: 字典项列表样式重构(下个版本再介入) 2025-05-29 11:10:46 +08:00
Netfan
96a10ca83f style: fix lint error (#6298) 2025-05-28 19:23:21 +08:00
dap
1f68fd31b7 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-05-28 17:13:50 +08:00
wyc001122
f31360ba4e feat: support for hybrid permission access control mode (#6294)
* feat: 添加混合权限访问控制模式

* feat: 文档补充
2025-05-28 17:01:58 +08:00
wyc001122
4eb16d6d3a fix: fix table-title slot not work (#6295) 2025-05-28 17:01:11 +08:00
dap
9db6ade1ed fix: oss配置是否默认取值 2025-05-27 19:35:22 +08:00
dap
2569e1da0d refactor: 字典项布局重构(未完成) 2025-05-27 14:27:01 +08:00
dap
2de9cd2334 docs: changelog 2025-05-27 11:52:41 +08:00
dap
739e04816a refactor: 只有超管才能对菜单进行增删改操作 2025-05-27 11:47:23 +08:00
dap
d9c57dfb61 feat: 流程预览dot画布+夜间背景色 2025-05-26 17:26:13 +08:00
dap
2217c96cd9 chore: ele版本会使用这个文件 只是为了不报错未找到对应组件才新建的这个文件 无实际意义 2025-05-26 17:08:15 +08:00
dap
752c1ac3ed refactor: 字典项布局重构(未完成) 2025-05-26 17:05:52 +08:00
liqiang0330
53304514b6 fix: Update index.ts (#6268)
* Update index.ts

VxeGridPropTypes.原文件缺少这个,现在补全!

* Update index.ts

增加空格!
2025-05-26 13:29:27 +08:00
dap
cf913f8b8d docs: changelog 2025-05-26 13:27:42 +08:00
dap
ad9c465622 update: remove unused file 2025-05-26 13:25:44 +08:00
dap
bea7c1a094 refactor: preserveTreeTableState 2025-05-26 09:28:54 +08:00
dap
95859e36a2 update: menuName i18n 2025-05-26 09:26:34 +08:00
dap
8bfc482a7f feat: logicflow显示流程图 2025-05-25 16:38:13 +08:00
Netfan
6fbf1387f5 fix: reset slider-captcha after login failed (#6275) 2025-05-25 16:04:56 +08:00
dap
c3edbec3f0 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-05-25 11:48:58 +08:00
Netfan
e5c937396d fix: json-bigint parse used in vxeTable (#6271)
* 修复vxeTable不能加载json-bigint解析的数据的问题
2025-05-24 13:01:58 +08:00
dap
45de9b7547 docs: changelog 2025-05-24 12:46:50 +08:00
dap
6daedd1de5 feat: 菜单级联删除 2025-05-24 12:45:18 +08:00
dap
c45eed90d9 fix: dept list 列宽调整 2025-05-24 11:59:39 +08:00
dap
845719d951 chore: version update 2025-05-23 16:29:49 +08:00
dap
4ef974ca4e feat: 跳转菜单图标更新 2025-05-23 16:07:17 +08:00
littlesparklet
af186f878d fix: repair the unexpected form default value (#5567)
* fix: Fix inconsistent spacing around search form (issue #5429)

* fix: repair the unexpected default value in validated form.(issue #5451)

* Update packages/@core/ui-kit/form-ui/src/use-form-context.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: Jin Mao <50581550+jinmao88@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-05-23 16:05:11 +08:00
dap
2dc7e564b2 feat: 升级需要执行sql的提示 2025-05-23 15:55:25 +08:00
wyc001122
97894a940e feat: optimize logo display (#6267)
* feat(VbenAvatar): add fit property to VbenAvatar component

* feat(VbenLogo): add fit property to VbenLogo component

* feat(VbenLogo): add logo fit preference configuration

- Add preferences.logo.fit setting for logo display control
- Include corresponding documentation for the new preference

* feat(preferences): add default value for logo.fit preference

- Set default configuration for logo fit behavior
- Ensures consistent logo display across applications

* test(preferences): update configuration snapshots

---------

Co-authored-by: wyc001122 <wangyongchao@testor.com.cn>
2025-05-23 15:24:01 +08:00
yingzi2019
48d70182b4 feat: improve check updates (#6257)
Co-authored-by: monkey <maotao@tutamail.com>
2025-05-23 15:23:06 +08:00
Netfan
a1091bad46 feat: enhances compatibility with APIs returning large numeric values (#6250) 2025-05-23 15:22:18 +08:00
zhang
9f9be21e2a fix: component Input is not registered when initialize page (#6246)
* fix: Component Input is not registered when initialize page

* fix: Component Input is not registered when initialize page
2025-05-23 15:21:09 +08:00
panda7
a2bdcd6e49 feat: ellipsis text automatically displays tooltip based on ellipsis (#6244)
* feat: ellipsis text automatically displays tooltip based on ellipsis

* feat: ellipsis text automatically displays tooltip based on ellipsis

---------

Co-authored-by: sqchen <9110848@qq.com>
Co-authored-by: sqchen <chenshiqi@sshlx.com>
2025-05-23 15:20:38 +08:00
dap
a38f2de982 refactor: 所有本地路由(除个人中心/workflow-iframe)改为从后端返回 适配 2025-05-23 14:30:12 +08:00
dap
d039c53053 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-05-22 09:20:32 +08:00
ali-pay
11b2b5bcc2 fix: 修复菜单管理中按钮类型值错误的问题 (#6255) 2025-05-22 09:09:31 +08:00
LinaBell
ebef2c91e2 fix: tab cannot be displayed correctly after browser refresh (#6256) 2025-05-22 09:04:40 +08:00
Netfan
0c3edb10b0 fix: getFieldComponentRef will return actual ref within AsyncComponentWrapper (#6252)
修复异步加载组件时,表单的getFieldComponentRef方法没能获取到正确的组件实例
2025-05-21 14:48:51 +08:00
dap
801514dbe3 feat: 支持pdf预览(原生 浏览器接管) 2025-05-21 11:54:55 +08:00
dap
2dce7718d6 refactor: fallbackImageBase64 2025-05-20 21:16:43 +08:00
dap
8a8e090792 Merge branch 'dev' of https://gitee.com/dapppp/ruoyi-plus-vben5 into dev 2025-05-20 12:56:14 +08:00
dap
6fc2c4e3cc Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-05-20 12:56:05 +08:00
wyc001122
8ac97688da fix(preferences): 更新内容内边距默认值 (#6233)
Co-authored-by: wyc001122 <wangyongchao@testor.com.cn>
2025-05-20 09:50:23 +08:00
陈绍华
c89ec0088b style: 更改OSS配置的状态方法添加 TS 类型
Signed-off-by: 陈绍华 <marlboro027@foxmail.com>
2025-05-20 01:40:04 +00:00
dap
0a076f5e6e fix: 更新后的padding设置为0 2025-05-19 21:52:16 +08:00
dap
4fd68bc083 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-05-19 21:33:49 +08:00
李轻舟
2efacb3e5b docs: Update build.md (#6228) 2025-05-19 16:30:39 +08:00
wyc001122
dae46abb71 feat: additional-settings (#6225)
* feat(preferences): 补充VbenAdminLayout传入属性(来自偏好设置)

* docs(@vben/docs):update settings doc

---------

Co-authored-by: wyc001122 <wangyongchao@testor.com.cn>
2025-05-19 16:29:15 +08:00
wyc001122
5ee2a74e2d fix(use-design-tokens): 完善element-plus暗色主题颜色 (#6224)
Co-authored-by: wyc001122 <wangyongchao@testor.com.cn>
2025-05-19 16:27:34 +08:00
dap
79d89005b6 对错误用法的提示(完全无奈...) 2025-05-18 14:28:57 +08:00
afe1
d0b8349a2d perf: stub unbuild params (#6210) 2025-05-18 10:35:20 +08:00
wyc001122
34c4ecb047 fix: in mixed layout mode, the sidebar does not display when the first child node is an external link (#6219)
Co-authored-by: wyc001122 <wangyongchao@testor.com.cn>
2025-05-18 10:34:41 +08:00
ming4762
3d9dba965f perf: perf the control logic of Tab (#6220)
* perf: perf the control logic of Tab

* 每个标签页Tab使用唯一的key来控制关闭打开等逻辑
* 统一函数获取tab的key
* 通过3种方式设置tab key:1、使用router query参数pageKey 2、使用路由meta参数fullPathKey设置使用fullPath或path作为key
* 单个路由可以打开多个标签页
* 如果设置fullPathKey为false,则query变更不会打开新的标签(这很实用)

* perf: perf the control logic of Tab

* perf: perf the control logic of Tab

* 测试用例适配

* perf: perf the control logic of Tab

* 解决AI提示的警告
2025-05-18 10:33:02 +08:00
dap
96b8ae94fd feat: 保存表格滚动/展开状态并执行回调 用于树表在执行 新增/编辑/删除等操作后 依然在当前位置(体验优化) 2025-05-16 16:48:22 +08:00
wyc001122
024c01d350 fix(@vben-core/shadcn-ui): fix disabled functionality not working in VbenTree component (#6205)
* fix(@vben-core/shadcn-ui): fix disabled functionality not working in VbenTree component

* fix(@vben-core/shadcn-ui): add cursor-not-allowed className when disabled and disable onfocus

---------

Co-authored-by: wyc001122 <wangyongchao@testor.com.cn>
Co-authored-by: Jin Mao <50581550+jinmao88@users.noreply.github.com>
2025-05-16 14:13:43 +08:00
dap
10b8b81954 docs: version update 2025-05-16 10:06:51 +08:00
afe1
2adb8acd80 fix: css style (#6176) 2025-05-16 09:40:40 +08:00
panda7
a23bc4cb5c fix: the mobile terminal can wrap lines and expand slot attributes (#6165)
Co-authored-by: sqchen <chenshiqi@sshlx.com>
2025-05-16 09:40:05 +08:00
XiaoHetitu
cf17a45d8d feat(tabs): 支持计算属性作为标签标题,解决 #6170 的问题 (#6163)
* feat(tabs): 支持动态函数作为标签标题

修改 `setTabTitle` 和 `tabsView` 逻辑,允许传入函数作为标签标题,以便动态生成标题内容

* feat(tabbar): 添加动态设置标签页标题功能

允许设置静态字符串或动态函数作为标签标题,支持根据状态或语言变化动态更新标题

* refactor(tabs): 移除冗余的newTabTitle2变量并优化标题设置逻辑

移除tabs组件中冗余的newTabTitle2变量,直接使用newTabTitle作为标题来源。同时,优化use-tabs和tabbar模块的标题设置逻辑,支持ComputedRef作为动态标题,提升代码简洁性和可维护性。

---------

Co-authored-by: yuanwj <ywj6792341@qq.com>
2025-05-16 09:37:50 +08:00
chewenye
b46ebe756e types: 导出authentication组件的type,自定义toolbarList时类型使用ToolbarType (#6158)
Co-authored-by: 车文烨 <chewy@china-lehua.com>
2025-05-16 09:36:49 +08:00
哦是吗
1f50c95c66 update apps/web-antd/src/components/upload/src/hook.ts.
fix: 上传组件清空绑定值时,同时清空innerFileList,避免外部使用时还能读取到

Signed-off-by: 哦是吗 <1733179386@qq.com>
2025-05-15 07:38:02 +00:00
zhangl1438
cd4706b717 fix:修复切换默认oss config后,上传文件报错:“文件存储服务类型无法找到” 2025-05-14 11:45:43 +08:00
dap
769aceb55f Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-05-13 11:32:35 +08:00
Netfan
e89cf400c0 fix: refresh command of tabbar issue, fixed: #6162 (#6169) 2025-05-12 23:34:08 +08:00
anyup
9e67929ee7 feat: support to refresh the tab page by route name (#6153)
Co-authored-by: anyup <anyupxing@163.com>
2025-05-10 22:33:31 +08:00
dap
7926865bf9 docs: version update 2025-05-09 11:12:54 +08:00
dap
51fbfcedd2 fix: 某些带Vxe表格弹窗 关闭后没有正常清理表格数据的问题 2025-05-09 11:12:24 +08:00
dap
8f71d6a5d9 docs: changelog and version update 2025-05-09 10:08:48 +08:00
afe1
90625782c0 fix: delete useless code (#6143) 2025-05-08 16:51:12 +08:00
dap
12d0ba24e5 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-05-08 09:28:52 +08:00
dap
540f24ed43 Revert "fix: 更新表格增加minWidth属性"
This reverts commit b52f3ba0c5.
2025-05-08 09:26:03 +08:00
Yann
c57d3f32b5 Merge remote-tracking branch 'origin/dev' 2025-05-07 22:53:51 +08:00
Yann
b52f3ba0c5 fix: 更新表格增加minWidth属性 2025-05-07 22:52:13 +08:00
wyc001122
84ef207d9c docs(@vben/docs): update settings doc (#6128)
Co-authored-by: wyc001122 <wangyongchao@testor.com.cn>
2025-05-07 12:04:48 +08:00
zyf0624
e68fff58e8 fix: tsconfig moduleResolution (#6122)
Co-authored-by: pzzyf <2279948211@qq.com>
2025-05-07 12:04:15 +08:00
dap
63c06e02b2 fix: 修改手机号验证码长度 2025-05-07 10:31:45 +08:00
Netfan
bf70539221 fix: missing argument for getPopupContainer 2025-05-06 22:48:03 +08:00
Leeson
5949c73a30 fix: delete Popconfirm being obscured by fixed columns (#6118)
* fix: delete Popconfirm being obscured by fixed columns

* fix: opened popConfirm will prevent the table from scrolling

---------

Co-authored-by: Netfan <netfan@foxmail.com>
2025-05-06 22:33:17 +08:00
vben
cc6c9bf7a0 chore: release v5.5.6 2025-05-06 22:32:58 +08:00
Jin Mao
6b1aab9c67 fix: handle undefined children in generate-menus (#6117)
When children is undefined, use empty array as fallback to prevent potential runtime errors. This matches the behavior when hideChildrenInMenu is true.
2025-05-06 14:29:50 +08:00
LinaBell
8f4d3d418d fix: when keepAlive is enabled, returning directly through browser buttons/gestures will not close pop ups (#6113) 2025-05-06 14:02:23 +08:00
dap
aa086a2800 refactor: replace defaultHomePath 2025-05-06 09:41:11 +08:00
ming4762
3b3f8e4e44 fix: fix IconPicker props warning (#6108)
Invalid prop: type check failed for prop "onUpdate:value". Expected Function, got Array
2025-05-06 09:30:37 +08:00
dap
b0763d6429 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-05-04 17:23:32 +08:00
vben
f94ca10adf chore: remove prepare script from package.json 2025-05-04 07:33:36 +08:00
vben
4471bc7a5d chore: update prepare script in package.json to remove lefthook installation 2025-05-04 00:05:29 +08:00
Vben
5689ac60ff feat(project): migrate from husky and lint-staged to lefthook (#6104) 2025-05-03 19:43:12 +08:00
Vben
045bc4e5ee feat: support smooth auto-scroll to active menu item (#6102) 2025-05-03 18:05:26 +08:00
Vben
17a18fc9ba chore: close eslint object sorting (#6101) 2025-05-03 16:06:36 +08:00
aonoa
41152d1722 refactor: modify the default homepage path loaded from the preference… (#6099)
* refactor: modify the default homepage path loaded from the preferences.ts

Signed-off-by: aonoa <1991849113@qq.com>

* refactor: modify the default homepage path loaded from the preferences.ts

Signed-off-by: aonoa <1991849113@qq.com>

---------

Signed-off-by: aonoa <1991849113@qq.com>
2025-05-03 16:03:08 +08:00
Netfan
f1af9f8f6e fix: add triggerClass binding to PopoverTrigger and update icon-picker styles (#6095)
* Popover支持设置trigger的样式
* 修正icon-picker的input值更新
2025-05-01 21:40:45 +08:00
Netfan
0517a7014f fix: add missing translation for preferences drawer (#6094) 2025-05-01 20:08:44 +08:00
Netfan
3e6d608a2f fix: destroyOnClose incorrect default value, fixed #6092 (#6093) 2025-05-01 14:09:37 +08:00
ming4762
5de954baa4 fix: fix LoginExpiredModal in some cases, message may be obscured (#6086) 2025-05-01 10:40:42 +08:00
Netfan
add1e61b6f fix: show validation message as tooltip in compact form (#6087)
* 紧凑模式表单的校验消息将显示为一个tooltip
2025-04-30 23:41:44 +08:00
dap
9f978cc9b0 fix: 用来标识是否为上传 这样在watch内部不需要请求api 2025-04-30 14:13:35 +08:00
dap
89dd4b8131 chore: version update 2025-04-30 11:26:57 +08:00
dap
a10a981fab Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-04-30 11:13:25 +08:00
Jin Mao
20c15f352f perf: page componet supports custom height offset for flexible content height … (#6081)
* perf: Page supports custom height offset for flexible content height control.

允许通过 height 属性调整页面内容高度计算。修改了 Page 组件以支持自定义高度偏移量,用于更灵活的内容高度控制。

* chore: typo

* perf(page): replace height with heightOffset for flexible content sizing

The `height` prop was replaced with `heightOffset` to better describe its purpose when used with `autoContentHeight`. The new prop allows custom offset values (in pixels) to adjust content area sizing, with clearer documentation.
2025-04-29 18:15:12 +08:00
Netfan
8aa7dabeff fix: calculation for collapsing search form is incorrect while initially hidden (#6068)
* 修复当默认隐藏搜索表单时,折叠位置的计算不正确的问题
2025-04-28 23:20:33 +08:00
vben
78c7c1589a chore: update readme.md 2025-04-28 23:11:34 +08:00
Vben
dd833ca56b chore: update dependencies and documentation, optimize build toolchain (#6060)
* chore: update packageManager version to pnpm@10.9.0 for compatibility improvements

* chore: Update dependent versions and configurations to improve compatibility and stability

- Update Node version to 22.1.0
- Updated pnpm version to 10.10.0
- Fixed syntax error in prettier command in lintstagedrc
- Update dependent versions in pnpm-lock.yaml to ensure consistency
- Update format and content in README documents to improve readability

* fix: lint error
2025-04-28 23:08:05 +08:00
vem
681c1dc267 fix: Update existing route index to prevent 404 on user switch (#6003)
Co-authored-by: tars-macmini <vem@qq.com>
2025-04-28 18:19:47 +08:00
Netfan
4545422ee0 fix: lock state will not change overflow style in drawer and modal (#6067)
* Modal和Drawer的锁定状态不再修改overflow样式
2025-04-28 17:02:54 +08:00
dap
5f26f5662e Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-04-28 13:19:57 +08:00
Gahotx
ca94ca906f fix: add rounded corners to project and quick nav items (#5296) 2025-04-27 22:50:42 +08:00
Vben
76de450c71 chore: update dependency version for improved stability and compatibility (#6023)
* chore: update dependency version for improved stability and compatibility

* fix: optimize clearPoints function in useCaptchaPoints hook to improve performance

* fix: make several props optional in various components for better flexibility
2025-04-27 22:06:49 +08:00
Trivikram Kamat
dd2b1ed580 fix: install corepack from npm (#5905)
* fix: install corepack from npm

* docs: install corepack from npm
2025-04-27 22:03:35 +08:00
ming4762
baec89f896 perf: resolve duplicate component names (#6039) 2025-04-27 22:02:38 +08:00
vben
7c7051a11e chore: release v5.5.5 2025-04-27 21:45:10 +08:00
Netfan
aa27a2f7a1 feat: encrypt the privacy data when it is persisted (#6056)
* 对私密数据持久化时执行加密
* 将锁屏密码合并到accessStore中进行加密
2025-04-27 20:59:10 +08:00
Jin Mao
9ee6d06d50 docs: add deepWiki doc link (#6057) 2025-04-27 20:54:07 +08:00
ming4762
0cc1cb5a7b perf: improve destroyOnClose for VbenDrawer&VbenModal (#6051)
* fix: fix that the default value of modal destroyOnClose does not take effect

* perf: improve destroyOnClose for VbenDrawer
2025-04-27 11:26:50 +08:00
dap
e662681ce2 fix: 拖拽上传在单文件时的样式 2025-04-27 09:57:16 +08:00
Netfan
0a9fc4e02d fix: title of search button in vxeTable toolbar (#6046)
* 修改vxeTable工具栏里的搜索按钮的提示文案
2025-04-26 01:08:41 +08:00
Netfan
be840460d8 feat: vbenSelect support prop allowClear (#6043) 2025-04-25 23:37:03 +08:00
Netfan
cb45987fe2 docs: update example (#6036)
* 跟进后端菜单逻辑的修改,现已无需传递basicLayout布局
2025-04-25 11:44:47 +08:00
panda7
5ffd7db8e0 fix: the initial value echo for the check-button-group (#6029)
Co-authored-by: sqchen <9110848@qq.com>
2025-04-25 08:35:03 +08:00
Netfan
14377705e7 fix: alert confirm state in beforeClose callback (#6019) 2025-04-23 12:20:52 +08:00
dap
23503778d4 chore: 去除vxe的锁定 2025-04-22 17:56:22 +08:00
dap
f54fab0bae Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-04-22 17:55:44 +08:00
pangyajun123
b985ff0584 fix: vxe-table theme token follow primary color (#6007) 2025-04-21 19:15:05 +08:00
dap
eff2f2a0b1 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-04-20 09:14:37 +08:00
dap
664fa800cd chore: 搜定vxe版本 2025-04-20 09:13:08 +08:00
dap
5dc4448c01 docs: changelog 2025-04-20 09:06:32 +08:00
dap
ccfe992779 chore: 暂时锁定vxe版本(样式问题) 2025-04-20 09:05:57 +08:00
dap
583504495d feat: 对模板的说明... 2025-04-20 08:53:50 +08:00
dap
7fb4bf3431 fix: 工作流list展示在开启缩放会有误差导致触底逻辑不会触发 2025-04-20 08:42:19 +08:00
wyc001122
b148b8ec92 fix: fix geader menu activation path (#5997)
Co-authored-by: 王泳超 <wangyongchao@testor.com.cn>
2025-04-19 14:35:33 +08:00
Netfan
79de6bcbf7 fix: alert send wrong confirm state to beforeClose (#5991)
* 修复alert在按下Esc或者点击遮罩关闭时,可能发送错误的isConfirm状态
2025-04-17 22:23:05 +08:00
Netfan
14bd6dd25d fix: destroyOnClose works within connectedComponent (#5989)
* 修复destroyOnClose没能销毁connectedComponent自身的问题
2025-04-17 20:25:49 +08:00
dap
9b577261e2 chore: vite6.3.1已经修复开发/打包问题 解除版本锁定 2025-04-17 14:21:47 +08:00
PIPEDREA_WZJ
7f269e0d69 Update tailwindcss.md (#5602)
tailwindcss最新的版本已经是v4.x,vben中使用的是3.x的tailwindcss。在未进行兼容前,会出现运行失败的问题
2025-04-17 14:01:39 +08:00
yuh
4baec83db5 feat: add examples: form-upload (#5955)
* feat: add examples: form-upload

* fix: upload: accept and label

* fix: upload: 设置表单值、图片预览
2025-04-17 14:00:46 +08:00
dap
7d8416890b docs: changelog 2025-04-16 21:53:29 +08:00
dap
2e2ffcd59e Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-04-16 21:33:11 +08:00
dap
2046bfa846 chore: 暂时锁定vite版本 会导致i18n插件打包失败 2025-04-16 19:57:07 +08:00
dap
0446adf778 refactor: 菜单图标更新 2025-04-16 17:38:11 +08:00
Netfan
f7a4d13a4c fix: fixed arguments of callbacks in formApi (#5970)
* 修复 `handleValuesChange` 传递的参数不是处理后的表单值的问题

* 修复 `handleReset` 未能传递正确参数的问题
2025-04-16 14:11:04 +08:00
dap
e587256425 update: placeholder update 2025-04-16 13:53:58 +08:00
Netfan
0936861da1 feat: pass fieldsChanged into the handleValuesChange callback function (#5968)
* fieldsChanged(已被改变值的字段名)将传入handleValuesChange回调函数
2025-04-16 11:29:01 +08:00
ming4762
3318d76bab perf: improve destroyOnClose for VbenModal (#5964) 2025-04-16 11:28:36 +08:00
LinaBell
8f3881eabf perf: beforeClose of drawer support promise (#5932)
* perf: the beforeClose function of drawer is consistent with that of modal

* refactor: drawer test update
2025-04-16 11:27:13 +08:00
zhouda1fu
5252480b09 fix: missing await in department form(#5967) 2025-04-16 11:22:59 +08:00
dap
f096dfc6e6 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-04-16 10:09:48 +08:00
Netfan
d18f56177c docs: update alert and apiComponent docs (#5961) 2025-04-15 20:52:23 +08:00
wyc001122
333998b518 fix: determine if scrollbar has been totally scrolled (#5934)
* 修复在系统屏幕缩放比例不为100%的情况下,滚动组件对是否已滚动到边界的判断可能不正确的问题
2025-04-15 20:51:38 +08:00
ming4762
3fb4fba1cb fix: modal closing animation (#5960) 2025-04-15 18:49:57 +08:00
ming4762
c7e6210c8d feat: modal&drawer support center-footer slot (#5956) 2025-04-15 16:04:44 +08:00
lztb
d864085c13 feat: vben-form添加arrayToStringFields属性 (#5957)
* feat: vben-form添加arrayToStringFields属性

* feat: 修改handleArrayToStringFields和handleStringToArrayFields中嵌套数组格式的处理不一致

---------

Co-authored-by: 米山 <17726957223@189.cn>
2025-04-15 16:03:20 +08:00
Netfan
fcdc1a1602 feat: add more expose methods for apiComponent (#5958)
* 为ApiComponent组件添加getOptions和getValue导出方法。
2025-04-15 15:32:30 +08:00
Netfan
bf7496f0d5 feat: add useAlertContext for Alert component (#5947)
* 新增Alert的子组件中获取弹窗上下文的能力
2025-04-15 00:00:05 +08:00
Netfan
9700150653 fix: table actions in fixed column (#5945) 2025-04-14 19:56:52 +08:00
Netfan
f0e9e55af2 feat: alert support customize footer (#5940)
* Alert组件支持自定义footer
2025-04-14 11:48:21 +08:00
Netfan
ff88274554 fix: long navigation menu can be scrolled (#5939)
* 修复超长的导航菜单无法纵向滚动的问题
2025-04-14 11:18:33 +08:00
ming4762
afce9dc5c0 perf: improve destroyOnClose for VbenModal (#5935)
* perf: 优化Vben Modal destroyOnClose,解决destroyOnClose=false,Modal依旧会被销毁的问题

影响范围(重要):destroyOnClose默认为true,这会导致所有的modal都会默认渲染到body
radix-vue Dialog组件默认会销毁挂载的组件,所以即使destroyOnClose=false,Modal依旧会被销毁的问题
对于一些大表单重复渲染导致卡顿,ApiComponent也会频繁的加载数据

* fix: modal closing animation

---------

Co-authored-by: Netfan <netfan@foxmail.com>
2025-04-13 23:02:07 +08:00
ming4762
b5700bd0b1 perf: improve autoSelect of ApiComponent (#5936)
* fix: 修复autoSelect不生效的问题,props.valueField已经被omit了

* feat: ApiComponent autoSelect支持使用函数,可以满足灵活性要求更高的场景
2025-04-13 20:03:18 +08:00
dap
e085083e42 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-04-12 22:28:23 +08:00
dap
a47910f650 refactor: 所有表格操作列宽度调整为'auto', 这样会根据子元素宽度适配(比如没有分配权限的情况) 2025-04-12 15:01:28 +08:00
Netfan
a8c4786311 feat: api-component support autoSelect prop (#5931)
* feat: api-component support autoSelect prop

* docs: add version requirement
2025-04-12 14:02:35 +08:00
Netfan
2971ccc0b7 docs: docs modal z-index fixed, update alert docs (#5930) 2025-04-12 13:41:40 +08:00
dap
4ead56eaf1 fix: onClosed 2025-04-12 10:44:53 +08:00
dap
4fad8d77de refactor: 角色管理 auto 2025-04-12 10:42:16 +08:00
dap
9db1087d32 update: 岗位 useBeforeCloseDiff 2025-04-12 10:38:14 +08:00
Netfan
4a2c7b313f fix: alert animation (#5927) 2025-04-12 10:37:47 +08:00
dap
0f5fc5f54c fix: onClosed 2025-04-12 10:34:44 +08:00
dap
76108e7b8f refactor: 宽度设置为auto(根据子元素宽度动态变化) 2025-04-12 10:31:43 +08:00
dap
6018817906 chore: version动态获取 2025-04-12 10:20:12 +08:00
dap
7e4bdf7bd6 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-04-12 09:56:51 +08:00
dap
32117574f6 chore: version update 2025-04-12 09:54:50 +08:00
dap
a48dfa1de2 fix: 新增dictType不显示 2025-04-12 09:44:27 +08:00
Netfan
36bf6fc149 fix: builtin color change throttled in preference drawer (#5924)
修复偏好设置中的自定义主题色拖动选择颜色时页面会明显卡顿的问题
2025-04-12 01:44:08 +08:00
Netfan
f46ec30995 fix: theme mode follow the system only auto (#5923)
* 修复主题在未设置为auto时,仍然会跟随系统主题变化的问题。
2025-04-12 01:16:57 +08:00
Netfan
9bd5a190c2 fix: alert action button focus, fixed #5921 (#5922)
* 修复Alert组件的按钮焦点切换问题
2025-04-12 00:59:56 +08:00
zhang
86da3cedc2 chore: 导出框架自带的组件,方便独立页面使用 (#5876) 2025-04-09 16:16:56 +08:00
340 changed files with 5417 additions and 1817 deletions

View File

@@ -2,5 +2,5 @@ ports:
- port: 5555
onOpen: open-preview
tasks:
- init: corepack enable && pnpm install
- init: npm i -g corepack && pnpm install
command: pnpm run dev:play

View File

@@ -1,6 +0,0 @@
echo Start running commit-msg hook...
# Check whether the git commit information is standardized
pnpm exec commitlint --edit "$1"
echo Run commit-msg hook done.

View File

@@ -1,3 +0,0 @@
# 每次 git pull 之后, 安装依赖
pnpm install

View File

@@ -1,7 +0,0 @@
# update `.vscode/vben-admin.code-workspace` file
pnpm vsh code-workspace --auto-commit
# Format and submit code according to lintstagedrc.js configuration
pnpm exec lint-staged
echo Run pre-commit hook done.

View File

@@ -1,20 +0,0 @@
export default {
'*.md': ['prettier --cache --ignore-unknown --write'],
'*.vue': [
'prettier --write',
'eslint --cache --fix',
'stylelint --fix --allow-empty-input',
],
'*.{js,jsx,ts,tsx}': [
'prettier --cache --ignore-unknown --write',
'eslint --cache --fix',
],
'*.{scss,less,styl,html,vue,css}': [
'prettier --cache --ignore-unknown --write',
'stylelint --fix --allow-empty-input',
],
'package.json': ['prettier --cache --write'],
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [
'prettier --cache --write--parser json',
],
};

View File

@@ -1 +1 @@
20.14.0
22.1.0

2
.npmrc
View File

@@ -1,5 +1,5 @@
registry = "https://registry.npmmirror.com"
public-hoist-pattern[]=husky
public-hoist-pattern[]=lefthook
public-hoist-pattern[]=eslint
public-hoist-pattern[]=prettier
public-hoist-pattern[]=prettier-plugin-tailwindcss

View File

@@ -14,7 +14,7 @@
"editor.tabSize": 2,
"editor.detectIndentation": false,
"editor.cursorBlinking": "expand",
"editor.largeFileOptimizations": false,
"editor.largeFileOptimizations": true,
"editor.accessibilitySupport": "off",
"editor.cursorSmoothCaretAnimation": "on",
"editor.guides.bracketPairs": "active",
@@ -91,6 +91,7 @@
"**/bower_components": true,
"**/.turbo": true,
"**/.idea": true,
"**/.vitepress": true,
"**/tmp": true,
"**/.git": true,
"**/.svn": true,
@@ -113,6 +114,8 @@
"**/yarn.lock": true
},
"typescript.tsserver.exclude": ["**/node_modules", "**/dist", "**/.turbo"],
// search
"search.searchEditor.singleClickBehaviour": "peekDefinition",
"search.followSymlinks": false,
@@ -217,7 +220,7 @@
"*.env": "$(capture).env.*",
"README.md": "README*,CHANGELOG*,LICENSE,CNAME",
"package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json",
"eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json",
"eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json,lefthook.yml",
"tailwind.config.mjs": "postcss.*"
},
"commentTranslate.hover.enabled": false,
@@ -232,12 +235,14 @@
"cSpell.words": [
"archiver",
"axios",
"Cascader",
"dotenv",
"isequal",
"jspm",
"napi",
"nolebase",
"rollup",
"tinymce",
"vitest"
]
}

View File

@@ -1,3 +1,80 @@
# 1.4.1
**FEATURES**
- Tinymce添加在antd原生表单/useVbenForm下的校验样式
- useVbenForm 增加 Cascader(级联选择器) 组件
**BUG FIX**
- 菜单管理 路由地址的必填项不生效
- withDefaultPlaceholder中placeholder 在keepalive & 语言切换 & tab切换 显示不变的问题
**REFACTOR**
- 字典接口抛出异常(为什么会抛出异常?)无限调用接口 兼容处理
- 代码生成 字典下拉加载 改为每次进入编辑页面都加载
- ~~个人中心 账号绑定 样式/逻辑重构~~(回滚了 既要又要的问题)
- ~~个人中心 下拉卡片 昵称超长省略显示~~(回滚了 既要又要的问题)
# 1.4.0
**FEATURES**
- 菜单管理(通用方法) 保存表格滚动/展开状态并执行回调 用于树表在执行 新增/编辑/删除等操作后 依然在当前位置(体验优化)
-
- 菜单管理 级联删除 删除菜单和children
**REFACTOR**
- 除个人中心外所有本地路由改为从后端返回(需要执行更新sql)
- 流程图预览改为logicflow预览而非图片 ...然后后端又更新了 又改成iframe了
- 菜单管理 新增角色校验(与后端权限保持一致) 只有superadmin可进行增删改
# 1.3.6
**BUG FIX**
- oss配置switch切换 导致报错`存储类型找不到`
- 文件上传无法正确清除(innerList)
# 1.3.5
**BUG FIX**
- 某些带Vxe表格弹窗 关闭后没有正常清理表格数据的问题
# 1.3.4
**BUG FIX**
- 文件上传多次触发导致数据不一致 https://gitee.com/dapppp/ruoyi-plus-vben5/issues/IC3BK6
**PREFORMANCE**
- 浏览器返回按钮/手势操作时 弹窗不会被关闭(keepAlive导致)
# 1.3.3
**BUG FIX**
- 工作流list展示在开启缩放会有误差导致触底逻辑不会触发
**OTHER**
- 代码生成预览对模板的提示...(下载都懒得点一下吗)
# 1.3.2
**REFACTOR**
- 所有表格操作列宽度调整为'auto', 这样会根据子元素宽度适配(比如没有分配权限的情况)
- 菜单图标更新了一部分 sql同步更新
**OTHER**
- 暂时锁死vite依赖 i18n会报错
# 1.3.1
**REFACTOR**

View File

@@ -1,8 +1,13 @@
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp"> </a> <br> <br>
<div align="center">
<a href="https://github.com/anncwb/vue-vben-admin">
<img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp">
</a>
<br>
<br>
[![license](https://img.shields.io/github/license/anncwb/vue-vben-admin.svg)](LICENSE)
<h1>Vue Vben Admin</h1>
<h1>Vue Vben Admin</h1>
</div>
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vbenjs_vue-vben-admin&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin) ![codeql](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml/badge.svg) ![build](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml/badge.svg) ![ci](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml/badge.svg) ![deploy](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml/badge.svg)
@@ -15,27 +20,27 @@ Vue Vben Adminは、最新の`vue3`、`vite`、`TypeScript`などの主流技術
## アップグレード通知
これは最新バージョン5.0であり、以前のバージョンとは互換性がありません。新しいプロジェクトを開始する場合は、最新バージョンを使用することをお勧めします。古いバージョンを表示したい場合は、[v2ブランチ](https://github.com/vbenjs/vue-vben-admin/tree/v2)を使用してください。
これは最新バージョン `5.0` であり、以前のバージョンとは互換性がありません。新しいプロジェクトを開始する場合は、最新バージョンを使用することをお勧めします。古いバージョンを表示したい場合は、[v2ブランチ](https://github.com/vbenjs/vue-vben-admin/tree/v2)を使用してください。
## 特徴
- **最新技術スタック**: Vue 3やViteなどの最先端フロントエンド技術で開発
- **TypeScript**: アプリケーション規模のJavaScriptのための言語
- **テーマ**: 複数のテーマカラーが利用可能で、カスタマイズオプションも豊富
- **国際化**: 完全な内蔵国際化サポート
- **権限管理**: 動的ルートベースの権限生成ソリューションを内蔵
- **最新技術スタック**Vue 3やViteなどの最先端フロントエンド技術で開発
- **TypeScript**アプリケーション規模のJavaScriptのための言語
- **テーマ**複数のテーマカラーが利用可能で、カスタマイズオプションも豊富
- **国際化**完全な内蔵国際化サポート
- **権限管理**動的ルートベースの権限生成ソリューションを内蔵
## プレビュー
- [Vben Admin](https://vben.pro/) - フルバージョンの中国語サイト
テストアカウント: vben/123456
テストアカウントvben/123456
<p align="center">
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview1.png">
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview2.png">
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview3.png">
</p>
<div align="center">
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview1.png">
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview2.png">
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview3.png">
</div>
### Gitpodを使用
@@ -49,30 +54,27 @@ GitpodGitHub用の無料オンライン開発環境でプロジェクト
## インストールと使用
- プロジェクトコードを取得
1. プロジェクトコードを取得
```bash
git clone https://github.com/vbenjs/vue-vben-admin.git
```
- 依存関係のインストール
2. 依存関係のインストール
```bash
cd vue-vben-admin
corepack enable
npm i -g corepack
pnpm install
```
- 実行
3. 実行
```bash
pnpm dev
```
- ビルド
4. ビルド
```bash
pnpm build
@@ -86,40 +88,39 @@ pnpm build
ご参加をお待ちしております![Issueを提出](https://github.com/anncwb/vue-vben-admin/issues/new/choose)するか、Pull Requestを送信してください。
**Pull Request:**
**Pull Request プロセス:**
1. コードをフォーク
2. 自分のブランチを作成: `git checkout -b feat/xxxx`
3. 変更をコミット: `git commit -am 'feat(function): add xxxxx'`
4. ブランチをプッシュ: `git push origin feat/xxxx`
1. コードをフォーク
2. 自分のブランチを作成`git checkout -b feat/xxxx`
3. 変更をコミット`git commit -am 'feat(function): add xxxxx'`
4. ブランチをプッシュ`git push origin feat/xxxx`
5. `pull request`を送信
## Git貢献提出規則
- 参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 規則 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 規則 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
- `feat` 新機能の追加
- `fix` 問題/バグの修正
- `style` コードスタイルに関連し、実行結果に影響しない
- `perf` 最適化/パフォーマンス向上
- `refactor` リファクタリング
- `revert` 変更の取り消し
- `test` テスト関連
- `docs` ドキュメント/注釈
- `chore` 依存関係の更新/スキャフォールディング設定の変更など
- `ci` 継続的インテグレーション
- `types` 型定義ファイルの変更
- `wip` 開発中
- `feat` 新機能の追加
- `fix` 問題/バグの修正
- `style` コードスタイルに関連し、実行結果に影響しない
- `perf` 最適化/パフォーマンス向上
- `refactor` リファクタリング
- `revert` 変更の取り消し
- `test` テスト関連
- `docs` ドキュメント/注釈
- `chore` 依存関係の更新/スキャフォールディング設定の変更など
- `ci` 継続的インテグレーション
- `types` 型定義ファイルの変更
## ブラウザサポート
ローカル開発には`Chrome 80+`ブラウザを推奨します
ローカル開発には `Chrome 80+` ブラウザを推奨します
モダンブラウザをサポートし、IEはサポートしません
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| :-: | :-: | :-: | :-: | :-: |
| サポートしない | 最新2バージョン | 最新2バージョン | 最新2バージョン | 最新2バージョン |
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| :-: | :-: | :-: | :-: |
| 最新2バージョン | 最新2バージョン | 最新2バージョン | 最新2バージョン |
## メンテナー
@@ -140,8 +141,7 @@ pnpm build
## 貢献者
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
<img alt="Contributors"
src="https://opencollective.com/vbenjs/contributors.svg?button=false" />
<img alt="Contributors" src="https://opencollective.com/vbenjs/contributors.svg?button=false" />
</a>
## Discord

View File

@@ -1,8 +1,13 @@
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp"> </a> <br> <br>
<div align="center">
<a href="https://github.com/anncwb/vue-vben-admin">
<img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp">
</a>
<br>
<br>
[![license](https://img.shields.io/github/license/anncwb/vue-vben-admin.svg)](LICENSE)
<h1>Vue Vben Admin</h1>
<h1>Vue Vben Admin</h1>
</div>
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vbenjs_vue-vben-admin&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin) ![codeql](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml/badge.svg) ![build](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml/badge.svg) ![ci](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml/badge.svg) ![deploy](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml/badge.svg)
@@ -17,7 +22,7 @@ Vue Vben Admin is a free and open source middle and back-end template. Using the
This is the latest version, 5.0, and it is not compatible with previous versions. If you are starting a new project, it is recommended to use the latest version. If you wish to view the old version, please use the [v2 branch](https://github.com/vbenjs/vue-vben-admin/tree/v2).
## Feature
## Features
- **Latest Technology Stack**: Developed with cutting-edge front-end technologies like Vue 3 and Vite
- **TypeScript**: A language for application-scale JavaScript
@@ -31,11 +36,11 @@ This is the latest version, 5.0, and it is not compatible with previous versions
Test Account: vben/123456
<p align="center">
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview1.png">
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview2.png">
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview3.png">
</p>
<div align="center">
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview1.png">
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview2.png">
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview3.png">
</div>
### Use Gitpod
@@ -47,31 +52,29 @@ Open the project in Gitpod (free online dev environment for GitHub) and start co
[Document](https://doc.vben.pro/)
## Install and use
## Install and Use
- Get the project code
1. Get the project code
```bash
git clone https://github.com/vbenjs/vue-vben-admin.git
```
- Installation dependencies
2. Install dependencies
```bash
cd vue-vben-admin
corepack enable
npm i -g corepack
pnpm install
```
- run
3. Run
```bash
pnpm dev
```
- build
4. Build
```bash
pnpm build
@@ -81,44 +84,43 @@ pnpm build
[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases)
## How to contribute
## How to Contribute
You are very welcome to join[Raise an issue](https://github.com/anncwb/vue-vben-admin/issues/new/choose) Or submit a Pull Request
You are very welcome to join! [Raise an issue](https://github.com/anncwb/vue-vben-admin/issues/new/choose) or submit a Pull Request.
**Pull Request:**
**Pull Request Process:**
1. Fork code!
2. Create your own branch: `git checkout -b feat/xxxx`
1. Fork the code
2. Create your branch: `git checkout -b feat/xxxx`
3. Submit your changes: `git commit -am 'feat(function): add xxxxx'`
4. Push your branch: `git push origin feat/xxxx`
5. submit`pull request`
5. Submit `pull request`
## Git Contribution submission specification
## Git Contribution Submission Specification
- reference [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) specification ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
Reference [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) specification ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
- `feat` Add new features
- `fix` Fix the problem/BUG
- `style` The code style is related and does not affect the running result
- `perf` Optimization/performance improvement
- `refactor` Refactor
- `revert` Undo edit
- `test` Test related
- `docs` Documentation/notes
- `chore` Dependency update/scaffolding configuration modification etc.
- `ci` Continuous integration
- `types` Type definition file changes
- `wip` In development
- `feat` Add new features
- `fix` Fix the problem/BUG
- `style` The code style is related and does not affect the running result
- `perf` Optimization/performance improvement
- `refactor` Refactor
- `revert` Undo edit
- `test` Test related
- `docs` Documentation/notes
- `chore` Dependency update/scaffolding configuration modification etc.
- `ci` Continuous integration
- `types` Type definition file changes
## Browser support
## Browser Support
The `Chrome 80+` browser is recommended for local development
Support modern browsers, not IE
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| :-: | :-: | :-: | :-: | :-: |
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| :-: | :-: | :-: | :-: |
| last 2 versions | last 2 versions | last 2 versions | last 2 versions |
## Maintainer
@@ -136,11 +138,10 @@ If you think this project is helpful to you, you can help the author buy a cup o
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aee;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
## Contributor
## Contributors
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
<img alt="Contributors"
src="https://opencollective.com/vbenjs/contributors.svg?button=false" />
<img alt="Contributors" src="https://opencollective.com/vbenjs/contributors.svg?button=false" />
</a>
## Discord

View File

@@ -6,7 +6,7 @@
v5版本采用分仓(包)目录结构, 具体开发路径为: `根目录/apps/web-antd`
目前对应后端版本: **分布式5.3.1/微服务2.3.0**
目前对应后端版本: **分布式5.4.0/微服务2.4.0**
V1.1.0版本已支持离线图标
@@ -18,7 +18,7 @@ V1.2.0版本对接warmflow工作流
| 组件/框架 | 版本 |
| :------------- | :----- |
| vben | 5.5.4 |
| vben | 5.5.6 |
| ant-design-vue | 4.2.6 |
| vue | 3.5.13 |
@@ -46,14 +46,6 @@ admin 账号: admin admin123
[RuoYi-Plus 文档地址](https://plus-doc.dromara.org/#/)
## 关于表单
如果你觉得`useVbenForm`难度很大, 完全可以**使用原生antd表单**进行开发, 不一定非得用`useVbenForm`进行开发
`apps/web-antd/src/views/system/notice/notice-modal.vue``通知公告modal`使用**原生antd form**进行(反向🤔)重构, 不想用`useVbenForm`可参考该页面进行表单开发
复杂表单(如各种联动, 需要自定义样式布局, 需要自定义组件)**优先使用原生表单**(反正说了也没人听听😅)
## 预览图
![图片](https://gitee.com/dapppp/ruoyi-plus-vben5/raw/main/scripts/preview/1.png) ![图片](https://gitee.com/dapppp/ruoyi-plus-vben5/raw/main/scripts/preview/2.png) ![图片](https://gitee.com/dapppp/ruoyi-plus-vben5/raw/main/scripts/preview/3.png) ![图片](https://gitee.com/dapppp/ruoyi-plus-vben5/raw/main/scripts/preview/4.png) ![图片](https://gitee.com/dapppp/ruoyi-plus-vben5/raw/main/scripts/preview/5.png) ![图片](https://gitee.com/dapppp/ruoyi-plus-vben5/raw/main/scripts/preview/6.png) ![图片](https://gitee.com/dapppp/ruoyi-plus-vben5/raw/main/scripts/preview/7.png) ![图片](https://gitee.com/dapppp/ruoyi-plus-vben5/raw/main/scripts/preview/8.png) ![图片](https://gitee.com/dapppp/ruoyi-plus-vben5/raw/main/scripts/preview/9.png)
@@ -76,7 +68,7 @@ admin 账号: admin admin123
git clone https://gitee.com/dapppp/ruoyi-plus-vben5.git
```
- 安装依赖
2. 安装依赖
```bash
cd ruoyi-plus-vben5
@@ -150,7 +142,7 @@ VITE_GLOB_WEBSOCKET_ENABLE=false
pnpm dev:antd
```
- 打包
4. 打包
```bash
pnpm build:antd
@@ -164,21 +156,21 @@ pnpm build:antd
## Git 贡献提交规范
- 参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 规范 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 规范 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
- `feat` 增加新功能
- `fix` 修复问题/BUG
- `style` 代码风格相关无影响运行结果的
- `perf` 优化/性能提升
- `refactor` 重构
- `revert` 撤销修改
- `test` 测试相关
- `docs` 文档/注释
- `chore` 依赖更新/脚手架配置修改等
- `workflow` 工作流改进
- `ci` 持续集成
- `types` 类型定义文件更改
- `wip` 开发中
- `feat` 增加新功能
- `fix` 修复问题/BUG
- `style` 代码风格相关无影响运行结果的
- `perf` 优化/性能提升
- `refactor` 重构
- `revert` 撤销修改
- `test` 测试相关
- `docs` 文档/注释
- `chore` 依赖更新/脚手架配置修改等
- `workflow` 工作流改进
- `ci` 持续集成
- `types` 类型定义文件更改
- `wip` 开发中
## 浏览器支持
@@ -186,7 +178,7 @@ pnpm build:antd
本地开发推荐使用`Chrome` 最新版本浏览器
支持现代浏览器, 不支持 IE
支持现代浏览器不支持 IE
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| :-: | :-: | :-: | :-: | :-: |

View File

@@ -0,0 +1,28 @@
export default eventHandler(async (event) => {
const userinfo = verifyAccessToken(event);
if (!userinfo) {
return unAuthorizedResponse(event);
}
const data = `
{
"code": 0,
"message": "success",
"data": [
{
"id": 123456789012345678901234567890123456789012345678901234567890,
"name": "John Doe",
"age": 30,
"email": "john-doe@demo.com"
},
{
"id": 987654321098765432109876543210987654321098765432109876543210,
"name": "Jane Smith",
"age": 25,
"email": "jane@demo.com"
}
]
}
`;
setHeader(event, 'Content-Type', 'application/json');
return data;
});

View File

@@ -0,0 +1,13 @@
import { verifyAccessToken } from '~/utils/jwt-utils';
import { unAuthorizedResponse } from '~/utils/response';
export default eventHandler((event) => {
const userinfo = verifyAccessToken(event);
if (!userinfo) {
return unAuthorizedResponse(event);
}
return useResponseSuccess({
url: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
});
// return useResponseError("test")
});

View File

@@ -7,6 +7,7 @@ export default defineEventHandler(() => {
<li><a href="/api/menu">/api/menu/all</a></li>
<li><a href="/api/auth/codes">/api/auth/codes</a></li>
<li><a href="/api/auth/login">/api/auth/login</a></li>
<li><a href="/api/upload">/api/upload</a></li>
</ul>
`;
});

View File

@@ -3,3 +3,6 @@ VITE_APP_TITLE=Plus Admin
# 应用命名空间用于缓存、store等功能的前缀确保隔离
VITE_APP_NAMESPACE=vben-web-antd
# 对store进行加密的密钥在将store持久化到localStorage时会使用该密钥进行加密
VITE_APP_STORE_SECURE_KEY=please-replace-me-with-your-own-key

View File

@@ -1,6 +1,6 @@
{
"name": "@vben/web-antd",
"version": "1.3.0",
"version": "1.4.1",
"homepage": "https://vben.pro",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {

View File

@@ -40,6 +40,9 @@ const AutoComplete = defineAsyncComponent(
() => import('ant-design-vue/es/auto-complete'),
);
const Button = defineAsyncComponent(() => import('ant-design-vue/es/button'));
const Cascader = defineAsyncComponent(
() => import('ant-design-vue/es/cascader'),
);
const Checkbox = defineAsyncComponent(
() => import('ant-design-vue/es/checkbox'),
);
@@ -88,13 +91,11 @@ const withDefaultPlaceholder = <T extends Component>(
componentProps: Recordable<any> = {},
) => {
return defineComponent({
inheritAttrs: false,
name: component.name,
inheritAttrs: false,
setup: (props: any, { attrs, expose, slots }) => {
/**
* 需要使用computed 否则后续updateSchema更新的placeholder无法显示(响应式问题)
*/
const placeholder = computed(
// 改为placeholder 解决在keepalive & 语言切换 & tab切换 显示不变的问题
const computedPlaceholder = computed(
() =>
props?.placeholder ||
attrs?.placeholder ||
@@ -118,7 +119,7 @@ const withDefaultPlaceholder = <T extends Component>(
component,
{
...componentProps,
placeholder: placeholder.value,
placeholder: computedPlaceholder.value,
...props,
...attrs,
ref: innerRef,
@@ -134,6 +135,7 @@ export type ComponentType =
| 'ApiSelect'
| 'ApiTreeSelect'
| 'AutoComplete'
| 'Cascader'
| 'Checkbox'
| 'CheckboxGroup'
| 'DatePicker'
@@ -168,21 +170,36 @@ async function initComponentAdapter() {
// 如果你的组件体积比较大,可以使用异步加载
// Button: () =>
// import('xxx').then((res) => res.Button),
ApiSelect: withDefaultPlaceholder(ApiComponent, 'select', {
component: Select,
loadingSlot: 'suffixIcon',
visibleEvent: 'onDropdownVisibleChange',
modelPropName: 'value',
}),
ApiTreeSelect: withDefaultPlaceholder(ApiComponent, 'select', {
component: TreeSelect,
fieldNames: { label: 'label', value: 'value', children: 'children' },
loadingSlot: 'suffixIcon',
modelPropName: 'value',
optionsPropName: 'treeData',
visibleEvent: 'onVisibleChange',
}),
ApiSelect: withDefaultPlaceholder(
{
...ApiComponent,
name: 'ApiSelect',
},
'select',
{
component: Select,
loadingSlot: 'suffixIcon',
visibleEvent: 'onDropdownVisibleChange',
modelPropName: 'value',
},
),
ApiTreeSelect: withDefaultPlaceholder(
{
...ApiComponent,
name: 'ApiTreeSelect',
},
'select',
{
component: TreeSelect,
fieldNames: { label: 'label', value: 'value', children: 'children' },
loadingSlot: 'suffixIcon',
modelPropName: 'value',
optionsPropName: 'treeData',
visibleEvent: 'onVisibleChange',
},
),
AutoComplete,
Cascader: withDefaultPlaceholder(Cascader, 'select'),
Checkbox,
CheckboxGroup,
DatePicker,

View File

@@ -10,44 +10,46 @@ import { $t } from '@vben/locales';
import { isArray } from 'lodash-es';
setupVbenForm<ComponentType>({
config: {
// ant design vue组件库默认都是 v-model:value
baseModelPropName: 'value',
async function initSetupVbenForm() {
setupVbenForm<ComponentType>({
config: {
// ant design vue组件库默认都是 v-model:value
baseModelPropName: 'value',
// 一些组件是 v-model:checked 或者 v-model:fileList
modelPropNameMap: {
Checkbox: 'checked',
Radio: 'checked',
RichTextarea: 'modelValue',
Switch: 'checked',
Upload: 'fileList',
// 一些组件是 v-model:checked 或者 v-model:fileList
modelPropNameMap: {
Checkbox: 'checked',
Radio: 'checked',
RichTextarea: 'modelValue',
Switch: 'checked',
Upload: 'fileList',
},
},
},
defineRules: {
// 输入项目必填国际化适配
required: (value, _params, ctx) => {
if (value === undefined || value === null || value.length === 0) {
return $t('ui.formRules.required', [ctx.label]);
}
return true;
defineRules: {
// 输入项目必填国际化适配
required: (value, _params, ctx) => {
if (value === undefined || value === null || value.length === 0) {
return $t('ui.formRules.required', [ctx.label]);
}
return true;
},
// 选择项目必填国际化适配
selectRequired: (value, _params, ctx) => {
if (
[false, null, undefined].includes(value) ||
(isArray(value) && value.length === 0)
) {
return $t('ui.formRules.selectRequired', [ctx.label]);
}
return true;
},
},
// 选择项目必填国际化适配
selectRequired: (value, _params, ctx) => {
if (
[false, null, undefined].includes(value) ||
(isArray(value) && value.length === 0)
) {
return $t('ui.formRules.selectRequired', [ctx.label]);
}
return true;
},
},
});
});
}
const useVbenForm = useForm<ComponentType>;
export { useVbenForm, z };
export { initSetupVbenForm, useVbenForm, z };
export type VbenFormSchema = FormSchema<ComponentType>;
export type { VbenFormProps };

View File

@@ -50,13 +50,15 @@ setupVbenVxeTable({
// 右上角工具栏
toolbarConfig: {
// 自定义列
custom: {
custom: true,
customOptions: {
icon: 'vxe-icon-setting',
},
// 最大化
zoom: true,
// 刷新
refresh: {
refresh: true,
refreshOptions: {
// 默认为reload 修改为在当前页刷新
code: 'query',
},

View File

@@ -1,7 +1,12 @@
import type { GrantType } from '@vben/common-ui';
import type { HttpResponse } from '@vben/request';
import { h } from 'vue';
import { useAppConfig } from '@vben/hooks';
import { Modal } from 'ant-design-vue';
import { requestClient } from '#/api/request';
const { clientId, sseEnable } = useAppConfig(
@@ -90,8 +95,32 @@ export async function loginApi(data: AuthApi.LoginParams) {
* 用户登出
* @returns void
*/
export function doLogout() {
return requestClient.post<void>('/auth/logout');
export async function doLogout() {
const resp = await requestClient.post<HttpResponse<void>>(
'/auth/logout',
null,
{
isTransformResponse: false,
},
);
// 无奈之举 对错误用法的提示
if (resp.code === 401 && import.meta.env.DEV) {
Modal.destroyAll();
Modal.warn({
title: '后端配置出现错误',
centered: true,
content: h('div', { class: 'flex flex-col gap-2' }, [
`检测到你的logout接口返回了401, 导致前端一直进入循环逻辑???`,
...Array.from({ length: 3 }, () =>
h(
'span',
{ class: 'font-bold text-red-500 text-[18px]' },
'去检查你的后端配置!别盯着前端找问题了!这不是前端问题!',
),
),
]),
});
}
}
/**

View File

@@ -7,4 +7,5 @@ export interface OnlineUser {
browser: string;
os: string;
loginTime: number;
deviceType: string;
}

View File

@@ -39,6 +39,11 @@ const { apiURL, clientId, enableEncrypt } = useAppConfig(
*/
let isLogoutProcessing = false;
/**
* 定义一个401专用异常 用于可能会用到的区分场景?
*/
export class UnauthorizedException extends Error {}
function createRequestClient(baseURL: string) {
const client = new RequestClient({
// 后端地址
@@ -228,7 +233,7 @@ function createRequestClient(baseURL: string) {
case 401: {
// 已经在登出过程中 不再执行
if (isLogoutProcessing) {
throw new Error(timeoutMsg);
throw new UnauthorizedException(timeoutMsg);
}
isLogoutProcessing = true;
const _msg = $t('http.loginTimeout');
@@ -238,7 +243,7 @@ function createRequestClient(baseURL: string) {
isLogoutProcessing = false;
});
// 不再执行下面逻辑
throw new Error(_msg);
throw new UnauthorizedException(_msg);
}
default: {
if (msg) {

View File

@@ -81,3 +81,12 @@ export function tenantPackageMenuTreeSelect(packageId: ID) {
`${Api.tenantPackageMenuTreeselect}/${packageId}`,
);
}
/**
* 批量删除菜单
* @param menuIds 菜单ids
* @returns void
*/
export function menuCascadeRemove(menuIds: IDS) {
return requestClient.deleteWithMsg<void>(`${Api.root}/cascade/${menuIds}`);
}

View File

@@ -37,9 +37,10 @@ export function ossConfigRemove(ossConfigIds: IDS) {
// 更改OSS配置的状态
export function ossConfigChangeStatus(data: any) {
const requestData = {
const requestData: Partial<OssConfig> = {
ossConfigId: data.ossConfigId,
status: data.status,
configKey: data.configKey,
};
return requestClient.putWithMsg(Api.ossConfigChangeStatus, requestData);
}

View File

@@ -94,7 +94,7 @@ export function pageByCurrent(params?: PageQuery) {
*/
export function flowInfo(businessId: string) {
return requestClient.get<FlowInfoResponse>(
`/workflow/instance/flowImage/${businessId}`,
`/workflow/instance/flowHisTaskList/${businessId}`,
);
}

View File

@@ -36,6 +36,6 @@ export interface Flow {
}
export interface FlowInfoResponse {
image: string;
instanceId: string;
list: Flow[];
}

View File

@@ -8,6 +8,8 @@ import { App, ConfigProvider, theme } from 'ant-design-vue';
import { antdLocale } from '#/locales';
import { useUploadTip } from './upload-tip';
defineOptions({ name: 'App' });
const { isDark } = usePreferences();
@@ -28,6 +30,8 @@ const tokenTheme = computed(() => {
token: tokens,
};
});
useUploadTip();
</script>
<template>

View File

@@ -13,6 +13,7 @@ import { setupGlobalComponent } from '#/components/global';
import { $t, setupI18n } from '#/locales';
import { initComponentAdapter } from './adapter/component';
import { initSetupVbenForm } from './adapter/form';
import App from './app.vue';
import { router } from './router';
@@ -20,6 +21,9 @@ async function bootstrap(namespace: string) {
// 初始化组件适配器
await initComponentAdapter();
// 初始化表单组件
await initSetupVbenForm();
// // 设置弹窗的默认配置
// setDefaultModalProps({
// fullscreenButton: false,

View File

@@ -202,13 +202,31 @@ const events = computed(() => {
</template>
<style lang="scss">
/***
由于modal/drawer的zIndex升级后为2000
这里会造成遮挡 修改为更高的zIndex
*/
// 展开层元素z-index
$dropdown-index: 2025;
@mixin tinymce-valid-fail($color) {
.app-tinymce {
// 最外层的tinymce容器
.tox-tinymce {
border-color: $color;
}
// focus样式
.tox .tox-edit-area::before {
border-color: $color;
border-right: none;
border-left: none;
}
}
}
.tox.tox-silver-sink.tox-tinymce-aux {
/** 该样式默认为1300的zIndex */
z-index: 2025;
z-index: $dropdown-index;
}
.tox-fullscreen .tox.tox-tinymce-aux {
z-index: $dropdown-index !important;
}
.app-tinymce {
@@ -218,5 +236,29 @@ const events = computed(() => {
.tox-promotion {
display: none;
}
/** 保持focus时与primary色一致 */
.tox .tox-edit-area::before {
border-color: hsl(var(--primary));
}
}
// antd原生表单 校验失败样式
.ant-form-item:has(.ant-form-item-explain-error) {
$error-color: #ff3860;
@include tinymce-valid-fail($error-color);
}
// useVbenForm 校验失败样式
.form-valid-error {
$error-color: hsl(var(--destructive));
@include tinymce-valid-fail($error-color);
}
// 全屏下样式处理 不去掉transform位置会异常
div[role='dialog']:has(.tox.tox-tinymce.tox-fullscreen) {
transform: none !important;
}
</style>

View File

@@ -98,7 +98,7 @@ Upload.Dragger只会影响样式
{{ $t('component.upload.upload') }}
</a-button>
</div>
<div v-if="enableDragUpload && innerFileList?.length < maxCount">
<div v-if="enableDragUpload">
<p class="ant-upload-drag-icon">
<InboxOutlined />
</p>

View File

@@ -160,6 +160,8 @@ export function useUpload(
return undefined;
}
// 用来标识是否为上传 这样在watch内部不需要请求api
let isUpload = false;
function handleChange(info: UploadChangeParam) {
/**
* 移除当前文件
@@ -199,7 +201,8 @@ export function useUpload(
currentFile.fileName = transformFilename(cb);
currentFile.name = transformFilename(cb);
currentFile.thumbUrl = transformThumbUrl(cb);
// 标记为上传 watch根据值做处理
isUpload = true;
// ossID添加 单个文件会被当做string
if (props.maxCount === 1) {
bindValue.value = ossId;
@@ -319,8 +322,18 @@ export function useUpload(
() => bindValue.value,
async (value) => {
if (value.length === 0) {
// 清空绑定值时同时清空innerFileList避免外部使用时还能读取到
innerFileList.value = [];
return;
}
// 上传完毕 不需要调用获取信息接口
if (isUpload) {
// 清理 使下一次状态可用
isUpload = false;
return;
}
const resp = await ossInfo(value);
function transformFile(info: OssFile) {
const cb = { type: 'info', response: info } as const;

View File

@@ -138,8 +138,8 @@ watch(
:avatar
:menus
:text="userStore.userInfo?.realName"
description="ann.vben@gmail.com"
tag-text="Pro"
:description="userStore.userInfo?.email || '未设置邮箱'"
:tag-text="userStore.userInfo?.username"
@logout="handleLogout"
/>
</template>

View File

@@ -20,6 +20,9 @@ export const overridesPreferences = defineOverridesPreferences({
* 这里可以设置默认头像 url链接或vite导入的图片链接
*/
// defaultAvatar: '',
/**
* 在这里设置应用标题
*/
name: import.meta.env.VITE_APP_TITLE,
/**
* 不支持modal模式 需要改动的地方太多

View File

@@ -1,6 +1,7 @@
import type {
ComponentRecordType,
GenerateMenuAndRoutesOptions,
RouteMeta,
RouteRecordStringComponent,
} from '@vben/types';
@@ -21,6 +22,37 @@ import { localMenuList } from './routes/local';
const forbiddenComponent = () => import('#/views/_core/fallback/forbidden.vue');
const NotFoundComponent = () => import('#/views/_core/fallback/not-found.vue');
/**
* 后端返回的meta有时候不包括需要的信息 比如activePath等
* 在这里定义映射
*/
const routeMetaMapping: Record<string, Omit<RouteMeta, 'title'>> = {
'/system/role-auth/user/:roleId': {
activePath: '/system/role',
requireHomeRedirect: true,
},
'/system/oss-config/index': {
activePath: '/system/oss',
requireHomeRedirect: true,
},
'/tool/gen-edit/index/:tableId': {
activePath: '/tool/gen',
requireHomeRedirect: true,
},
'/workflow/design/index': {
activePath: '/workflow/processDefinition',
requireHomeRedirect: true,
},
'/workflow/leaveEdit/index': {
activePath: '/demo/leave',
requireHomeRedirect: true,
},
};
/**
* 后台路由转vben路由
* @param menuList 后台菜单
@@ -98,6 +130,17 @@ function backMenuToVbenMenu(
path: menu.path,
};
// 处理meta映射
if (Object.keys(routeMetaMapping).includes(vbenRoute.path)) {
const routeMeta = routeMetaMapping[vbenRoute.path];
if (routeMeta) {
vbenRoute.meta = {
...vbenRoute.meta,
...(routeMeta as RouteMeta),
};
}
}
// 添加路由参数信息
if (menu.query) {
try {

View File

@@ -1,6 +1,6 @@
import type { Router } from 'vue-router';
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
import { LOGIN_PATH } from '@vben/constants';
import { preferences } from '@vben/preferences';
import { useAccessStore, useUserStore } from '@vben/stores';
import { startProgress, stopProgress } from '@vben/utils';
@@ -18,7 +18,7 @@ function setupCommonGuard(router: Router) {
// 记录已经加载的页面
const loadedPaths = new Set<string>();
router.beforeEach(async (to) => {
router.beforeEach((to) => {
to.meta.loaded = loadedPaths.has(to.path);
// 页面加载进度条
@@ -56,7 +56,7 @@ function setupAccessGuard(router: Router) {
return decodeURIComponent(
(to.query?.redirect as string) ||
userStore.userInfo?.homePath ||
DEFAULT_HOME_PATH,
preferences.app.defaultHomePath,
);
}
return true;
@@ -75,7 +75,7 @@ function setupAccessGuard(router: Router) {
path: LOGIN_PATH,
// 如不需要,直接删除 query
query:
to.fullPath === DEFAULT_HOME_PATH
to.fullPath === preferences.app.defaultHomePath
? {}
: { redirect: encodeURIComponent(to.fullPath) },
// 携带当前跳转的页面,登录后重新跳转该页面
@@ -108,8 +108,8 @@ function setupAccessGuard(router: Router) {
accessStore.setAccessRoutes(accessibleRoutes);
accessStore.setIsAccessChecked(true);
const redirectPath = (from.query.redirect ??
(to.path === DEFAULT_HOME_PATH
? userInfo.homePath || DEFAULT_HOME_PATH
(to.path === preferences.app.defaultHomePath
? userInfo.homePath || preferences.app.defaultHomePath
: to.fullPath)) as string;
return {

View File

@@ -1,6 +1,7 @@
import type { RouteRecordRaw } from 'vue-router';
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
import { LOGIN_PATH } from '@vben/constants';
import { preferences } from '@vben/preferences';
import { $t } from '#/locales';
@@ -34,7 +35,7 @@ const coreRoutes: RouteRecordRaw[] = [
},
name: 'Root',
path: '/',
redirect: DEFAULT_HOME_PATH,
redirect: preferences.app.defaultHomePath,
children: [],
},
{

View File

@@ -2,8 +2,14 @@ import type { RouteRecordStringComponent } from '@vben/types';
import { $t } from '@vben/locales';
const {
version,
// vite inject-metadata 插件注入的全局变量
} = __VBEN_ADMIN_METADATA__ || {};
/**
* 该文件放非后台返回的路由 比如个人中心 等需要跳转显示的页面
* 也可以直接在菜单管理配置
*/
const localRoutes: RouteRecordStringComponent[] = [
{
@@ -17,69 +23,6 @@ const localRoutes: RouteRecordStringComponent[] = [
name: 'Profile',
path: '/profile',
},
{
component: '/system/oss-config/index',
meta: {
activePath: '/system/oss',
icon: 'ant-design:setting-outlined',
title: 'oss配置',
hideInMenu: true,
requireHomeRedirect: true,
},
name: 'OssConfig',
path: '/system/oss-config',
},
{
component: '/tool/gen/edit-gen',
meta: {
activePath: '/tool/gen',
icon: 'tabler:code',
title: '生成配置',
hideInMenu: true,
requireHomeRedirect: true,
},
name: 'GenConfig',
path: '/code-gen/edit/:tableId',
},
{
component: '/system/role-assign/index',
meta: {
activePath: '/system/role',
icon: 'eos-icons:role-binding-outlined',
title: '分配角色',
hideInMenu: true,
requireHomeRedirect: true,
},
name: 'RoleAssign',
path: '/system/role-assign/:roleId',
},
{
component: '/workflow/components/flow-designer',
meta: {
activePath: '/workflow/processDefinition',
icon: 'fluent-mdl2:flow',
title: '流程设计',
hideInMenu: true,
requireHomeRedirect: true,
},
name: 'WorkflowDesigner',
path: '/workflow/designer',
},
/**
* 需要添加iframe路由 同目录的./workflow-iframe.ts
*/
{
component: 'workflow/leave/leave-form',
meta: {
icon: 'flat-color-icons:leave',
title: '请假申请',
activePath: '/demo/leave',
hideInMenu: true,
requireHomeRedirect: true,
},
name: 'WorkflowLeaveIndex',
path: '/workflow/leaveEdit/index',
},
];
/**
@@ -134,8 +77,8 @@ export const localMenuList: RouteRecordStringComponent[] = [
icon: 'lucide:book-open-text',
keepAlive: true,
title: '更新记录',
badge: '1.3.0',
badgeVariants: '#CC0033',
badge: `当前: ${version}`,
badgeVariants: 'bg-primary',
},
},
],

View File

@@ -4,7 +4,8 @@ import type { UserInfo } from '@vben/types';
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { DEFAULT_HOME_PATH, LOGIN_PATH } from '@vben/constants';
import { LOGIN_PATH } from '@vben/constants';
import { preferences } from '@vben/preferences';
import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores';
import { notification } from 'ant-design-vue';
@@ -55,7 +56,9 @@ export const useAuthStore = defineStore('auth', () => {
if (accessStore.loginExpired) {
accessStore.setLoginExpired(false);
} else {
onSuccess ? await onSuccess?.() : await router.push(DEFAULT_HOME_PATH);
onSuccess
? await onSuccess?.()
: await router.push(preferences.app.defaultHomePath);
}
if (userInfo?.realName) {
@@ -115,6 +118,7 @@ export const useAuthStore = defineStore('auth', () => {
roles,
userId: user.userId,
username: user.userName,
email: user.email ?? '',
};
userStore.setUserInfo(userInfo);
/**

View File

@@ -0,0 +1,36 @@
import { onMounted } from 'vue';
import { useLocalStorage } from '@vueuse/core';
import { Modal } from 'ant-design-vue';
export function useUploadTip() {
const readTip = useLocalStorage<boolean>('__upload_tip_read_5.4.0', false);
onMounted(() => {
if (readTip.value || !import.meta.env.DEV) {
return;
}
const modalInstance = Modal.info({
title: '提示',
centered: true,
content:
'如果你的版本是从低版本升级到后端>5.4.0, 记得执行升级sql, 否则跳转页面(如oss 代码生成配置)等会404',
okButtonProps: { disabled: true },
onOk() {
modalInstance.destroy();
readTip.value = true;
},
});
let time = 3;
const interval = setInterval(() => {
modalInstance.update({
okText: time === 0 ? '我知道了, 不再弹出' : `${time}秒后关闭`,
okButtonProps: { disabled: time > 0 },
});
if (time <= 0) {
clearInterval(interval);
}
time--;
}, 1000);
});
}

View File

@@ -1,3 +1,4 @@
import { UnauthorizedException } from '#/api/request';
import { dictDataInfo } from '#/api/system/dict/dict-data';
import { useDictStore } from '#/store/dict';
@@ -27,9 +28,16 @@ function fetchAndCacheDictData<T>(
// 内部处理了push的逻辑 这里不用push
setDictInfo(dictName, resp, formatNumber);
})
.catch(() => {
// 401时 移除字典缓存 下次登录重新获取
dictRequestCache.delete(dictName);
.catch((error) => {
/**
* 需要判断是否为401抛出的特定异常 401清除缓存
* 其他error清除缓存会导致无限循环调用字典接口 则不做处理
*/
if (error instanceof UnauthorizedException) {
// 401时 移除字典缓存 下次登录重新获取
dictRequestCache.delete(dictName);
}
// 其他不做处理
})
.finally(() => {
// 移除请求状态缓存

View File

@@ -18,7 +18,7 @@ import { useAuthStore } from '#/store';
defineOptions({ name: 'CodeLogin' });
const loading = ref(false);
const CODE_LENGTH = 6;
const CODE_LENGTH = 4;
const tenantInfo = ref<TenantResp>({
tenantEnabled: false,
@@ -85,8 +85,8 @@ const formSchema = computed((): VbenFormSchema[] => {
: $t('authentication.sendCode');
return text;
},
// 验证码长度 在这设置
codeLength: 4,
// 验证码长度
codeLength: CODE_LENGTH,
placeholder: $t('authentication.code'),
handleSendCode: async () => {
const { valid, value } = await form.validateField('phoneNumber');

View File

@@ -1,6 +1,6 @@
import type { Component, CSSProperties } from 'vue';
import { ref } from 'vue';
import { markRaw, ref } from 'vue';
import { DEFAULT_TENANT_ID } from '@vben/constants';
import {
@@ -69,32 +69,32 @@ export async function handleAuthBinding(source: string) {
*/
export const accountBindList: BindItem[] = [
{
avatar: GiteeIcon,
avatar: markRaw(GiteeIcon),
description: '绑定Gitee账号',
source: 'gitee',
title: 'Gitee',
style: { color: '#c71d23' },
},
{
avatar: GithubOAuthIcon,
avatar: markRaw(GithubOAuthIcon),
description: '绑定Github账号',
source: 'github',
title: 'Github',
},
{
avatar: SvgMaxKeyIcon,
avatar: markRaw(SvgMaxKeyIcon),
description: '绑定MaxKey账号',
source: 'maxkey',
title: 'MaxKey',
},
{
avatar: SvgTopiamIcon,
avatar: markRaw(SvgTopiamIcon),
description: '绑定topiam账号',
source: 'topiam',
title: 'Topiam',
},
{
avatar: SvgWechatIcon,
avatar: markRaw(SvgWechatIcon),
description: '绑定wechat账号',
source: 'wechat',
title: 'Wechat',

View File

@@ -1,174 +1,142 @@
<script setup lang="tsx">
import type { VxeGridProps } from '@vben/plugins/vxe-table';
import type { BindItem } from '../../oauth-common';
import { computed, ref, unref } from 'vue';
import type { SocialInfo } from '#/api/system/social/model';
import { useVbenVxeGrid } from '@vben/plugins/vxe-table';
import { onMounted, ref } from 'vue';
import { Alert, Avatar, Card, List, ListItem, Modal } from 'ant-design-vue';
import { Alert, Avatar, Card, Empty, Modal, Tooltip } from 'ant-design-vue';
import { authUnbinding } from '#/api';
import { socialList } from '#/api/system/social';
import { accountBindList, handleAuthBinding } from '../../oauth-common';
function buttonText(item: BindItem) {
return item.bound ? '已绑定' : '绑定';
interface BindItemWithInfo extends BindItem {
info?: SocialInfo;
bind?: boolean;
}
/**
* 已经绑定的平台
*/
const boundPlatformsList = ref<string[]>([]);
const bindList = computed<BindItem[]>(() => {
const list = [...accountBindList];
const bindList = ref<BindItemWithInfo[]>([]);
async function loadData() {
const resp = await socialList();
const list: BindItemWithInfo[] = [...accountBindList];
list.forEach((item) => {
item.bound = !!unref(boundPlatformsList).includes(item.source);
/**
* 平台转小写
*/
item.bound = resp
.map((social) => social.source.toLowerCase())
.includes(item.source.toLowerCase());
/**
* 添加info信息
*/
if (item.bound) {
item.info = resp.find(
(social) => social.source.toLowerCase() === item.source,
);
}
});
return list;
});
const gridOptions: VxeGridProps = {
columns: [
{
field: 'source',
title: '绑定平台',
},
{
slots: {
default: ({ row }) => {
return <Avatar src={row.avatar} />;
},
},
field: 'avatar',
title: '头像',
},
{
align: 'center',
field: 'userName',
title: '账号',
},
{
align: 'center',
slots: {
default: 'action',
},
title: '操作',
},
],
height: 220,
keepSource: true,
pagerConfig: {
enabled: false,
},
toolbarConfig: {
enabled: false,
},
proxyConfig: {
ajax: {
query: async () => {
const resp = await socialList();
/**
* 平台转小写
* 已经绑定的平台
*/
boundPlatformsList.value = resp.map((item) =>
item.source.toLowerCase(),
);
return {
rows: resp,
};
},
},
},
rowConfig: {
isCurrent: false,
keyField: 'id',
},
id: 'profile-bind-table',
};
const [BasicTable, tableApi] = useVbenVxeGrid({
gridOptions,
});
bindList.value = list;
}
onMounted(loadData);
/**
* 解绑账号
*/
function handleUnbind(record: Record<string, any>) {
function handleUnbind(record: BindItemWithInfo) {
if (!record.info) {
return;
}
Modal.confirm({
content: `确定解绑[${record.source}]平台的[${record.userName}]账号吗?`,
content: `确定解绑[${record.source}]平台的[${record.info.userName}]账号吗?`,
async onOk() {
await authUnbinding(record.id);
await tableApi.reload();
await authUnbinding(record.info!.id);
await loadData();
},
title: '提示',
type: 'warning',
});
}
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
</script>
<template>
<div class="flex flex-col gap-[16px]">
<BasicTable>
<template #action="{ row }">
<a-button type="link" @click="handleUnbind(row)">解绑</a-button>
</template>
</BasicTable>
<div class="pb-3">
<List
:data-source="bindList"
:grid="{ gutter: 8, xs: 1, sm: 1, md: 2, lg: 3, xl: 3, xxl: 3 }"
<div class="flex flex-col gap-4 pb-4">
<div
v-if="bindList.length > 0"
class="grid grid-cols-1 gap-4 lg:grid-cols-2 2xl:grid-cols-3"
>
<Card
class="transition-shadow duration-300 hover:shadow-md"
v-for="item in bindList"
:key="item.source"
>
<template #renderItem="{ item }">
<ListItem>
<Card>
<div class="flex w-full items-center gap-4">
<component
:is="item.avatar"
v-if="item.avatar"
:style="item?.style ?? {}"
class="size-[40px]"
/>
<div class="flex flex-1 items-center justify-between">
<div class="flex flex-col">
<h4
class="mb-[4px] text-[14px] text-black/85 dark:text-white/85"
>
{{ item.title }}
</h4>
<span class="text-black/45 dark:text-white/45">
{{ item.description }}
</span>
</div>
<a-button
:disabled="item.bound"
size="small"
type="link"
@click="handleAuthBinding(item.source)"
>
{{ buttonText(item) }}
</a-button>
</div>
</div>
</Card>
</ListItem>
</template>
</List>
<Alert message="说明" type="info">
<template #description>
<p>
需要添加第三方账号在
<span class="font-bold">
apps\web-antd\src\views\_core\oauth-common.ts
</span>
中accountBindList按模板添加
</p>
</template>
</Alert>
<div class="flex w-full items-center gap-4">
<component
:is="item.avatar"
v-if="item.avatar"
:style="item?.style ?? {}"
class="size-[40px]"
/>
<div class="flex flex-1 items-center justify-between">
<div class="flex flex-col">
<h4 class="mb-[4px] text-[14px] text-black/85 dark:text-white/85">
{{ item.title }}
</h4>
<span class="text-black/45 dark:text-white/45">
<template v-if="!item.bound">
{{ item.description }}
</template>
<template v-if="item.bound && item.info">
<Tooltip>
<template #title>
<div class="flex flex-col items-center gap-2 p-2">
<Avatar :size="36" :src="item.info.avatar" />
<div>绑定时间: {{ item.info.createTime }}</div>
</div>
</template>
<div class="cursor-pointer">
已绑定: {{ item.info.nickName }}
</div>
</Tooltip>
</template>
</span>
</div>
<!-- TODO: 这里有优化空间? -->
<a-button
size="small"
:type="item.bound ? 'default' : 'link'"
@click="
item.bound ? handleUnbind(item) : handleAuthBinding(item.source)
"
>
{{ item.bound ? '取消绑定' : '绑定' }}
</a-button>
</div>
</div>
</Card>
</div>
<div
v-if="bindList.length === 0"
class="flex items-center justify-center rounded-lg border py-4"
>
<Empty :image="simpleImage" description="暂无可绑定的第三方账户" />
</div>
<Alert message="说明" type="info">
<template #description>
<p>
需要添加第三方账号在
<span class="font-bold">
apps\web-antd\src\views\_core\oauth-common.ts
</span>
中accountBindList按模板添加
</p>
</template>
</Alert>
</div>
</template>

View File

@@ -4,7 +4,8 @@ import type { AuthApi } from '#/api';
import { onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { DEFAULT_HOME_PATH, DEFAULT_TENANT_ID } from '@vben/constants';
import { DEFAULT_TENANT_ID } from '@vben/constants';
import { preferences } from '@vben/preferences';
import { useAccessStore } from '@vben/stores';
import { message } from 'ant-design-vue';
@@ -70,7 +71,7 @@ onMounted(async () => {
// 500 你还没有绑定第三方账号,绑定后才可以登录!
} finally {
setTimeout(() => {
router.push(DEFAULT_HOME_PATH);
router.push(preferences.app.defaultHomePath);
}, 1500);
}
});

View File

@@ -51,7 +51,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
resizable: false,
width: 'auto',
},
];

View File

@@ -60,7 +60,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
resizable: false,
width: 'auto',
},
];

View File

@@ -79,6 +79,7 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 120,
resizable: false,
width: 'auto',
},
];

View File

@@ -86,6 +86,7 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 120,
resizable: false,
width: 'auto',
},
];

View File

@@ -95,7 +95,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
resizable: false,
width: 'auto',
},
];

View File

@@ -71,7 +71,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
resizable: false,
width: 'auto',
},
];

View File

@@ -30,7 +30,6 @@ export const columns: VxeGridProps['columns'] = [
field: 'deptName',
title: '部门名称',
treeNode: true,
width: 200,
},
{
field: 'deptCategory',
@@ -39,11 +38,9 @@ export const columns: VxeGridProps['columns'] = [
{
field: 'orderNum',
title: '排序',
width: 180,
},
{
field: 'status',
width: 180,
title: '状态',
slots: {
default: ({ row }) => {
@@ -60,7 +57,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 200,
resizable: false,
width: 'auto',
},
];
@@ -97,6 +95,7 @@ export const drawerSchema: FormSchemaGetter = () => [
fieldName: 'orderNum',
label: '显示排序',
rules: 'required',
defaultValue: 0,
},
{
component: 'Input',

View File

@@ -0,0 +1,6 @@
<template>
<div>
ele版本会使用这个文件 只是为了不报错`未找到对应组件`才新建的这个文件
无实际意义
</div>
</template>

View File

@@ -45,7 +45,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
resizable: false,
width: 'auto',
},
];
@@ -98,6 +99,7 @@ export const drawerSchema: FormSchemaGetter = () => [
fieldName: 'dictSort',
label: '显示排序',
rules: 'required',
defaultValue: 0,
},
{
component: 'Textarea',

View File

@@ -77,7 +77,7 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
const { dictCode, dictType } = drawerApi.getData() as DrawerProps;
isUpdate.value = !!dictCode;
formApi.setFieldValue('dictType', dictType);
await formApi.setFieldValue('dictType', dictType);
if (dictCode && isUpdate.value) {
const record = await dictDetailInfo(dictCode);

View File

@@ -39,7 +39,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
resizable: false,
width: 'auto',
},
];

View File

@@ -0,0 +1,281 @@
<!-- 使用vxe实现成本最小 且自带虚拟滚动 -->
<script setup lang="ts">
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { DictType } from '#/api/system/dict/dict-type-model';
import { h, ref, shallowRef, watch } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { cn } from '@vben/utils';
import {
DeleteOutlined,
EditOutlined,
ExportOutlined,
PlusOutlined,
SyncOutlined,
} from '@ant-design/icons-vue';
import {
Alert,
Input,
Modal,
Popconfirm,
Space,
Tooltip,
} from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import {
dictTypeExport,
dictTypeList,
dictTypeRemove,
refreshDictTypeCache,
} from '#/api/system/dict/dict-type';
import { commonDownloadExcel } from '#/utils/file/download';
import { emitter } from '../mitt';
import dictTypeModal from './dict-type-modal.vue';
const tableAllData = shallowRef<DictType[]>([]);
const gridOptions: VxeGridProps = {
columns: [
{
title: 'name',
field: 'render',
slots: { default: 'render' },
},
],
height: 'auto',
keepSource: true,
pagerConfig: {
enabled: false,
},
proxyConfig: {
ajax: {
query: async () => {
const resp = await dictTypeList();
total.value = resp.total;
tableAllData.value = resp.rows;
return resp;
},
},
},
rowConfig: {
keyField: 'dictId',
// 高亮当前行
isCurrent: true,
},
cellConfig: {
height: 60,
},
showHeader: false,
toolbarConfig: {
enabled: false,
},
// 开启虚拟滚动
scrollY: {
enabled: false,
gt: 0,
},
rowClassName: 'cursor-pointer',
id: 'system-dict-data-index',
};
const [BasicTable, tableApi] = useVbenVxeGrid({
gridOptions,
gridEvents: {
cellClick: ({ row }) => {
handleRowClick(row);
},
},
});
const [DictTypeModal, modalApi] = useVbenModal({
connectedComponent: dictTypeModal,
});
function handleAdd() {
modalApi.setData({});
modalApi.open();
}
async function handleEdit(record: DictType) {
modalApi.setData({ id: record.dictId });
modalApi.open();
}
async function handleDelete(row: DictType) {
await dictTypeRemove([row.dictId]);
await tableApi.query();
}
async function handleReset() {
currentRowId.value = '';
searchValue.value = '';
await tableApi.query();
}
function handleDownloadExcel() {
commonDownloadExcel(dictTypeExport, '字典类型数据');
}
function handleRefreshCache() {
Modal.confirm({
title: '提示',
content: '确认刷新字典类型缓存吗?',
okButtonProps: {
danger: true,
},
onOk: async () => {
await refreshDictTypeCache();
await tableApi.query();
},
});
}
const lastDictType = ref<string>('');
const currentRowId = ref<null | number | string>(null);
function handleRowClick(row: DictType) {
if (lastDictType.value === row.dictType) {
return;
}
currentRowId.value = row.dictId;
emitter.emit('rowClick', row.dictType);
}
const searchValue = ref('');
const total = ref(0);
watch(searchValue, (value) => {
if (!tableApi) {
return;
}
if (value) {
const names = tableAllData.value.filter((item) =>
item.dictName.includes(searchValue.value),
);
const types = tableAllData.value.filter((item) =>
item.dictType.includes(searchValue.value),
);
const filtered = [...new Set([...names, ...types])];
total.value = filtered.length;
tableApi.grid.loadData(filtered);
} else {
total.value = tableAllData.value.length;
tableApi.grid.loadData(tableAllData.value);
}
});
</script>
<template>
<div
:class="
cn(
'bg-background flex max-h-[100vh] w-[360px] flex-col overflow-y-hidden',
'rounded-lg',
'dict-type-card',
)
"
>
<div :class="cn('flex items-center justify-between', 'border-b px-4 py-2')">
<span class="font-semibold">字典项列表</span>
<Space>
<Tooltip title="刷新缓存">
<a-button
v-access:code="['system:dict:edit']"
:icon="h(SyncOutlined)"
@click="handleRefreshCache"
/>
</Tooltip>
<Tooltip :title="$t('pages.common.export')">
<a-button
v-access:code="['system:dict:export']"
:icon="h(ExportOutlined)"
@click="handleDownloadExcel"
/>
</Tooltip>
<Tooltip :title="$t('pages.common.add')">
<a-button
v-access:code="['system:dict:add']"
:icon="h(PlusOutlined)"
@click="handleAdd"
/>
</Tooltip>
</Space>
</div>
<div class="flex flex-1 flex-col overflow-y-hidden p-4">
<Alert
class="mb-4"
show-icon
message="如果你的数据量大 自行开启虚拟滚动"
/>
<Input
placeholder="搜索字典项名称/类型"
v-model:value="searchValue"
allow-clear
>
<template #addonAfter>
<Tooltip title="重置/刷新">
<SyncOutlined
v-access:code="['system:dict:edit']"
@click="handleReset"
/>
</Tooltip>
</template>
</Input>
<BasicTable class="flex-1 overflow-hidden">
<template #render="{ row: item }">
<div :class="cn('flex items-center justify-between px-2 py-2')">
<div class="flex flex-col items-baseline overflow-hidden">
<span class="font-medium">{{ item.dictName }}</span>
<div
class="max-w-full overflow-hidden text-ellipsis whitespace-nowrap"
>
{{ item.dictType }}
</div>
</div>
<div class="flex items-center gap-3 text-[17px]">
<EditOutlined
class="text-primary"
v-access:code="['system:dict:edit']"
@click.stop="handleEdit(item)"
/>
<Popconfirm
placement="left"
:title="`确认删除 [${item.dictName}]?`"
@confirm="handleDelete(item)"
>
<DeleteOutlined
v-access:code="['system:dict:remove']"
class="text-destructive"
@click.stop=""
/>
</Popconfirm>
</div>
</div>
</template>
</BasicTable>
</div>
<div class="border-t px-4 py-3"> {{ total }} 条数据</div>
<DictTypeModal @reload="tableApi.query()" />
</div>
</template>
<style lang="scss">
.dict-type-card {
.vxe-grid {
padding: 12px 0 0;
.vxe-body--row {
&.row--current {
// 选中行背景色
background-color: hsl(var(--accent-hover)) !important;
}
}
}
.ant-alert {
padding: 6px 12px;
}
}
</style>

View File

@@ -145,7 +145,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 200,
resizable: false,
width: 'auto',
},
];
@@ -218,6 +219,7 @@ export const drawerSchema: FormSchemaGetter = () => [
fieldName: 'orderNum',
help: '排序, 数字越小越靠前',
label: '显示排序',
defaultValue: 0,
rules: 'required',
},
{
@@ -236,6 +238,7 @@ export const drawerSchema: FormSchemaGetter = () => [
if (model.isFrame !== '0') {
return z
.string({ message: '请输入路由地址' })
.min(1, '请输入路由地址')
.refine((val) => !val.startsWith('/'), {
message: '路由地址不需要带/',
});

View File

@@ -4,16 +4,17 @@ import type { VbenFormProps } from '@vben/common-ui';
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { Menu } from '#/api/system/menu/model';
import { computed } from 'vue';
import { computed, ref } from 'vue';
import { useAccess } from '@vben/access';
import { Fallback, Page, useVbenDrawer } from '@vben/common-ui';
import { eachTree, getVxePopupContainer } from '@vben/utils';
import { $t } from '@vben/locales';
import { eachTree, getVxePopupContainer, treeToList } from '@vben/utils';
import { Popconfirm, Space } from 'ant-design-vue';
import { Popconfirm, Space, Switch, Tooltip } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { menuList, menuRemove } from '#/api/system/menu';
import { menuCascadeRemove, menuList, menuRemove } from '#/api/system/menu';
import { columns, querySchema } from './data';
import menuDrawer from './menu-drawer.vue';
@@ -67,6 +68,8 @@ const gridOptions: VxeGridProps = {
rowField: 'menuId',
// 自动转换为tree 由vxe处理 无需手动转换
transform: true,
// 刷新接口后 记录展开行的情况
reserve: true,
},
id: 'system-menu-index',
};
@@ -111,11 +114,41 @@ async function handleEdit(record: Menu) {
drawerApi.open();
}
/**
* 是否级联删除
*/
const cascadingDeletion = ref(false);
async function handleDelete(row: Menu) {
await menuRemove([row.menuId]);
if (cascadingDeletion.value) {
// 级联删除
const menuAndChildren: Menu[] = treeToList([row], { id: 'menuId' });
await menuCascadeRemove(menuAndChildren.map((item) => item.menuId));
} else {
// 单删除
await menuRemove([row.menuId]);
}
await tableApi.query();
}
function removeConfirmTitle(row: Menu) {
const menuName = $t(row.menuName);
if (!cascadingDeletion.value) {
return `是否确认删除 [${menuName}] ?`;
}
const menuAndChildren = treeToList([row], { id: 'menuId' });
if (menuAndChildren.length === 1) {
return `是否确认删除 [${menuName}] ?`;
}
return `是否确认删除 [${menuName}] 及 [${menuAndChildren.length - 1}]个子项目 ?`;
}
/**
* 编辑/添加成功后刷新表格
*/
async function afterEditOrAdd() {
tableApi.query();
}
/**
* 全部展开/折叠
* @param expand 是否展开
@@ -128,6 +161,9 @@ function setExpandOrCollapse(expand: boolean) {
/**
* 与后台逻辑相同
* 只有租户管理和超级管理能访问菜单管理
* 注意: 只有超管才能对菜单进行`增删改`操作
* 注意: 只有超管才能对菜单进行`增删改`操作
* 注意: 只有超管才能对菜单进行`增删改`操作
*/
const { hasAccessByRoles } = useAccess();
const isAdmin = computed(() => {
@@ -140,6 +176,16 @@ const isAdmin = computed(() => {
<BasicTable table-title="菜单列表" table-title-help="双击展开/收起子菜单">
<template #toolbar-tools>
<Space>
<Tooltip title="删除菜单以及子菜单">
<div
v-access:role="['superadmin']"
v-access:code="['system:menu:remove']"
class="flex items-center"
>
<span class="mr-2 text-sm text-[#666666]">级联删除</span>
<Switch v-model:checked="cascadingDeletion" />
</div>
</Tooltip>
<a-button @click="setExpandOrCollapse(false)">
{{ $t('pages.common.collapse') }}
</a-button>
@@ -149,6 +195,7 @@ const isAdmin = computed(() => {
<a-button
type="primary"
v-access:code="['system:menu:add']"
v-access:role="['superadmin']"
@click="handleAdd"
>
{{ $t('pages.common.add') }}
@@ -159,6 +206,7 @@ const isAdmin = computed(() => {
<Space>
<ghost-button
v-access:code="['system:menu:edit']"
v-access:role="['superadmin']"
@click="handleEdit(row)"
>
{{ $t('pages.common.edit') }}
@@ -168,6 +216,7 @@ const isAdmin = computed(() => {
v-if="row.menuType !== 'F'"
class="btn-success"
v-access:code="['system:menu:add']"
v-access:role="['superadmin']"
@click="handleSubAdd(row)"
>
{{ $t('pages.common.add') }}
@@ -175,12 +224,13 @@ const isAdmin = computed(() => {
<Popconfirm
:get-popup-container="getVxePopupContainer"
placement="left"
title="确认删除?"
:title="removeConfirmTitle(row)"
@confirm="handleDelete(row)"
>
<ghost-button
danger
v-access:code="['system:menu:remove']"
v-access:role="['superadmin']"
@click.stop=""
>
{{ $t('pages.common.delete') }}
@@ -189,7 +239,7 @@ const isAdmin = computed(() => {
</Space>
</template>
</BasicTable>
<MenuDrawer @reload="tableApi.query()" />
<MenuDrawer @reload="afterEditOrAdd" />
</Page>
<Fallback v-else description="您没有菜单管理的访问权限" status="403" />
</template>

View File

@@ -69,7 +69,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
resizable: false,
width: 'auto',
},
];

View File

@@ -28,7 +28,10 @@ export const querySchema: FormSchemaGetter = () => [
{
component: 'Select',
componentProps: {
options: getDictOptions(DictEnum.SYS_YES_NO),
options: [
{ label: '是', value: '0' },
{ label: '否', value: '1' },
],
},
fieldName: 'status',
label: '是否默认',
@@ -81,7 +84,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
resizable: false,
width: 'auto',
},
];

View File

@@ -131,6 +131,8 @@ const { hasAccessByCodes } = useAccess();
v-model:value="row.status"
:api="() => ossConfigChangeStatus(row)"
:disabled="!hasAccessByCodes(['system:ossConfig:edit'])"
checked-text=""
un-checked-text=""
@reload="tableApi.query()"
/>
</template>

View File

@@ -0,0 +1,11 @@
<!--
后端版本>=5.4.0 这个从本地路由变为从后台返回
未修改文件名 而是新加了这个文件
-->
<script setup lang="ts">
import OssConfigPage from '#/views/system/oss-config/index.vue';
</script>
<template>
<OssConfigPage />
</template>

View File

@@ -0,0 +1,2 @@
/** 支持的图片列表 */
export const supportImageList = ['jpg', 'jpeg', 'png', 'gif', 'webp'];

View File

@@ -69,12 +69,7 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
resizable: false,
width: 'auto',
},
];
/**
* 图片加载失败的fallback
*/
export const fallbackImageBase64 =
'data:image/png;base64,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';

View File

@@ -0,0 +1 @@
data:image/png;base64,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

View File

@@ -31,7 +31,11 @@ const [BasicModal, modalApi] = useVbenModal({
title="文件上传"
>
<div class="flex flex-col gap-4">
<FileUpload v-model:value="fileList" :enable-drag-upload="true" />
<FileUpload
v-model:value="fileList"
:enable-drag-upload="true"
:max-count="3"
/>
</div>
</BasicModal>
</template>

View File

@@ -31,7 +31,7 @@ const [BasicModal, modalApi] = useVbenModal({
title="图片上传"
>
<div class="flex flex-col gap-4">
<ImageUpload v-model:value="fileList" />
<ImageUpload v-model:value="fileList" :max-count="3" />
</div>
</BasicModal>
</template>

View File

@@ -33,7 +33,9 @@ import { ossDownload, ossList, ossRemove } from '#/api/system/oss';
import { calculateFileSize } from '#/utils/file';
import { downloadByData } from '#/utils/file/download';
import { columns, fallbackImageBase64, querySchema } from './data';
import { supportImageList } from './constant';
import { columns, querySchema } from './data';
import fallbackImageBase64 from './fallback-image.txt?raw';
import fileUploadModal from './file-upload-modal.vue';
import imageUploadModal from './image-upload-modal.vue';
@@ -154,7 +156,7 @@ function handleMultiDelete() {
const router = useRouter();
function handleToSettings() {
router.push('/system/oss-config');
router.push('/system/oss-config/index');
}
const preview = ref(false);
@@ -163,10 +165,32 @@ onMounted(async () => {
preview.value = previewStr === 'true';
});
/**
* 根据拓展名判断是否是图片
* @param ext 拓展名
*/
function isImageFile(ext: string) {
const supportList = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
return supportList.some((item) => ext.toLocaleLowerCase().includes(item));
return supportImageList.some((item) =>
ext.toLocaleLowerCase().includes(item),
);
}
/**
* 判断是否是pdf文件
* @param ext 扩展名
*/
function isPdfFile(ext: string) {
return ext.toLocaleLowerCase().includes('pdf');
}
/**
* pdf预览 使用浏览器接管
* @param url 文件地址
*/
function pdfPreview(url: string) {
window.open(url);
}
const [ImageUploadModal, imageUploadApi] = useVbenModal({
connectedComponent: imageUploadModal,
});
@@ -230,6 +254,12 @@ const [FileUploadModal, fileUploadApi] = useVbenModal({
</div>
</template>
</Image>
<!-- pdf预览 使用浏览器开新窗口 -->
<span
v-else-if="preview && isPdfFile(row.url)"
class="icon-[vscode-icons--file-type-pdf2] size-10 cursor-pointer"
@click.stop="pdfPreview(row.url)"
></span>
<span v-else>{{ row.url }}</span>
</template>
<template #action="{ row }">

View File

@@ -65,7 +65,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
resizable: false,
width: 'auto',
},
];
@@ -110,6 +111,7 @@ export const drawerSchema: FormSchemaGetter = () => [
fieldName: 'postSort',
label: '岗位排序',
rules: 'required',
defaultValue: 0,
},
{
component: 'RadioGroup',

View File

@@ -8,6 +8,7 @@ import { addFullName, cloneDeep } from '@vben/utils';
import { useVbenForm } from '#/adapter/form';
import { postAdd, postInfo, postUpdate } from '#/api/system/post';
import { getDeptTree } from '#/api/system/user';
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
import { drawerSchema } from './data';
@@ -50,8 +51,16 @@ async function setupDeptSelect() {
]);
}
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
{
initializedGetter: defaultFormValueGetter(formApi),
currentGetter: defaultFormValueGetter(formApi),
},
);
const [BasicDrawer, drawerApi] = useVbenDrawer({
onCancel: handleCancel,
onBeforeClose,
onClosed: handleClosed,
onConfirm: handleConfirm,
async onOpenChange(isOpen) {
if (!isOpen) {
@@ -67,31 +76,33 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
const record = await postInfo(id);
await formApi.setValues(record);
}
await markInitialized();
drawerApi.drawerLoading(false);
},
});
async function handleConfirm() {
try {
drawerApi.drawerLoading(true);
drawerApi.lock(true);
const { valid } = await formApi.validate();
if (!valid) {
return;
}
const data = cloneDeep(await formApi.getValues());
await (isUpdate.value ? postUpdate(data) : postAdd(data));
resetInitialized();
emit('reload');
await handleCancel();
drawerApi.close();
} catch (error) {
console.error(error);
} finally {
drawerApi.drawerLoading(false);
drawerApi.lock(false);
}
}
async function handleCancel() {
drawerApi.close();
async function handleClosed() {
await formApi.resetForm();
resetInitialized();
}
</script>

View File

@@ -37,6 +37,7 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
resizable: false,
width: 'auto',
},
];

View File

@@ -17,6 +17,7 @@ const emit = defineEmits<{ reload: [] }>();
const [BasicDrawer, drawerApi] = useVbenDrawer({
onConfirm: handleSubmit,
onCancel: handleReset,
destroyOnClose: true,
});
const route = useRoute();

View File

@@ -0,0 +1,11 @@
<!--
后端版本>=5.4.0 这个从本地路由变为从后台返回
未修改文件名 而是新加了这个文件
-->
<script setup lang="ts">
import RoleAssignPage from '#/views/system/role-assign/index.vue';
</script>
<template>
<RoleAssignPage />
</template>

View File

@@ -94,7 +94,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
resizable: false,
width: 'auto',
},
];
@@ -126,6 +127,7 @@ export const drawerSchema: FormSchemaGetter = () => [
fieldName: 'roleSort',
label: '角色排序',
rules: 'required',
defaultValue: 0,
},
{
component: 'Select',

View File

@@ -11,14 +11,7 @@ import { useAccess } from '@vben/access';
import { Page, useVbenDrawer, useVbenModal } from '@vben/common-ui';
import { getVxePopupContainer } from '@vben/utils';
import {
Dropdown,
Menu,
MenuItem,
Modal,
Popconfirm,
Space,
} from 'ant-design-vue';
import { Modal, Popconfirm, Space } from 'ant-design-vue';
import { useVbenVxeGrid, vxeCheckboxChecked } from '#/adapter/vxe-table';
import {
@@ -142,7 +135,7 @@ function handleAuthEdit(record: Role) {
const router = useRouter();
function handleAssignRole(record: Role) {
router.push(`/system/role-assign/${record.roleId}`);
router.push(`/system/role-auth/user/${record.roleId}`);
}
</script>
@@ -200,6 +193,18 @@ function handleAssignRole(record: Role) {
>
{{ $t('pages.common.edit') }}
</ghost-button>
<ghost-button
v-access:code="['system:role:edit']"
@click.stop="handleAuthEdit(row)"
>
权限
</ghost-button>
<ghost-button
v-access:code="['system:role:edit']"
@click.stop="handleAssignRole(row)"
>
分配
</ghost-button>
<Popconfirm
:get-popup-container="getVxePopupContainer"
placement="left"
@@ -215,21 +220,6 @@ function handleAssignRole(record: Role) {
</ghost-button>
</Popconfirm>
</Space>
<Dropdown placement="bottomRight">
<template #overlay>
<Menu>
<MenuItem key="1" @click="handleAuthEdit(row)">
数据权限
</MenuItem>
<MenuItem key="2" @click="handleAssignRole(row)">
分配用户
</MenuItem>
</Menu>
</template>
<a-button size="small" type="link">
{{ $t('pages.common.more') }}
</a-button>
</Dropdown>
</template>
</template>
</BasicTable>

View File

@@ -52,7 +52,7 @@ const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
const [BasicModal, modalApi] = useVbenModal({
fullscreenButton: false,
onBeforeClose,
onCancel: handleClosed,
onClosed: handleClosed,
onConfirm: handleConfirm,
onOpenChange: async (isOpen) => {
if (!isOpen) {

View File

@@ -85,6 +85,7 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
onBeforeClose,
onClosed: handleClosed,
onConfirm: handleConfirm,
destroyOnClose: true,
async onOpenChange(isOpen) {
if (!isOpen) {
return null;

View File

@@ -68,7 +68,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 200,
resizable: false,
width: 'auto',
},
];

View File

@@ -29,7 +29,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 180,
resizable: false,
width: 'auto',
},
];

View File

@@ -10,7 +10,7 @@ import { cloneDeep, eachTree } from '@vben/utils';
import { omit } from 'lodash-es';
import { useVbenForm } from '#/adapter/form';
import { menuTreeSelect, tenantPackageMenuTreeSelect } from '#/api/system/menu';
import { tenantPackageMenuTreeSelect } from '#/api/system/menu';
import {
packageAdd,
packageInfo,
@@ -40,30 +40,18 @@ const [BasicForm, formApi] = useVbenForm({
const menuTree = ref<MenuOption[]>([]);
async function setupMenuTree(id?: number | string) {
if (id) {
const resp = await tenantPackageMenuTreeSelect(id);
const menus = resp.menus;
// i18n处理
eachTree(menus, (node) => {
node.label = $t(node.label);
});
// 设置菜单信息
menuTree.value = resp.menus;
// keys依赖于menu 需要先加载menu
await nextTick();
await formApi.setFieldValue('menuIds', resp.checkedKeys);
} else {
const resp = await menuTreeSelect();
// i18n处理
eachTree(resp, (node) => {
node.label = $t(node.label);
});
// 设置菜单信息
menuTree.value = resp;
// keys依赖于menu 需要先加载menu
await nextTick();
await formApi.setFieldValue('menuIds', []);
}
// 0为新增使用 获取除了`租户管理`的所有菜单
const resp = await tenantPackageMenuTreeSelect(id ?? 0);
const menus = resp.menus;
// i18n处理
eachTree(menus, (node) => {
node.label = $t(node.label);
});
// 设置菜单信息
menuTree.value = menus;
// keys依赖于menu 需要先加载menu
await nextTick();
await formApi.setFieldValue('menuIds', resp.checkedKeys);
}
async function customFormValueGetter() {
@@ -85,6 +73,7 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
onBeforeClose,
onClosed: handleClosed,
onConfirm: handleConfirm,
destroyOnClose: true,
async onOpenChange(isOpen) {
if (!isOpen) {
return null;

View File

@@ -0,0 +1,6 @@
<template>
<div>
ele版本会使用这个文件 只是为了不报错`未找到对应组件`才新建的这个文件
无实际意义
</div>
</template>

View File

@@ -87,7 +87,7 @@ export const columns: VxeGridProps['columns'] = [
slots: { default: 'action' },
title: '操作',
resizable: false,
width: 180,
width: 'auto',
},
];

View File

@@ -80,6 +80,7 @@ onMounted(loadTree);
v-model:value="searchValue"
:placeholder="$t('pages.common.search')"
size="small"
allow-clear
>
<template #enterButton>
<a-button @click="handleReload">
@@ -102,9 +103,9 @@ onMounted(loadTree);
@select="$emit('select')"
>
<template #title="{ label }">
<span v-if="label.indexOf(searchValue) > -1">
<span v-if="label.includes(searchValue)">
{{ label.substring(0, label.indexOf(searchValue)) }}
<span style="color: #f50">{{ searchValue }}</span>
<span class="text-primary">{{ searchValue }}</span>
{{
label.substring(
label.indexOf(searchValue) + searchValue.length,

View File

@@ -20,7 +20,7 @@ import {
} from '@vben/icons';
import { useClipboard } from '@vueuse/core';
import { Skeleton, Tree } from 'ant-design-vue';
import { Alert, Skeleton, Tree } from 'ant-design-vue';
import { previewCode } from '#/api/tool/gen';
@@ -185,6 +185,11 @@ const { copy } = useClipboard({ legacy: true });
</div>
</template>
</Tree>
<Alert
class="mt-2"
show-icon
message="👆显示的名称为模板的文件名,非最终下载文件名..."
/>
</div>
<CodeMirror
v-model="codeContent"

View File

@@ -4,9 +4,10 @@ import type { Ref } from 'vue';
import type { VxeGridProps } from '#/adapter/vxe-table';
import type { GenInfo } from '#/api/tool/gen/model';
import { inject } from 'vue';
import { inject, onMounted, reactive } from 'vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import { dictOptionSelectList } from '#/api/system/dict/dict-type';
import { validRules, vxeTableColumns } from './gen-data';
@@ -15,8 +16,26 @@ import { validRules, vxeTableColumns } from './gen-data';
*/
const genInfoData = inject('genInfoData') as Ref<GenInfo['info']>;
const dictOptions = reactive<{ label: string; value: string }[]>([
{ label: '未设置', value: '' },
]);
/**
* 加载字典下拉数据
*/
onMounted(async () => {
const resp = await dictOptionSelectList();
const options = resp.map((dict) => ({
label: `${dict.dictName} | ${dict.dictType}`,
value: dict.dictType,
}));
dictOptions.push(...options);
});
const gridOptions: VxeGridProps = {
columns: vxeTableColumns,
columns: vxeTableColumns(dictOptions),
keepSource: true,
editConfig: { trigger: 'click', mode: 'cell', showStatus: true },
editRules: validRules,

View File

@@ -2,14 +2,10 @@ import type { Recordable } from '@vben/types';
import type { VxeGridProps } from '#/adapter/vxe-table';
import { reactive } from 'vue';
import { getPopupContainer } from '@vben/utils';
import { Checkbox, Input, Select } from 'ant-design-vue';
import { dictOptionSelectList } from '#/api/system/dict/dict-type';
const JavaTypes: string[] = [
'Long',
'String',
@@ -45,24 +41,6 @@ const componentsOptions = [
{ label: '富文本', value: 'editor' },
];
const dictOptions = reactive<{ label: string; value: string }[]>([
{ label: '未设置', value: '' },
]);
/**
* 在这里初始化字典下拉框
*/
(async function init() {
const ret = await dictOptionSelectList();
ret.forEach((dict) => {
const option = {
label: `${dict.dictName} | ${dict.dictType}`,
value: dict.dictType,
};
dictOptions.push(option);
});
})();
function renderBooleanTag(row: Recordable<any>, field: string) {
const value = row[field] ? '是' : '否';
const className = row[field] ? 'text-green-500' : 'text-red-500';
@@ -78,7 +56,10 @@ export const validRules: VxeGridProps['editRules'] = {
javaField: [{ required: true, message: '请输入' }],
};
export const vxeTableColumns: VxeGridProps['columns'] = [
// 内部依赖的字典从外部通过函数传入
export const vxeTableColumns: (
dictOptions: { label: string; value: string }[],
) => VxeGridProps['columns'] = (dictOptions) => [
{
title: '序号',
type: 'seq',

View File

@@ -0,0 +1,10 @@
<!--
后端版本>=5.4.0 这个从本地路由变为从后台返回
-->
<script setup lang="ts">
import EditGenPage from './edit-gen.vue';
</script>
<template>
<EditGenPage />
</template>

View File

@@ -110,7 +110,7 @@ function handlePreview(record: Recordable<any>) {
const router = useRouter();
function handleEdit(record: Recordable<any>) {
router.push(`/code-gen/edit/${record.tableId}`);
router.push(`/tool/gen-edit/index/${record.tableId}`);
}
async function handleSync(record: Recordable<any>) {

View File

@@ -86,11 +86,13 @@ const gridOptions: VxeGridProps = {
},
},
rowConfig: {
keyField: 'tableId',
keyField: 'tableName',
},
toolbarConfig: {
enabled: false,
},
id: 'import-table-modal',
cellClassName: 'cursor-pointer',
};
const [BasicTable, tableApi] = useVbenVxeGrid({ formOptions, gridOptions });

View File

@@ -33,7 +33,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 200,
resizable: false,
width: 'auto',
},
];

View File

@@ -42,6 +42,7 @@ import { renderDict } from '#/utils/render';
import { approvalModal, approvalRejectionModal, flowInterfereModal } from '.';
import ApprovalDetails from './approval-details.vue';
import FlowPreview from './flow-preview.vue';
import { approveWithReasonModal } from './helper';
import userSelectModal from './user-select-modal.vue';
@@ -442,10 +443,7 @@ async function handleCopy(text: string) {
/>
</TabPane>
<TabPane key="2" tab="审批流程图">
<img
:src="`data:image/png;base64,${currentFlowInfo.image}`"
class="rounded-lg border"
/>
<FlowPreview :instance-id="currentFlowInfo.instanceId" />
</TabPane>
</Tabs>
</div>

View File

@@ -6,6 +6,7 @@ import { stringify } from '@vben/request';
import { useAccessStore } from '@vben/stores';
import { useEventListener } from '@vueuse/core';
import { Alert } from 'ant-design-vue';
defineOptions({ name: 'FlowDesigner' });
@@ -48,5 +49,13 @@ useEventListener('message', messageHandler);
</script>
<template>
<iframe :src="url" class="size-full"></iframe>
<div class="size-full">
<Alert
class="mx-4 my-2"
type="warning"
:show-icon="true"
message="这是iframe页面! iframe页面! iframe页面! 不是我写的真服了"
/>
<iframe :src="url" class="size-full"></iframe>
</div>
</template>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
import { useAppConfig } from '@vben/hooks';
import { stringify } from '@vben/request';
import { useAccessStore } from '@vben/stores';
import { useWarmflowIframe } from './hook';
defineOptions({ name: 'FlowPreview' });
const props = defineProps<{ instanceId: string }>();
const { clientId } = useAppConfig(import.meta.env, import.meta.env.PROD);
const accessStore = useAccessStore();
const params = {
Authorization: `Bearer ${accessStore.accessToken}`,
id: props.instanceId,
clientid: clientId,
type: 'FlowChart',
};
/**
* iframe地址
*/
const url = `${import.meta.env.VITE_GLOB_API_URL}/warm-flow-ui/index.html?${stringify(params)}`;
const { iframeRef } = useWarmflowIframe();
</script>
<template>
<iframe ref="iframeRef" :src="url" class="h-[500px] w-full border"></iframe>
</template>

View File

@@ -0,0 +1,37 @@
import { onMounted, useTemplateRef, watch } from 'vue';
import { usePreferences } from '@vben/preferences';
/**
* warmflow ref相关操作
* @returns hook
*/
export function useWarmflowIframe() {
const iframeRef = useTemplateRef<HTMLIFrameElement>('iframeRef');
const { isDark } = usePreferences();
onMounted(() => {
/**
* load只是iframe加载完 而非vue加载完
*/
iframeRef.value?.addEventListener('load', async () => {
/**
* TODO: 这里可以优化 因为拿不到内部vue的mount状态
*/
await new Promise((resolve) => setTimeout(resolve, 500));
const theme = isDark.value ? 'theme-dark' : 'theme-light';
iframeRef.value?.contentWindow?.postMessage({ type: theme });
});
});
// 监听主题切换 通知iframe切换
watch(isDark, (dark) => {
if (!iframeRef.value) {
return;
}
const theme = dark ? 'theme-dark' : 'theme-light';
iframeRef.value.contentWindow?.postMessage({ type: theme });
});
return { iframeRef };
}

View File

@@ -91,7 +91,8 @@ export const columns: VxeGridProps['columns'] = [
fixed: 'right',
slots: { default: 'action' },
title: '操作',
width: 210,
resizable: false,
width: 'auto',
},
];

View File

@@ -0,0 +1,11 @@
<!--
后端版本>=5.4.0 这个从本地路由变为从后台返回
未修改文件名 而是新加了这个文件
-->
<script setup lang="ts">
import LeaveFormPage from './leave-form.vue';
</script>
<template>
<LeaveFormPage />
</template>

View File

@@ -1,7 +1,9 @@
<script setup lang="ts">
import type { PropType } from 'vue';
import type { CategoryTree } from '#/api/workflow/category/model';
import { onMounted, type PropType, ref } from 'vue';
import { onMounted, ref } from 'vue';
import { SyncOutlined } from '@ant-design/icons-vue';
import { InputSearch, Skeleton, Tree } from 'ant-design-vue';
@@ -71,6 +73,7 @@ onMounted(loadTree);
v-model:value="searchValue"
:placeholder="$t('pages.common.search')"
size="small"
allow-clear
>
<template #enterButton>
<a-button @click="handleReload">
@@ -93,9 +96,9 @@ onMounted(loadTree);
@select="$emit('select')"
>
<template #title="{ label }">
<span v-if="label.indexOf(searchValue) > -1">
<span v-if="label.includes(searchValue)">
{{ label.substring(0, label.indexOf(searchValue)) }}
<span style="color: #f50">{{ searchValue }}</span>
<span class="text-primary">{{ searchValue }}</span>
{{
label.substring(
label.indexOf(searchValue) + searchValue.length,

View File

@@ -63,7 +63,7 @@ export const columns: VxeGridProps['columns'] = [
slots: { default: 'action' },
title: '操作',
resizable: false,
width: 200,
width: 'auto',
},
];

View File

@@ -0,0 +1,11 @@
<!--
后端版本>=5.4.0 这个从本地路由变为从后台返回
未修改文件名 而是新加了这个文件
-->
<script setup lang="ts">
import FlowDesignerPage from '../components/flow-designer.vue';
</script>
<template>
<FlowDesignerPage />
</template>

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