226 Commits
1.5.1 ... 1.5.3

Author SHA1 Message Date
dap
78f6a6a3f2 chore: 更新依赖版本并迁移至pnpm catalog管理
将多个依赖从固定版本号迁移至pnpm catalog统一管理,提升依赖一致性
移除未使用的UI框架依赖(element-plus、naive-ui、tdesign-vue-next)
为echarts添加冲突解决配置以支持特定版本需求
2026-02-03 10:25:43 +08:00
dap
87a2071dab docs: 更新 CHANGELOG.md 为版本 1.5.3 发布内容
更新版本号至 1.5.3 并添加版本说明,明确对应后端版本及更新性质为纯前端更新
2026-02-03 10:24:43 +08:00
dap
81442f7b8d docs: 更新README.zh-CN.md的分支和预览地址信息
更新分支说明,明确main、dev和antdv-next分支的定位和预览地址。
将代码生成模板的描述从“付费功能”修改为“不再放出,建议用AI照着抄”。
2026-02-03 10:23:18 +08:00
dap
c8d60e7e27 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2026-02-03 10:15:43 +08:00
ming4762
c48943bc67 fix: fix Nested Objects dependencies not effective (#7345) 2026-01-31 16:44:20 +08:00
xingyu
7680b33b99 fix: #7140 (#7153)
* chore: add yaml eslint validate

* chore: update deps

* fix: unused ts lint

* fix: 弹窗只能点击一次 #7140

* chore: update actions/checkout v6

* chore: update node version v22.22.0
2026-01-28 18:05:20 +08:00
dap
bb6bd2ea2e Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2026-01-27 16:57:31 +08:00
Jin Mao
bb5d75bc7e fix: 修复表单展开无效 (#7148)
- 修正模板中 ref 属性的引用名称
2026-01-27 11:35:50 +08:00
dap
349b518a31 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2026-01-26 18:58:05 +08:00
ming4762
528395e2c3 perf: optimizing hidden fields cannot trigger dependencies (#7142) 2026-01-26 16:12:26 +08:00
programmer
6a9012e5e4 chore: 给个人中心的2个按钮加上 i18n (#7138)
* fix: 个人中心按钮i18n

* fix: eslint format

* fix: 调整label宽度让英文显示在一行

* chore: 调整label小点

* fix: import

---------

Co-authored-by: Jin Mao <50581550+jinmao88@users.noreply.github.com>
2026-01-26 16:12:09 +08:00
橙子
6e8315ab40 fix: arguments order update (#7132)
Co-authored-by: Jin Mao <50581550+jinmao88@users.noreply.github.com>
2026-01-26 16:11:37 +08:00
Jin Mao
7cb2699f19 chore: 更新pnpm工作区配置
- 移除 neverBuiltDependencies 配置
- 移除 peerDependencyRules.allowedVersions 配置
- 重新整理 overrides 和 catalog 部分
- 保持 @ast-grep/napi 等依赖的目录引用配置
- 维持 eslint 版本约束在目录顶层配置中
2026-01-25 19:55:53 +08:00
Jin Mao
4b5da81ba6 chore(deps): 更新项目依赖包版本
- 更新 @playwright/test 从 1.57.0 到 1.58.0
- 更新 @tanstack/vue-query 从 5.92.8 到 5.92.9
- 更新 cheerio 从 1.1.2 到 1.2.0
- 更新 eslint-config-turbo 从 2.7.5 到 2.7.6
- 更新 playwright 从 1.57.0 到 1.58.0
- 更新 turbo 从 2.7.5 到 2.7.6
- 更新 vxe-pc-ui 从 4.12.10 到 4.12.12
- 更新 @babel/helper-define-polyfill-provider 从 0.6.5 到 0.6.6
- 更新 @cspell/dict-fullstack 从 3.2.7 到 3.2.8
- 更新 @cspell/dict-git 从 3.0.7 到 3.1.0
- 更新 @cspell/dict-node 从 5.0.8 到 5.0.9
- 更新 @cspell/dict-npm 从 5.2.29 到 5.2.30
- 更新 @parcel/watcher 相关包从 2.5.4 到 2.5.6
- 更新 @tanstack/query-core 从 5.90.19 到 5.90.20
- 更新 babel-plugin-polyfill 相关包到最新版本
- 更新 baseline-browser-mapping 从 2.9.17 到 2.9.18
- 更新 caniuse-lite 从 1.0.30001765 到 1.0.30001766
- 更新 electron-to-chromium 从 1.5.277 到 1.5.278
- 更新 eslint-plugin-turbo 从 2.7.5 到 2.7.6
- 更新 playwright-core 从 1.57.0 到 1.58.0
- 更新 turbo 平台特定版本到 2.7.6
- 更新 undici 从 7.19.0 到 7.19.1
2026-01-25 15:05:14 +08:00
Jin Mao
6aca9a9c99 test(dom): 更新元素可见区域计算的测试用例
- 修正了getElementVisibleRect函数的期望值断言
- 将bottom值从800更正为0以匹配实际计算结果
- 将left值从1100更正为0以匹配实际计算结果
- 将right值从1000更正为0以匹配实际计算结果
- 将top值从900更正为0以匹配实际计算结果
2026-01-25 14:22:22 +08:00
xingyu4j
fa195fde8e fix: lint 2026-01-23 14:48:21 +08:00
xingyu4j
1057f2932b chore: update deps 2026-01-23 14:48:06 +08:00
Jin Mao
b9224fc379 Merge branch 'main' into fix 2026-01-23 13:48:54 +08:00
Jin Mao
57dd818170 Merge branch 'fork/kuchaguangjie/fix' 2026-01-23 13:47:54 +08:00
Jin Mao
49256ec1b7 Merge branch 'main' into fix 2026-01-23 13:46:59 +08:00
Jin Mao
f6f92e5403 Merge branch 'fork/kuchaguangjie/fix' 2026-01-23 13:45:38 +08:00
Jin Mao
613c311076 Merge branch 'fork/abcd0f/dev/sun-local' 2026-01-23 13:25:03 +08:00
Jin Mao
9ee7a7d9ff Merge branch 'fork/Child-qjj/patch-1' 2026-01-23 13:21:49 +08:00
橙子
44f8aed06d fix: timer not need reactivity (#7128) 2026-01-23 13:16:09 +08:00
Sun
d5d4a5c591 feat(effects-plugins): 添加 echarts 图表更新功能
新增 updateDate 方法用于更新 echarts 图表选项,支持合并配置、
完全替换和延迟更新等模式。该方法会在组件未初始化时自动执
行首次渲染,并能够合并全局配置如 backgroundColor 等选项。
2026-01-23 11:06:45 +08:00
yuhengshen
74381aa8c1 fix: 嵌套弹窗,错误 merge options (#7126) 2026-01-22 20:07:13 +08:00
橙子
203ee9b623 fix(@vben-core/shared): element outside viewport, the element visible rect each prop expect 0 (#7120)
* fix(@vben-core/shared): element outside viewport

* fix(@vben-core/shared): element outside viewport
2026-01-22 11:37:01 +08:00
JyQAQ
6c8c49966a Perf: 优化antd upload组件参数获取 (#7114)
* perf(antd upload params): 优化组件参数取值 确保不同调用场景配置参数可用

* perf(antd upload params): 优化组件参数取值 确保不同调用场景配置参数可用

* perf(antd upload params): 优化组件参数取值 确保不同调用场景配置参数可用

* perf(antd upload params): 优化组件参数取值 确保不同调用场景配置参数可用
2026-01-21 17:20:53 +08:00
Qiu
3862942e9f fix: chart instance disposal condition
dom has been disposed in vue3 v-if,but chartInstance exist
2026-01-21 11:47:01 +08:00
xingyu
8571fc43b0 Merge branch 'main' into fix 2026-01-19 15:28:12 +08:00
JyQAQ
59aabd956d Perf: Optimization of cropping component result acquisition & optimization of cropping frame prompts (#7111)
* perf(cropper): enhance image cropping functionality and add output type support

* perf(cropper): enhance image cropping functionality and add output type support
2026-01-19 14:18:36 +08:00
xingyu
9b09ba4483 Merge branch 'main' into fix 2026-01-19 10:54:43 +08:00
橙子
686c3f9208 docs(@vben-core/preferences): update comments (#7107) 2026-01-14 19:36:45 +08:00
MRSWC
8a7e2bd8e4 fix:修复默认默认首页如果携带参数时刷新页面参数丢失问题 (#7102)
Co-authored-by: chenwei <chenw@jiuzhekan.com>
2026-01-14 15:38:55 +08:00
JyQAQ
174c4ae749 fix(antd Upload onChange Event): rewrite onChange event to handle upl… (#7098)
* fix(antd Upload onChange Event): rewrite onChange event to handle upload success or error messages

* fix(antd Upload onChange Event): rewrite onChange event to handle upload success or error messages

* fix(antd Upload onChange Event): rewrite onChange event to handle upload success or error messages
2026-01-14 15:38:21 +08:00
JyQAQ
67da9417a8 feat(upload prop:crop,aspectRatio): from Upload component accept prop… (#7095)
* feat(upload prop:crop,aspectRatio): from Upload component accept prop crop,aspectRatio

* feat(upload prop:crop,aspectRatio): from Upload component accept prop crop,aspectRatio

* feat(upload prop:crop,aspectRatio): from Upload component accept prop crop,aspectRatio

* feat(upload prop:crop,aspectRatio): from Upload component accept prop crop,aspectRatio
2026-01-14 15:38:05 +08:00
ppxb
f4a4ced88d fix: header auto mode issue (#7096) 2026-01-12 21:40:26 +08:00
ppxb
19b2d7af41 fix: tdesign theme toggle and demos (#7087) 2026-01-10 14:11:08 +08:00
yuhengshen
343d8a1c1e fix: 切换时区后,页面不刷新 (#7085)
* fix: 切换时区后,页面不刷新

* fix: keep-alive 的页面,i18n 和 timezone 不更新
2026-01-10 14:10:27 +08:00
JyQAQ
9480f8272a feat(common-ui cropper): Implement the image cropping component VCropper (#7082)
* feat(common-ui cropper): Implement the image cropping component VCropper

* feat(common-ui cropper): Implement the image cropping component VCropper

* feat(common-ui cropper): Implement the image cropping component VCropper

* feat(common-ui cropper): Implement the image cropping component VCropper

* feat(common-ui cropper): Implement the image cropping component VCropper
2026-01-10 14:08:15 +08:00
ppxb
0d9e260a6a fix: vite.config.mts type error (#7081) 2026-01-10 14:07:28 +08:00
ppxb
51bca25345 fix(lint): pnpm format lint warning (#7080) 2026-01-10 14:06:03 +08:00
eric
694396dcfb refactor: move cleanup to finally block 2026-01-09 23:22:49 +08:00
eric
1cb53e943e refactor: 将跳转放到最后 2026-01-09 23:13:06 +08:00
eric
13c8318adc refactor: 1. 用 ref 包装 flag; 2. 在最后 清理 flag; 2026-01-09 23:05:05 +08:00
eric
48ed797055 fix: 防止 /logout 死循环 2026-01-09 22:38:11 +08:00
dap
1383f63361 refactor: 使用antd组件替换密码登录表单
- 废弃原有的shadcn-ui验证码组件,改用antd实现
- 更新登录表单中的验证码输入组件为新的antd实现
- 调整表单布局和样式
2026-01-08 11:47:37 +08:00
dap
dbe8beb7f9 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2026-01-08 11:18:02 +08:00
xingyu4j
3c2285141c chore: update deps 2026-01-06 13:58:42 +08:00
xingyu
49b884c0b1 Merge branch 'main' into fix 2026-01-06 13:50:26 +08:00
ppxb
24d20ca9ee refactor: preference manager and export (#7068)
* fix: preferences drawer outline z-index

* refactor: preferencesManager and exports
2026-01-06 12:42:32 +08:00
wyc001122
6f02181024 fix(layout): 修复双列模式下重复显示logo的问题(#7071) (#7072) 2026-01-05 21:13:06 +08:00
dap
3566191cd1 merge 2026-01-05 20:42:25 +08:00
ppxb
ed3353a271 fix: preferences drawer outline z-index (#7067) 2026-01-04 14:44:33 +08:00
xingyu4j
965f5f96b7 chore: update deps 2026-01-04 11:03:19 +08:00
xingyu4j
61d9df7f58 feat: add cascader cspell:words 2026-01-04 11:00:07 +08:00
xingyu4j
231a5169ec fix: lint 2026-01-04 10:56:55 +08:00
xingyu4j
ce7b7b910a Merge branch 'main' of https://github.com/xingyu4j/vue-vben-admin into fix 2026-01-04 10:56:14 +08:00
JyQAQ
81a61558cb feat(upload prop:maxSize): from Upload component accept prop maxSize (AI prompt fixed) (#7059)
* feat(upload prop:maxSize): from component accept prop maxSize

* feat(upload prop:maxSize): from component accept prop maxSize

* feat(upload prop:maxSize): from component accept prop maxSize

* feat(upload prop:maxSize): from component accept prop maxSize
2026-01-03 13:19:40 +08:00
ppxb
7d2bc2e885 fix: dropdown raido menu type error (#7062)
* fix: dropdown raido menu type

* chore: format code
2026-01-02 14:25:19 +08:00
luoqiz
89b237f6b4 feat: 添加上下文菜单演示,添加菜单项隐藏性 (#7057)
Co-authored-by: luoqiz <851092732@qq.com>
2026-01-02 14:22:19 +08:00
JyQAQ
a1bb132233 feat(api-cascader): 添加联级组件ApiCascader (#7031) 2025-12-22 20:00:31 +08:00
zhenghaoyang24
022d538940 Fix formatting in thin.md for clarity (#7008)
修改一些语句错误
2025-12-22 19:58:05 +08:00
xueyitt
ccf70a1b76 feat: 修正菜单排序在二级菜单不生效问题 (#7007)
* treeUtil增加对树形结构数据进行递归排序

* 菜单sort排序各级菜单均生效
2025-12-22 19:57:21 +08:00
xingyu4j
af3fe53ec8 fix: type error 2025-12-22 17:50:06 +08:00
xingyu4j
e981fb159f chore: update deps 2025-12-22 17:49:51 +08:00
xingyu
79b9d55854 Merge branch 'main' into fix 2025-12-22 16:42:15 +08:00
dap
3dca100349 chore: 添加实验性最小分块大小配置以优化性能
添加 experimentalMinChunkSize 配置项,设置为 20KB 以优化打包时的分块策略
2025-12-18 15:00:56 +08:00
xingyu4j
b2055a4457 chore: update deps 2025-12-17 13:47:37 +08:00
JyQAQ
1479f159aa feat(CellImage): CellImage组件支持图片属性写入 (#6992) 2025-12-06 10:12:58 +08:00
xingyu4j
7bf7e09002 fix: lint 2025-12-05 15:09:43 +08:00
xingyu4j
de8d39ffed chore: workspace file is deprecated 2025-12-05 15:07:39 +08:00
xingyu4j
543a7e3962 chore: update deps 2025-12-05 14:55:44 +08:00
xingyu4j
9dfe3f5af8 fix: type not find 2025-12-04 18:03:55 +08:00
xingyu4j
f11b08d8cb chore: update deps 2025-12-04 17:59:12 +08:00
xingyu4j
45b6f08984 chore: neverBuiltDependencies 2025-12-03 16:34:14 +08:00
xingyu4j
92a4676f8d chore: update version 2025-12-03 16:27:14 +08:00
xingyu4j
7bf7c0bb06 chore: remove unused deps 2025-12-03 16:26:58 +08:00
xingyu4j
8f8cf5b704 chore: update deps 2025-12-03 16:22:14 +08:00
xingyu4j
6be238430d chore: add cspell 2025-12-03 16:12:08 +08:00
xingyu4j
f77216d8f4 feat: lint add pnpm config 2025-12-03 16:11:51 +08:00
xingyu4j
d42a9b2409 feat: lint add yaml config 2025-12-03 16:01:27 +08:00
xingyu4j
6753834054 fix: lint 2025-12-03 15:37:33 +08:00
xingyu4j
77a4a64eb4 fix: stylelint 2025-12-03 15:37:20 +08:00
xingyu4j
49db40d557 feat: cspell sort 2025-12-03 15:37:04 +08:00
xingyu4j
0032c608f1 chore: update deps 2025-12-03 15:36:43 +08:00
xingyu4j
fa603b32b1 fix: locales 2025-12-03 13:51:30 +08:00
JyQAQ
9105d4d14a feat(api-component): api-component组件的options支持指定disabled值 (#6991) 2025-12-03 10:03:23 +08:00
dap
8c08fd683d Merge branch 'fix-vxe-1201' into dev 2025-12-01 15:57:40 +08:00
dap
5fee909fa3 chore: 锁定版本 2025-12-01 15:55:38 +08:00
luoqiz
c76db7d8d1 fix: 修复icon丢失根属性导致的样式错误 (#6986) 2025-12-01 09:51:27 +08:00
dap
880a768f34 merge 2025-11-30 01:57:43 +08:00
dap
e595a9712f merge 2025-11-30 01:57:18 +08:00
Jin Mao
6f39e9136e Merge branch 'main' into feature/antd上传组件支持调用Image组件查看图片 2025-11-24 21:59:34 +08:00
milletpeak
1f1ba16ead Merge branch 'main' into milletpeak-fontsize 2025-11-24 08:55:54 +08:00
panda7
b6edc5f574 fix: centered 为 true 时设置 draggable 无法拖拽 (#6948)
Co-authored-by: sqchen <chenshiqi@sshlx.com>
2025-11-24 08:38:10 +08:00
Jin Mao
8b0f138100 Merge branch 'main' into deps 2025-11-24 08:23:02 +08:00
ming4762
a810cd0b48 fix: fix IconPicker reporting an error when using search if no icon is found (#6944)
* 修复未搜索图标时分页报错的问题
 * 优化`IconPicker` 的分页逻辑,由total触发跳转到第一页,而不是用户控制
2025-11-24 07:56:55 +08:00
aonoa
b17fec41b0 fix: tree component cannot set a value (#6941)
Signed-off-by: aonoa <1991849113@qq.com>
2025-11-24 07:56:16 +08:00
Rex
5fcfabf1d4 fix: 修复菜单管理中标题栏样式异常问题 (#6934) 2025-11-24 07:00:08 +08:00
yuan.ji
1d77b018bb feat(function): add antd上传组件支持调用Image组件查看图片 2025-11-21 17:33:59 +08:00
米山
f7d9d1b1af chore: update package.json and app.vue imports, and ensure global styles are included 2025-11-19 11:13:06 +08:00
米山
aaf0274fe9 feat: add menu font size variable and update related components
- Introduced a new CSS variable `--menu-font-size` calculated from the base font size.
- Updated `PreferenceManager` to trigger CSS variable updates when `fontSize` is modified.
- Adjusted `updateCSSVariables` to set the new `--menu-font-size` based on the theme's font size.
- Ensured that the menu components utilize the updated font size with `!important` to maintain styling consistency.
2025-11-19 10:51:10 +08:00
米山
c142af482b fix: update snapshot for defaultPreferences immutability test to reflect fontSize change
- Adjusted the snapshot to ensure consistency with the updated defaultPreferences configuration, specifically retaining the fontSize property.
2025-11-19 10:19:16 +08:00
米山
cd7c11c7d0 fix: run 'pnpm format' update various components and improve layout structure
- Updated demo-preview and preview-group components for better error handling and layout.
- Enhanced drawer and modal components for improved auto-height functionality.
- Refactored layout components including header, footer, sidebar, and tabbar for better responsiveness and usability.
- Adjusted tooltip and help tooltip components for better user guidance.
- Fixed issues in various UI components to ensure consistent styling and functionality across the application.
2025-11-19 10:14:04 +08:00
milletpeak
fb8f36eeec Merge branch 'main' into milletpeak-fontsize 2025-11-19 09:41:55 +08:00
dap
13f5e949a9 Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-11-18 16:24:31 +08:00
xingyu4j
9a4f0f6ab1 feat: Consider multi-line formatting for readability 2025-11-17 14:24:43 +08:00
xingyu4j
bed52983a0 fix: lint 2025-11-17 12:15:14 +08:00
xingyu4j
655ce2c2e8 chore: update node version >=20.12.0 2025-11-17 12:11:43 +08:00
xingyu4j
f841568e5a chore: update vue-tsc 2025-11-17 12:04:30 +08:00
xingyu4j
469697845c fix: sort 2025-11-17 11:21:49 +08:00
xingyu4j
273f3cbaf8 Merge branch 'main' of https://github.com/xingyu4j/vue-vben-admin into deps 2025-11-17 11:21:16 +08:00
xingyu4j
96f671908e chore: update deps 2025-11-17 11:17:47 +08:00
Jin Mao
f09aace765 fix: 更新依赖后的表格显示问题 2025-11-14 21:44:32 +08:00
Copilot
772529e2fb fix: rollback zod-defaults version to 0.1.3 (#6925)
* Initial plan

* fix: downgrade zod-defaults from 0.2.3 to 0.1.3 for Zod v3 compatibility

Co-authored-by: likui628 <90845831+likui628@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: likui628 <90845831+likui628@users.noreply.github.com>
2025-11-14 16:16:45 +08:00
xingyu4j
eeff017b9f chore: add tdesign cspell 2025-11-14 10:22:18 +08:00
xingyu4j
22394ebdeb chore: move tdesign-vue-next to workspace 2025-11-14 10:21:48 +08:00
dependabot[bot]
ffbc9b22a7 chore(deps): bump the non-breaking-changes group across 1 directory with 26 updates (#6914)
Bumps the non-breaking-changes group with 20 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [vue](https://github.com/vuejs/core) | `3.5.23` | `3.5.24` |
| [@clack/prompts](https://github.com/bombshell-dev/clack/tree/HEAD/packages/prompts) | `0.10.1` | `0.11.0` |
| [@iconify/json](https://github.com/iconify/icon-sets) | `2.2.404` | `2.2.406` |
| [@pnpm/workspace.read-manifest](https://github.com/pnpm/pnpm) | `1000.2.5` | `1000.2.6` |
| [@tanstack/vue-query](https://github.com/TanStack/query/tree/HEAD/packages/vue-query) | `5.90.7` | `5.91.0` |
| [@tanstack/vue-store](https://github.com/TanStack/store/tree/HEAD/packages/vue-store) | `0.7.7` | `0.8.0` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.46.3` | `8.46.4` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.46.3` | `8.46.4` |
| [@vue/shared](https://github.com/vuejs/core/tree/HEAD/packages/shared) | `3.5.23` | `3.5.24` |
| [commitlint-plugin-function-rules](https://github.com/vidavidorra/commitlint-plugin-function-rules) | `4.1.0` | `4.1.1` |
| [eslint-config-turbo](https://github.com/vercel/turborepo/tree/HEAD/packages/eslint-config-turbo) | `2.6.0` | `2.6.1` |
| [lucide-vue-next](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-vue-next) | `0.507.0` | `0.553.0` |
| [prettier-plugin-tailwindcss](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) | `0.6.14` | `0.7.1` |
| [sass](https://github.com/sass/dart-sass) | `1.93.3` | `1.94.0` |
| [typescript](https://github.com/microsoft/TypeScript) | `5.8.2` | `5.9.3` |
| [unplugin-element-plus](https://github.com/element-plus/unplugin-element-plus) | `0.10.0` | `0.11.1` |
| [vxe-pc-ui](https://github.com/x-extends/vxe-pc-ui) | `4.10.16` | `4.10.22` |
| [vxe-table](https://github.com/x-extends/vxe-table) | `4.17.10` | `4.17.14` |
| [zod-defaults](https://github.com/Ced-Sharp/zod-defaults) | `0.1.3` | `0.2.3` |
| [@ast-grep/napi](https://github.com/ast-grep/ast-grep) | `0.37.0` | `0.39.9` |



Updates `vue` from 3.5.23 to 3.5.24
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/compare/v3.5.23...v3.5.24)

Updates `@clack/prompts` from 0.10.1 to 0.11.0
- [Release notes](https://github.com/bombshell-dev/clack/releases)
- [Changelog](https://github.com/bombshell-dev/clack/blob/@clack/prompts@0.11.0/packages/prompts/CHANGELOG.md)
- [Commits](https://github.com/bombshell-dev/clack/commits/@clack/prompts@0.11.0/packages/prompts)

Updates `@iconify/json` from 2.2.404 to 2.2.406
- [Commits](https://github.com/iconify/icon-sets/compare/2.2.404...2.2.406)

Updates `@pnpm/workspace.read-manifest` from 1000.2.5 to 1000.2.6
- [Release notes](https://github.com/pnpm/pnpm/releases)
- [Commits](https://github.com/pnpm/pnpm/commits)

Updates `@tanstack/vue-query` from 5.90.7 to 5.91.0
- [Release notes](https://github.com/TanStack/query/releases)
- [Changelog](https://github.com/TanStack/query/blob/main/packages/vue-query/CHANGELOG.md)
- [Commits](https://github.com/TanStack/query/commits/@tanstack/vue-query@5.91.0/packages/vue-query)

Updates `@tanstack/vue-store` from 0.7.7 to 0.8.0
- [Release notes](https://github.com/TanStack/store/releases)
- [Commits](https://github.com/TanStack/store/commits/v0.8.0/packages/vue-store)

Updates `@typescript-eslint/eslint-plugin` from 8.46.3 to 8.46.4
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.4/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.46.3 to 8.46.4
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.46.4/packages/parser)

Updates `@vue/shared` from 3.5.23 to 3.5.24
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/commits/v3.5.24/packages/shared)

Updates `autoprefixer` from 10.4.21 to 10.4.22
- [Release notes](https://github.com/postcss/autoprefixer/releases)
- [Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/autoprefixer/compare/10.4.21...10.4.22)

Updates `commitlint-plugin-function-rules` from 4.1.0 to 4.1.1
- [Release notes](https://github.com/vidavidorra/commitlint-plugin-function-rules/releases)
- [Changelog](https://github.com/vidavidorra/commitlint-plugin-function-rules/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vidavidorra/commitlint-plugin-function-rules/compare/v4.1.0...v4.1.1)

Updates `eslint-config-turbo` from 2.6.0 to 2.6.1
- [Release notes](https://github.com/vercel/turborepo/releases)
- [Changelog](https://github.com/vercel/turborepo/blob/main/release.md)
- [Commits](https://github.com/vercel/turborepo/commits/v2.6.1/packages/eslint-config-turbo)

Updates `lodash.clonedeep` from 4.5.0 to 
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/commits)

Updates `lodash.get` from 4.4.2 to 
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/commits)

Updates `lodash.isequal` from 4.5.0 to 
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/commits)

Updates `lodash.set` from 4.3.2 to 
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/commits)

Updates `lucide-vue-next` from 0.507.0 to 0.553.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/0.553.0/packages/lucide-vue-next)

Updates `prettier-plugin-tailwindcss` from 0.6.14 to 0.7.1
- [Release notes](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/prettier-plugin-tailwindcss/compare/v0.6.14...v0.7.1)

Updates `sass` from 1.93.3 to 1.94.0
- [Release notes](https://github.com/sass/dart-sass/releases)
- [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sass/dart-sass/compare/1.93.3...1.94.0)

Updates `typescript` from 5.8.2 to 5.9.3
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.8.2...v5.9.3)

Updates `unplugin-element-plus` from 0.10.0 to 0.11.1
- [Release notes](https://github.com/element-plus/unplugin-element-plus/releases)
- [Commits](https://github.com/element-plus/unplugin-element-plus/compare/v0.10.0...v0.11.1)

Updates `vxe-pc-ui` from 4.10.16 to 4.10.22
- [Release notes](https://github.com/x-extends/vxe-pc-ui/releases)
- [Commits](https://github.com/x-extends/vxe-pc-ui/commits)

Updates `vxe-table` from 4.17.10 to 4.17.14
- [Release notes](https://github.com/x-extends/vxe-table/releases)
- [Commits](https://github.com/x-extends/vxe-table/compare/4.17.10...4.17.14)

Updates `zod-defaults` from 0.1.3 to 0.2.3
- [Commits](https://github.com/Ced-Sharp/zod-defaults/commits)

Updates `@ast-grep/napi` from 0.37.0 to 0.39.9
- [Release notes](https://github.com/ast-grep/ast-grep/releases)
- [Changelog](https://github.com/ast-grep/ast-grep/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ast-grep/ast-grep/compare/0.37.0...0.39.9)

Updates `@vue/reactivity` from 3.5.23 to 3.5.24
- [Release notes](https://github.com/vuejs/core/releases)
- [Changelog](https://github.com/vuejs/core/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vuejs/core/commits/v3.5.24/packages/reactivity)

---
updated-dependencies:
- dependency-name: vue
  dependency-version: 3.5.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@clack/prompts"
  dependency-version: 0.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: "@iconify/json"
  dependency-version: 2.2.406
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@pnpm/workspace.read-manifest"
  dependency-version: 1000.2.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@tanstack/vue-query"
  dependency-version: 5.91.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: "@tanstack/vue-store"
  dependency-version: 0.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.46.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.46.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: "@vue/shared"
  dependency-version: 3.5.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: autoprefixer
  dependency-version: 10.4.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: commitlint-plugin-function-rules
  dependency-version: 4.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: eslint-config-turbo
  dependency-version: 2.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: lodash.clonedeep
  dependency-version: 
  dependency-type: direct:production
  dependency-group: non-breaking-changes
- dependency-name: lodash.get
  dependency-version: 
  dependency-type: direct:production
  dependency-group: non-breaking-changes
- dependency-name: lodash.isequal
  dependency-version: 
  dependency-type: direct:production
  dependency-group: non-breaking-changes
- dependency-name: lodash.set
  dependency-version: 
  dependency-type: direct:production
  dependency-group: non-breaking-changes
- dependency-name: lucide-vue-next
  dependency-version: 0.553.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: prettier-plugin-tailwindcss
  dependency-version: 0.7.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: sass
  dependency-version: 1.94.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: typescript
  dependency-version: 5.9.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: unplugin-element-plus
  dependency-version: 0.11.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: vxe-pc-ui
  dependency-version: 4.10.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: vxe-table
  dependency-version: 4.17.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
- dependency-name: zod-defaults
  dependency-version: 0.2.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: "@ast-grep/napi"
  dependency-version: 0.39.9
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: non-breaking-changes
- dependency-name: "@vue/reactivity"
  dependency-version: 3.5.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-14 08:05:04 +08:00
Jin Mao
c3a7562e2c Merge branch 'main' into milletpeak-fontsize 2025-11-13 17:01:42 +08:00
Jin Mao
605e0ea128 chore: 更新依赖 2025-11-13 09:23:12 +08:00
dependabot[bot]
c89a83f980 chore(deps): bump stylelint-config-recommended from 14.0.1 to 17.0.0 (#6901)
Bumps [stylelint-config-recommended](https://github.com/stylelint/stylelint-config-recommended) from 14.0.1 to 17.0.0.
- [Release notes](https://github.com/stylelint/stylelint-config-recommended/releases)
- [Changelog](https://github.com/stylelint/stylelint-config-recommended/blob/main/CHANGELOG.md)
- [Commits](https://github.com/stylelint/stylelint-config-recommended/compare/14.0.1...17.0.0)

---
updated-dependencies:
- dependency-name: stylelint-config-recommended
  dependency-version: 17.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-13 09:17:44 +08:00
Jin Mao
cfad88666b chore: 替换 lodash-es为 es-toolkit 并调整样式引入方式 (#6912) 2025-11-13 08:20:30 +08:00
Jin Mao
f918dc24c6 chore(@vben/utils): 移除冗余的 lodash 依赖 2025-11-13 07:47:48 +08:00
Jin Mao
e898993fe8 chore(@vben/utils): 替换 lodash 工具函数为 es-toolkit 实现 2025-11-13 07:45:10 +08:00
Jin Mao
5cafcb4a01 refactor(shared): 替换 lodash 工具函数为 es-toolkit 兼容版本
- 将 get 和 isEqual 方法从 lodash 替换为 es-toolkit/compat 导出
- 更新 package.json 添加 es-toolkit 依赖
- 注释掉原有的 lodash.get 和 lodash.isequal 导出语句- 锁定 es-toolkit 版本至1.41.0 并更新相关依赖配置
2025-11-13 07:28:52 +08:00
Jin Mao
03154cde88 chore: update depends 2025-11-13 07:16:28 +08:00
dependabot[bot]
a74bf7b63f chore(deps): bump echarts from 5.6.0 to 6.0.0 (#6859)
Bumps [echarts](https://github.com/apache/echarts) from 5.6.0 to 6.0.0.
- [Release notes](https://github.com/apache/echarts/releases)
- [Commits](https://github.com/apache/echarts/compare/5.6.0...6.0.0)

---
updated-dependencies:
- dependency-name: echarts
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jin Mao <50581550+jinmao88@users.noreply.github.com>
2025-11-13 07:10:59 +08:00
米山
0bc7169698 feat: add global font size adjustment 2025-11-12 17:39:07 +08:00
米山
24b6e7a835 feat: add global font size adjustment 2025-11-12 17:38:41 +08:00
Jin Mao
f59e33682c Merge branch 'main' into main 2025-11-12 04:27:57 +08:00
Jin Mao
d811af37dd Merge branch 'main' into profile 2025-11-12 04:03:27 +08:00
Jin Mao
0e1a7d61f3 Merge branch 'main' into fix-downloader 2025-11-12 02:05:03 +08:00
panda7
573637222d feat: add form handleCollapsedChange event (#6893)
* feat: add form handleCollapsedChange event

* fix: ts lint

* fix: ts error

---------

Co-authored-by: sqchen <chenshiqi@sshlx.com>
2025-11-12 02:03:12 +08:00
shixi
05e9d65251 fix: for tdesign 2025-11-12 01:14:28 +08:00
shixi
0bbb20fee0 feat: sport notification link 2025-11-12 00:55:24 +08:00
shixi
dbc5ea32ae feat: add read and delete for each notification 2025-11-12 00:02:13 +08:00
xingyu4j
0319604863 feat: playground add profile 2025-11-10 18:00:50 +08:00
xingyu4j
acf99f2441 feat: tdesign add profile 2025-11-10 18:00:25 +08:00
xingyu4j
cbf2a02877 feat: naive add profile 2025-11-10 17:59:58 +08:00
xingyu4j
226d9bd1ad feat: ele add profile 2025-11-10 17:59:33 +08:00
xingyu4j
48b3d30088 fix: types 2025-11-10 17:54:01 +08:00
xingyu4j
e311cfb8da feat: route add profile 2025-11-10 17:51:10 +08:00
xingyu4j
4347fba80a feat: antdv add profile 2025-11-10 17:50:47 +08:00
xingyu4j
7dc68ed368 feat: add profile comps 2025-11-10 17:49:58 +08:00
dap
eb2e2c6f15 update: 社交授权成功后延迟跳转到首页 2025-11-10 11:03:50 +08:00
dap
1e853b7347 feat: 授权成功后跳转到默认首页
在社交授权回调处理完成后,新增路由跳转逻辑,将用户重定向到系统配置的默认首页路径。
2025-11-10 10:53:47 +08:00
Jin Mao
a4aa133db5 Merge branch 'main' into tdesign 2025-11-09 12:04:30 +08:00
Utopia
6bbe523f6f chore: 增强 util-formatDate ts 类型提示 (#6886)
* feat: 为 auth layout 添加 slot: logo, 提升组件的灵活性和可复用性

* feat: 增强 util-formatDate ts 类型提示
2025-11-09 12:00:49 +08:00
Copilot
fef1e35c54 fix: prevent JSONBigInt parsing error on non-string data (#6891)
* Initial plan

* Fix json-bigint serialization error when data is not a string

Co-authored-by: likui628 <90845831+likui628@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: likui628 <90845831+likui628@users.noreply.github.com>
2025-11-09 11:49:38 +08:00
xingyu
20410aeb03 fix: updating Tailwind CSS variables from --radix-* to --reka-* (#6890)
* fix: radix => reka

* chore: add reka cspell

* fix: Composition url
2025-11-09 11:49:01 +08:00
dap
2bc495d601 docs: changelog 2025-11-07 15:01:34 +08:00
dap
aebabdc21f fix: 将时区选项隐藏 2025-11-07 14:58:26 +08:00
dap
60f03602ae refactor: 适配新的滚动元素 2025-11-07 14:41:10 +08:00
dap
5e4cc5729b Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev 2025-11-07 14:39:42 +08:00
Li Kui
83ea27af3e fix: fix lint 2025-11-06 23:16:23 +08:00
Li Kui
b7bfd69788 fix: fix lint 2025-11-06 23:10:03 +08:00
Li Kui
99d663a6f2 fix: fix typecheck 2025-11-06 23:02:24 +08:00
Li Kui
1af11240a6 chore: add tdesign logo 2025-11-06 22:37:55 +08:00
Li Kui
3e8e8690e3 chore: Merge branch 'main' into tdesign 2025-11-06 17:49:51 +08:00
Li Kui
be843300be chore: update demo routes 2025-11-06 17:44:34 +08:00
Li Kui
dc77721c11 chore: demo i18n 2025-11-06 16:42:25 +08:00
Li Kui
a38d081f17 chore: enable VITE_NITRO_MOCK 2025-11-06 16:37:15 +08:00
Copilot
1e09fa4642 feat: migrate from Radix Vue to Reka UI (#6870)
* Initial plan

* Update dependencies and imports from radix-vue to reka-ui

Co-authored-by: likui628 <90845831+likui628@users.noreply.github.com>

* Fix type errors after reka-ui migration

Co-authored-by: likui628 <90845831+likui628@users.noreply.github.com>

* Run formatter to fix code style

Co-authored-by: likui628 <90845831+likui628@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: likui628 <90845831+likui628@users.noreply.github.com>
2025-11-06 16:22:24 +08:00
Li Kui
bd8ff73f9e fix: fix build error 2025-11-06 15:26:24 +08:00
kiki1373639299
15dc0c121f fix: update delete functionality to use spelDelete API 2025-11-06 07:13:17 +00:00
Li Kui
57620dc2ea chore: apply coderabbitai suggestions 2025-11-06 15:11:28 +08:00
Li Kui
b472fbb72f Merge branch 'main' into tdesign 2025-11-06 14:36:27 +08:00
Copilot
04321b16c1 fix: replace ant-design-vue with tdesign components in web-tdesign app (#6880)
* Initial plan

* fix: replace ant-design-vue with tdesign components

Co-authored-by: likui628 <90845831+likui628@users.noreply.github.com>

* fix: remove trailing comma in package.json

Co-authored-by: likui628 <90845831+likui628@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: likui628 <90845831+likui628@users.noreply.github.com>
2025-11-06 14:35:50 +08:00
dap
28b4e907a8 chore: 降级 @types/node 版本 2025-11-06 09:43:42 +08:00
dap
8c42c8cc70 merge 2025-11-06 09:38:42 +08:00
Olexandr88
7268824612 Update README.md (#6828)
Co-authored-by: Jin Mao <50581550+jinmao88@users.noreply.github.com>
2025-11-04 04:34:55 +08:00
dependabot[bot]
23c41a8059 chore(deps): bump @types/node from 12.20.55 to 24.9.2 (#6860)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 12.20.55 to 24.9.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 24.9.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-04 04:16:59 +08:00
dependabot[bot]
fdc78faa13 chore(deps): bump @vueuse/integrations from 12.8.2 to 14.0.0 (#6861)
Bumps [@vueuse/integrations](https://github.com/vueuse/vueuse/tree/HEAD/packages/integrations) from 12.8.2 to 14.0.0.
- [Release notes](https://github.com/vueuse/vueuse/releases)
- [Commits](https://github.com/vueuse/vueuse/commits/v14.0.0/packages/integrations)

---
updated-dependencies:
- dependency-name: "@vueuse/integrations"
  dependency-version: 14.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-04 04:16:37 +08:00
dependabot[bot]
c8af580866 chore(deps): bump SamKirkland/FTP-Deploy-Action (#6873)
Bumps the non-breaking-changes group with 1 update: [SamKirkland/FTP-Deploy-Action](https://github.com/samkirkland/ftp-deploy-action).


Updates `SamKirkland/FTP-Deploy-Action` from 4.3.5 to 4.3.6
- [Release notes](https://github.com/samkirkland/ftp-deploy-action/releases)
- [Commits](https://github.com/samkirkland/ftp-deploy-action/compare/v4.3.5...v4.3.6)

---
updated-dependencies:
- dependency-name: SamKirkland/FTP-Deploy-Action
  dependency-version: 4.3.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: non-breaking-changes
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-04 04:14:47 +08:00
Jin Mao
348c97710f Revert "refactor(jwt): 更新 JWT 工具函数实现方式 (#6869)"
This reverts commit a854760d26.
2025-11-03 19:34:45 +08:00
zengmingyong
a854760d26 refactor(jwt): 更新 JWT 工具函数实现方式 (#6869)
refactor(jwt): 更新 JWT 工具函数实现方式

- 将默认导入 jsonwebtoken 改为解构导入 sign 和 verify 方法
2025-11-03 13:08:06 +08:00
Copilot
c7c39047de feat: add theme-aware logo support via optional sourceDark configuration (#6866)
* Initial plan

* Initial exploration and setup

Co-authored-by: aonoa <32682251+aonoa@users.noreply.github.com>

* Add support for separate light and dark theme logos

Co-authored-by: aonoa <32682251+aonoa@users.noreply.github.com>

* Update documentation with dark theme logo configuration

Co-authored-by: aonoa <32682251+aonoa@users.noreply.github.com>

* feat: Add theme-aware logo support for authentication/login page

Co-authored-by: aonoa <32682251+aonoa@users.noreply.github.com>

* revert: .npmrc

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

---------

Signed-off-by: aonoa <1991849113@qq.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: aonoa <32682251+aonoa@users.noreply.github.com>
Co-authored-by: Li Kui <90845831+likui628@users.noreply.github.com>
Co-authored-by: aonoa <1991849113@qq.com>
2025-11-03 13:07:36 +08:00
Jin Mao
f8a7a0a9a2 chore: lint 2025-10-31 09:11:40 +08:00
Jin Mao
2cb3dcf499 Merge branch 'fork/ming4762/timezone-fix-20251030'
# Conflicts:
#	packages/stores/src/modules/timezone.ts
2025-10-31 09:08:08 +08:00
Jin Mao
b36d32b66d chore(timezone): 添加 $reset 方法并初始化时区存储
- 在时区存储中添加 `$reset` 方法
-修复导入语句引号为双引号
- 优化时区初始化错误处理逻辑- 导出 `$reset` 方法以便外部调用- 确保时区设置与默认选项同步
- 提升代码一致性和可维护性
2025-10-30 23:27:53 +08:00
zhongming4762
639ea96bb9 fix: resolve the issue of logout failure caused by the timezone store 2025-10-30 17:12:45 +08:00
zhongming4762
565be77e96 fix: resolve the issue of logout failure caused by the timezone store 2025-10-30 17:01:49 +08:00
Jin Mao
ac6de0324c Merge branch 'fork/ming4762/timezone-20251020' 2025-10-30 00:42:05 +08:00
zhongming4762
f46ae023ba feat: increase support for multiple time zones
* 优化实现方法
2025-10-29 20:14:48 +08:00
zhongming4762
212144feca feat: increase support for multiple time zones
* 优化实现方法
2025-10-29 20:10:25 +08:00
zhongming4762
3eed51fd3e feat: increase support for multiple time zones
* 优化实现方法
2025-10-29 20:03:21 +08:00
zhongming4762
4d713db546 feat: increase support for multiple time zones
* 优化实现方法
2025-10-29 19:47:10 +08:00
dap
4c39bef181 docs: version 2025-10-28 13:32:43 +08:00
dap
86bcceaa84 feat: 租户管理 同步租户参数配置 2025-10-28 13:30:09 +08:00
dap
1980a2482d fix: modal/drawer里使用列配置 重置列弹窗被遮挡 2025-10-27 19:19:52 +08:00
Jin Mao
0cd9f4615c Merge branch 'main' into fix-downloader 2025-10-26 15:28:34 +08:00
Jin Mao
cfbce0d737 chore: fix lint 2025-10-26 15:27:28 +08:00
Jin Mao
1d9cd88dd7 Merge branch 'main' into fix-downloader 2025-10-26 15:16:32 +08:00
Jin Mao
0e62862119 chore: fix lint 2025-10-26 15:01:58 +08:00
Jin Mao
6b6cdef42d chore: 一些调整 2025-10-26 15:00:47 +08:00
Jin Mao
33b7a605c0 chore: 一些调整和兼容性更新 2025-10-26 14:23:37 +08:00
dap
a38cf80ea4 refactor: 重构数据权限 - 部门分配组件 2025-10-24 16:05:04 +08:00
dap
a986e1a2ab fix: v-access:role指令错误判断code而非role 2025-10-24 10:27:03 +08:00
dap
9822d2af8a fix: 菜单管理 新增没有加载下拉选择api 2025-10-24 10:06:17 +08:00
dap
b51f5d1fa6 update: 发起流程loading + 默认不显示抄送字段 2025-10-23 14:16:22 +08:00
zhongming4762
e01803ce9d feat: increase support for multiple time zones 2025-10-22 20:39:00 +08:00
zhongming4762
e3e5755903 feat: increase support for multiple time zones 2025-10-22 20:29:53 +08:00
zhongming4762
61ce53b686 feat: increase support for multiple time zones 2025-10-22 20:23:55 +08:00
zhongming4762
b029f77b6a feat: increase support for multiple time zones 2025-10-22 20:18:25 +08:00
zhongming4762
0a8339a405 feat: increase support for multiple time zones 2025-10-22 19:52:01 +08:00
dap
56104b2abf refactor: tinymce 避免图片地址和链接地址转换成相对路径 2025-10-22 11:02:29 +08:00
dap
b4ca3f43a9 docs: 更新私有桶预览说明文档 2025-10-22 10:27:02 +08:00
dap
0666483c58 refactor: 调整菜单圆角大小 2025-10-21 14:11:31 +08:00
Jin Mao
2264eaae18 feat: 新增集成tdesign组件的apps 2025-10-21 10:47:39 +08:00
dap
77c45d855b docs: changelog 2025-10-20 16:01:23 +08:00
dap
8ce52eef51 feat: 修改流程变量 2025-10-20 15:51:47 +08:00
dap
738a918df6 refactor: 优化代码 2025-10-20 14:49:18 +08:00
dap
d9131cbe22 fix: 修复审批人昵称包含逗号时显示不正确的问题
将审批人昵称分割逻辑从 approveName 字段改为 approver 字段,
并添加注释说明昵称中带逗号的处理仍不准确,为后续优化提供提醒。
2025-10-20 14:45:10 +08:00
dap
968a2eb7b6 refactor: 优化审批面板接口加载逻辑 防止多余的api加载 2025-10-20 14:39:05 +08:00
dap
9b59a8acdb refactor: 重构审批详情footer及相关代码 移除之前iframe方案过时代码
新增 `FlowActions` 审批操作按钮组件,统一处理我的申请、审批、管理员等不同场景下的操作逻辑。
重构 `ApprovalPanel` 组件,将操作按钮抽离为独立组件,简化原有代码结构。
移除了 `ApprovalDetails` 中冗余的 iframe 高度控制属性。
优化 `FlowPreview` 组件,调整 iframe 样式并增强主题切换时与子页面的通信逻辑。
新增 `ApprovalType` 类型定义文件,明确各审批场景类型。
2025-10-20 14:23:51 +08:00
zoumingjun
38f91da5af fix: 优化双列布局组件点击展开左侧按钮位置不居中问题 2025-10-18 21:22:58 +08:00
zoumingjun
8edea3aee5 fix: 调整zh-CN/system.json中<"title": "系统管理">配置位置,保持与en-US/system.json中位置一致 2025-10-18 21:20:54 +08:00
zoumingjun
08b6e7713e fix: 修复对话框modal组件方法名错误 2025-10-18 21:13:34 +08:00
zoumingjun
6a89814b83 chore: 优化侧边栏展开折叠图标 2025-10-18 20:59:14 +08:00
zoumingjun
52d3aa9315 fix: 优化Checkbox组件hover样式,Input组件placeholder样式,TabsList组件圆角样式 2025-10-18 20:58:48 +08:00
zoumingjun
4e264c503a fix: 锁屏和解锁密码输入框自动聚焦 2025-10-18 20:55:53 +08:00
zoumingjun
32051e9ca0 fix: 优化左侧和右侧认证面板动画效果 2025-10-18 20:52:27 +08:00
zoumingjun
2b0079580b fix: 修复mock-data.ts父id错误 2025-10-18 16:32:23 +08:00
lighua
fddfc6d494 fix: 增加对不支持的 HTTP 方法的错误处理 2025-08-21 11:13:02 +08:00
lighua
df655015b1 fix: 优化文件下载器方法 2025-08-08 15:31:31 +08:00
422 changed files with 6349 additions and 1909 deletions

View File

@@ -9,7 +9,7 @@ runs:
uses: pnpm/action-setup@v4
- name: Install Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version-file: .node-version
cache: 'pnpm'

View File

@@ -30,7 +30,7 @@ jobs:
- windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0

View File

@@ -25,7 +25,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0

View File

@@ -28,7 +28,7 @@ jobs:
timeout-minutes: 20
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -67,7 +67,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -90,7 +90,7 @@ jobs:
- windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0

View File

@@ -57,7 +57,7 @@ jobs:
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -30,7 +30,7 @@ jobs:
run: pnpm build:play
- name: Sync Playground files
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
uses: SamKirkland/FTP-Deploy-Action@v4.3.6
with:
server: ${{ secrets.PRO_FTP_HOST }}
username: ${{ secrets.WEB_PLAYGROUND_FTP_ACCOUNT }}
@@ -43,7 +43,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -54,7 +54,7 @@ jobs:
run: pnpm build:docs
- name: Sync Docs files
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
uses: SamKirkland/FTP-Deploy-Action@v4.3.6
with:
server: ${{ secrets.PRO_FTP_HOST }}
username: ${{ secrets.WEBSITE_FTP_ACCOUNT }}
@@ -67,7 +67,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -85,7 +85,7 @@ jobs:
run: pnpm run build:antd
- name: Sync files
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
uses: SamKirkland/FTP-Deploy-Action@v4.3.6
with:
server: ${{ secrets.PRO_FTP_HOST }}
username: ${{ secrets.WEB_ANTD_FTP_ACCOUNT }}
@@ -98,7 +98,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -116,7 +116,7 @@ jobs:
run: pnpm run build:ele
- name: Sync files
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
uses: SamKirkland/FTP-Deploy-Action@v4.3.6
with:
server: ${{ secrets.PRO_FTP_HOST }}
username: ${{ secrets.WEB_ELE_FTP_ACCOUNT }}
@@ -129,7 +129,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -147,7 +147,7 @@ jobs:
run: pnpm run build:naive
- name: Sync files
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
uses: SamKirkland/FTP-Deploy-Action@v4.3.6
with:
server: ${{ secrets.PRO_FTP_HOST }}
username: ${{ secrets.WEB_NAIVE_FTP_ACCOUNT }}

View File

@@ -19,15 +19,15 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20]
node-version: [22]
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
# - name: Checkout code
# uses: actions/checkout@v4
# uses: actions/checkout@v6
# with:
# fetch-depth: 0

View File

@@ -1 +1 @@
22.1.0
22.22.0

2
.npmrc
View File

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

View File

@@ -181,7 +181,8 @@
"markdown",
"json",
"jsonc",
"json5"
"json5",
"yaml"
],
"tailwindCSS.experimental.classRegex": [

View File

@@ -1,3 +1,38 @@
# 1.5.3
对应后端版本 单体/微服务: 5.5.3/2.5.3(向后兼容5.5.x版本)
全是纯前端更新 没有后端功能变动
**REFACTOR**
- 使用antd组件替换密码登录表单
**FEATURES**
- 合并官方更新 将Radix(v1)替换为Reka UI(v2)
# 1.5.2
对应后端版本 单体/微服务: 5.5.1/2.5.1
该版本后端功能值包含一个`同步租户参数配置`功能 旧版本也能升级(使用)
**REFACTOR**
- 流程相关代码重构 移除之前的历史代码
**FEATURES**
- 修改流程变量
- 租户管理 同步租户参数配置
**BUG FIX**
- 菜单管理 新增没有加载下拉选择api
- v-access:role指令错误判断code而非role
- modal/drawer里使用列配置 重置列弹窗被遮挡
# 1.5.1
对应后端版本 单体/微服务: 5.5.0/2.5.0

View File

@@ -10,7 +10,7 @@
<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)
[![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)](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml) [![build](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml/badge.svg)](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml) [![ci](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml/badge.svg)](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml) [![deploy](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml/badge.svg)](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml)
**English** | [中文](./README.zh-CN.md) | [日本語](./README.ja-JP.md)

View File

@@ -6,7 +6,7 @@
v5版本采用分仓(包)目录结构, 具体开发路径为: `根目录/apps/web-antd`
目前对应后端版本: **分布式5.5.0/微服务2.5.0**
目前对应后端版本: **分布式5.5.1/微服务2.5.1**
V1.1.0版本已支持离线图标
@@ -32,11 +32,19 @@ V1.2.0版本对接warmflow工作流
admin 账号: admin admin123
[预览地址点这里](http://vben5.dapdap.top)
[dev分支预览](http://vben5.dapdap.top)
## WX Group
[antdv-next分支预览](http://antdv-next.dapdap.top)
演示站 - 微信群菜单
## 分支说明
- `main` 主分支 稳定分支(基于ant-design-vue)
- `dev` 开发分支 包含前端/后端新功能 没问题后合并到`main`分支(基于ant-design-vue) [预览地址](http://vben5.dapdap.top)
- `antdv-next`分支 使用`antdv-next`替换已经不维护的`ant-design-vue` 分支 且包含破坏性更新 (基于antv-next) [预览地址](http://antdv-next.dapdap.top)
antdv-next开发完毕且测试正常后 会作为主分支更新(v2) 原先基于`ant-design-vue`的版本会作为归档(v1)
antdv-next目前为阿尔法版本
## 文档
@@ -82,7 +90,7 @@ pnpm install
- 关于代码生成
V5版本代码生成模板为付费功能 [详见](https://dapdap.top/other/template.html)
原先为付费功能 现由于某些原因不再放出 建议用AI照着抄 [详见](https://dapdap.top/other/template.html)
- 关于一些监控的地址配置(微服务版本可以跳过这一小节)

View File

@@ -0,0 +1,12 @@
import { eventHandler } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
import { getTimezone } from '~/utils/timezone-utils';
export default eventHandler((event) => {
const userinfo = verifyAccessToken(event);
if (!userinfo) {
return unAuthorizedResponse(event);
}
return useResponseSuccess(getTimezone());
});

View File

@@ -0,0 +1,11 @@
import { eventHandler } from 'h3';
import { TIME_ZONE_OPTIONS } from '~/utils/mock-data';
import { useResponseSuccess } from '~/utils/response';
export default eventHandler(() => {
const data = TIME_ZONE_OPTIONS.map((o) => ({
label: `${o.timezone} (GMT${o.offset >= 0 ? `+${o.offset}` : o.offset})`,
value: o.timezone,
}));
return useResponseSuccess(data);
});

View File

@@ -0,0 +1,22 @@
import { eventHandler, readBody } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
import { TIME_ZONE_OPTIONS } from '~/utils/mock-data';
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
import { setTimezone } from '~/utils/timezone-utils';
export default eventHandler(async (event) => {
const userinfo = verifyAccessToken(event);
if (!userinfo) {
return unAuthorizedResponse(event);
}
const body = await readBody<{ timezone?: unknown }>(event);
const timezone =
typeof body?.timezone === 'string' ? body.timezone : undefined;
const allowed = TIME_ZONE_OPTIONS.some((o) => o.timezone === timezone);
if (!timezone || !allowed) {
setResponseStatus(event, 400);
return useResponseError('Bad Request', 'Invalid timezone');
}
setTimezone(timezone);
return useResponseSuccess({});
});

View File

@@ -7,6 +7,11 @@ export interface UserInfo {
homePath?: string;
}
export interface TimezoneOption {
offset: number;
timezone: string;
}
export const MOCK_USERS: UserInfo[] = [
{
id: 0,
@@ -276,7 +281,7 @@ export const MOCK_MENU_LIST = [
children: [
{
id: 20_401,
pid: 201,
pid: 202,
name: 'SystemDeptCreate',
status: 1,
type: 'button',
@@ -285,7 +290,7 @@ export const MOCK_MENU_LIST = [
},
{
id: 20_402,
pid: 201,
pid: 202,
name: 'SystemDeptEdit',
status: 1,
type: 'button',
@@ -294,7 +299,7 @@ export const MOCK_MENU_LIST = [
},
{
id: 20_403,
pid: 201,
pid: 202,
name: 'SystemDeptDelete',
status: 1,
type: 'button',
@@ -388,3 +393,29 @@ export function getMenuIds(menus: any[]) {
});
return ids;
}
/**
* 时区选项
*/
export const TIME_ZONE_OPTIONS: TimezoneOption[] = [
{
offset: -5,
timezone: 'America/New_York',
},
{
offset: 0,
timezone: 'Europe/London',
},
{
offset: 8,
timezone: 'Asia/Shanghai',
},
{
offset: 9,
timezone: 'Asia/Tokyo',
},
{
offset: 9,
timezone: 'Asia/Seoul',
},
];

View File

@@ -0,0 +1,9 @@
let mockTimeZone: null | string = null;
export const setTimezone = (timeZone: string) => {
mockTimeZone = timeZone;
};
export const getTimezone = () => {
return mockTimeZone;
};

View File

@@ -1,6 +1,6 @@
{
"name": "@vben/web-antd",
"version": "1.5.1",
"version": "1.5.3",
"homepage": "https://vben.pro",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {
@@ -27,8 +27,8 @@
"#/*": "./src/*"
},
"dependencies": {
"@ant-design/icons-vue": "^7.0.1",
"@tinymce/tinymce-vue": "^6.0.1",
"@ant-design/icons-vue": "catalog:",
"@tinymce/tinymce-vue": "catalog:",
"@vben/access": "workspace:*",
"@vben/common-ui": "workspace:*",
"@vben/constants": "workspace:*",
@@ -45,18 +45,18 @@
"@vben/utils": "workspace:*",
"@vueuse/core": "catalog:",
"ant-design-vue": "catalog:",
"cropperjs": "^1.6.2",
"cropperjs": "catalog:",
"dayjs": "catalog:",
"echarts": "^5.5.1",
"lodash-es": "^4.17.21",
"echarts": "catalog:conflicts_echarts_h5_5_1",
"lodash-es": "catalog:",
"pinia": "catalog:",
"tinymce": "7.9.1",
"unplugin-vue-components": "^0.27.3",
"tinymce": "catalog:",
"unplugin-vue-components": "catalog:",
"vue": "catalog:",
"vue-router": "catalog:",
"vue3-colorpicker": "^2.3.0"
"vue3-colorpicker": "catalog:"
},
"devDependencies": {
"@types/lodash-es": "^4.17.12"
"@types/lodash-es": "catalog:"
}
}

View File

@@ -127,6 +127,7 @@ const withDefaultPlaceholder = <T extends Component>(
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
export type ComponentType =
| 'ApiCascader'
| 'ApiSelect'
| 'ApiTreeSelect'
| 'AutoComplete'
@@ -166,34 +167,28 @@ async function initComponentAdapter() {
// 如果你的组件体积比较大,可以使用异步加载
// Button: () =>
// import('xxx').then((res) => res.Button),
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',
},
),
ApiCascader: withDefaultPlaceholder(ApiComponent, 'select', {
component: Cascader,
fieldNames: { label: 'label', value: 'value', children: 'children' },
loadingSlot: 'suffixIcon',
modelPropName: 'value',
visibleEvent: 'onVisibleChange',
}),
ApiSelect: withDefaultPlaceholder(ApiComponent, 'select', {
component: Select,
loadingSlot: 'suffixIcon',
modelPropName: 'value',
visibleEvent: 'onVisibleChange',
}),
ApiTreeSelect: withDefaultPlaceholder(ApiComponent, 'select', {
component: TreeSelect,
fieldNames: { label: 'label', value: 'value', children: 'children' },
loadingSlot: 'suffixIcon',
modelPropName: 'value',
optionsPropName: 'treeData',
visibleEvent: 'onVisibleChange',
}),
AutoComplete,
Cascader: withDefaultPlaceholder(Cascader, 'select'),
Checkbox,

View File

@@ -78,9 +78,10 @@ setupVbenVxeTable({
// 表格配置项可以用 cellRender: { name: 'CellImage' },
vxeUI.renderer.add('CellImage', {
renderTableDefault(_renderOpts, params) {
renderTableDefault(renderOpts, params) {
const { props } = renderOpts;
const { column, row } = params;
return h(Image, { src: row[column.field] });
return h(Image, { src: row[column.field], ...props });
},
});

View File

@@ -125,3 +125,13 @@ export function dictSyncTenant(tenantId?: string) {
successMessageMode: 'message',
});
}
/**
* 同步租户配置
* @returns void
*/
export function syncTenantConfig() {
return requestClient.get<void>('/system/tenant/syncTenantConfig', {
successMessageMode: 'message',
});
}

View File

@@ -1,5 +1,5 @@
import type { TaskInfo } from '../task/model';
import type { FlowInfoResponse } from './model';
import type { FlowInfoResponse, FlowInstanceVariableResp } from './model';
import type { ID, IDS, PageQuery, PageResult } from '#/api/common';
@@ -104,8 +104,8 @@ export function flowInfo(businessId: string) {
* @returns Map<string,any>
*/
export function instanceVariable(instanceId: string) {
return requestClient.get<Record<string, any>>(
`/workflow/instance/variable/${instanceId}`,
return requestClient.get<FlowInstanceVariableResp>(
`/workflow/instance/instanceVariable/${instanceId}`,
);
}
@@ -118,3 +118,22 @@ export function workflowInstanceInvalid(data: {
}) {
return requestClient.postWithMsg<void>('/workflow/instance/invalid', data);
}
/**
* 修改流程参数
* @param data 参数
* @param data.instanceId 实例ID
* @param data.key 参数key
* @param data.value 值
* @returns void
*/
export function updateFlowVariable(data: {
instanceId: string;
key: string;
value: any;
}) {
return requestClient.putWithMsg<void>(
'/workflow/instance/updateVariable',
data,
);
}

View File

@@ -1,3 +1,5 @@
export {};
export interface Flow {
id: string;
createTime: string;
@@ -39,3 +41,14 @@ export interface FlowInfoResponse {
instanceId: string;
list: Flow[];
}
export interface FlowInstanceVariableResp {
/**
* json字符串 流程变量
*/
variable: string;
variableList: {
key: string;
value: any;
}[];
}

View File

@@ -0,0 +1,27 @@
import { defineComponent, h } from 'vue';
/**
* 使用默认插槽来自定义组件
* 给vbenForm的components使用
*/
export const DefaultSlot = defineComponent({
name: 'DefaultSlot',
inheritAttrs: false,
props: {
/**
* 绑定到根节点的div上的属性
*/
rootDivAttrs: {
type: Object,
default: () => ({}),
},
},
render() {
/**
* 获取属性 传递给作用域插槽供外部使用
*/
const attrs = this.$attrs;
return h('div', { ...this.rootDivAttrs }, this.$slots.default?.(attrs));
},
});

View File

@@ -133,6 +133,11 @@ const initOptions = computed((): InitOptions => {
toolbar_mode: 'sliding',
// 隐藏下面的 按xxx获取帮助
help_accessibility: false,
// https://blog.csdn.net/qq_46380656/article/details/122171418
// 避免图片地址和链接地址转换成相对路径
relative_urls: false,
remove_script_host: false,
convert_urls: false,
...options,
/**
* 覆盖默认的base64行为

View File

@@ -1,215 +1,170 @@
<script setup lang="ts">
import type { CheckboxChangeEvent } from 'ant-design-vue/es/checkbox/interface';
import type { DataNode } from 'ant-design-vue/es/tree';
import type { CheckInfo } from 'ant-design-vue/es/vc-tree/props';
import type { PropType, SetupContext } from 'vue';
import { computed, nextTick, onMounted, ref } from 'vue';
import { computed, nextTick, onMounted, ref, useSlots, watch } from 'vue';
import { findGroupParentIds, treeToList } from '@vben/utils';
import { treeToList } from '@vben/utils';
import { Checkbox, Tree } from 'ant-design-vue';
import { uniq } from 'lodash-es';
/** 需要禁止透传 */
defineOptions({ inheritAttrs: false });
const props = defineProps({
checkStrictly: {
default: true,
type: Boolean,
},
expandAllOnInit: {
default: false,
type: Boolean,
},
fieldNames: {
default: () => ({ key: 'id', title: 'label' }),
type: Object as PropType<{ key: string; title: string }>,
},
/** 点击节点关联/独立时 清空已勾选的节点 */
resetOnStrictlyChange: {
default: true,
type: Boolean,
},
treeData: {
default: () => [],
type: Array as PropType<DataNode[]>,
},
const props = withDefaults(defineProps<Props>(), {
expandAllOnInit: false,
fieldNames: () => ({ key: 'id', title: 'label' }),
resetOnStrictlyChange: true,
treeData: () => [],
});
const emit = defineEmits<{ checkStrictlyChange: [boolean] }>();
const expandStatus = ref(false);
const selectAllStatus = ref(false);
interface Props {
/**
* 是否展开所有节点 mount
*/
expandAllOnInit?: boolean;
/**
* 自定义字段
*/
fieldNames?: { key: string; title: string };
/**
* 点击节点关联/独立时 清空已勾选的节点
*/
resetOnStrictlyChange?: boolean;
/**
* 树结构数据
*/
treeData?: DataNode[];
}
/**
* 后台的这个字段跟antd/ele是反的
* 组件库这个字段代表不关联
* 后台这个代表关联
* 展开的状态
*/
const innerCheckedStrictly = computed(() => {
return !props.checkStrictly;
});
const expandStatus = ref(false);
/**
* 全选状态
*/
const selectAllStatus = ref(false);
const associationText = computed(() => {
return props.checkStrictly ? '父子节点关联' : '父子节点独立';
return checkStrictly.value ? '父子节点关联' : '父子节点独立';
});
/**
* 这个只用于界面显示
* 关联情况下 只会有最末尾的节点被选中
*/
const checkedKeys = defineModel('value', {
const checkedKeys = defineModel<(number | string)[]>('value', {
default: () => [],
type: Array as PropType<(number | string)[]>,
});
/**
* 是否节点关联 后端字段跟前端字段是反的
*/
const checkStrictly = defineModel<boolean>('checkStrictly', {
default: () => true,
});
const computedCheckedKeys = computed<any>({
get() {
/**
* 严格模式(节点不关联) 需要返回{checked: string[] | number[], halfChecked: string[]}
* @see https://www.antdv.com/components/tree-cn#tree-props
*/
if (!checkStrictly.value) {
return {
checked: [...checkedKeys.value],
halfChecked: [],
};
}
return checkedKeys.value;
},
set(v) {
if (!checkStrictly.value) {
checkedKeys.value = [...v.checked, ...v.halfChecked];
return;
}
checkedKeys.value = v;
},
});
// 所有节点的ID
const allKeys = computed(() => {
const idField = props.fieldNames.key;
return treeToList(props.treeData).map((item: any) => item[idField]);
});
/** 已经选择的所有节点 包括子/父节点 用于提交 */
const checkedRealKeys = ref<(number | string)[]>([]);
/**
* 取第一次的menuTree id 设置到checkedMenuKeys
* 主要为了解决没有任何修改 直接点击保存的情况
*
* length为0情况(即新增时候没有勾选节点) 勾选这里会延迟触发 节点会拼接上父节点 导致ID重复
*/
const stop = watch([checkedKeys, () => props.treeData], () => {
if (
props.checkStrictly &&
checkedKeys.value.length > 0 &&
props.treeData.length > 0
) {
/** 找到父节点 添加上 */
const parentIds = findGroupParentIds(
props.treeData,
checkedKeys.value as any,
{ id: props.fieldNames.key },
);
/**
* uniq 解决上面的id重复问题
*/
checkedRealKeys.value = uniq([...parentIds, ...checkedKeys.value]);
stop();
}
if (!props.checkStrictly && checkedKeys.value.length > 0) {
/** 节点独立 这里是全部的节点 */
checkedRealKeys.value = checkedKeys.value;
stop();
}
});
/**
*
* @param checkedStateKeys 已经选中的子节点的ID
* @param info info.halfCheckedKeys为父节点的ID
*/
type CheckedState<T = number | string> =
| T[]
| { checked: T[]; halfChecked: T[] };
function handleChecked(checkedStateKeys: CheckedState, info: CheckInfo) {
// 数组的话为节点关联
if (Array.isArray(checkedStateKeys)) {
const halfCheckedKeys: number[] = (info.halfCheckedKeys || []) as number[];
checkedRealKeys.value = [...halfCheckedKeys, ...checkedStateKeys];
} else {
checkedRealKeys.value = [...checkedStateKeys.checked];
// fix: Invalid prop: type check failed for prop "value". Expected Array, got Object
checkedKeys.value = [...checkedStateKeys.checked];
}
}
function handleExpandChange(e: CheckboxChangeEvent) {
function handleCheckedAllChange(e: CheckboxChangeEvent) {
// 这个用于展示
checkedKeys.value = e.target.checked ? allKeys.value : [];
// 这个用于提交
checkedRealKeys.value = e.target.checked ? allKeys.value : [];
}
const expandedKeys = ref<string[]>([]);
function handleExpandOrCollapseAll(e: CheckboxChangeEvent) {
const expand = e.target.checked;
expandedKeys.value = expand ? allKeys.value : [];
function handleExpandOrCollapseAll() {
expandStatus.value = !expandStatus.value;
expandedKeys.value = expandStatus.value ? allKeys.value : [];
}
function handleCheckStrictlyChange(e: CheckboxChangeEvent) {
emit('checkStrictlyChange', e.target.checked);
function handleCheckStrictlyChange() {
if (props.resetOnStrictlyChange) {
checkedKeys.value = [];
checkedRealKeys.value = [];
}
}
/**
* 暴露方法来获取用于提交的全部节点
* uniq去重(保险方案)
*/
defineExpose({
getCheckedKeys: () => uniq(checkedRealKeys.value),
});
onMounted(async () => {
if (props.expandAllOnInit) {
await nextTick();
expandedKeys.value = allKeys.value;
}
});
const slots = useSlots() as SetupContext['slots'];
</script>
<template>
<div class="bg-background w-full rounded-lg border-[1px] p-[12px]">
<!-- <div class="flex flex-col gap-6 text-[13px]">
<div>computedCheckedKeys {{ computedCheckedKeys }}</div>
<div>checkedKeys {{ checkedKeys }}</div>
</div> -->
<div class="flex items-center justify-between gap-2 border-b-[1px] pb-2">
<div>
<div class="opacity-75">
<span>节点状态: </span>
<span :class="[props.checkStrictly ? 'text-primary' : 'text-red-500']">
<span :class="[checkStrictly ? 'text-primary' : 'text-red-500']">
{{ associationText }}
</span>
</div>
<div>
已选中
<span class="text-primary mx-1 font-semibold">
{{ checkedRealKeys.length }}
</span>
个节点
</div>
</div>
<div
class="flex flex-wrap items-center justify-between border-b-[1px] py-2"
>
<Checkbox
v-model:checked="expandStatus"
@change="handleExpandOrCollapseAll"
>
<a-button size="small" @click="handleExpandOrCollapseAll">
展开/折叠全部
</Checkbox>
<Checkbox v-model:checked="selectAllStatus" @change="handleExpandChange">
</a-button>
<Checkbox
v-model:checked="selectAllStatus"
@change="handleCheckedAllChange"
>
全选/取消全选
</Checkbox>
<Checkbox :checked="checkStrictly" @change="handleCheckStrictlyChange">
<Checkbox
v-model:checked="checkStrictly"
@change="handleCheckStrictlyChange"
>
父子节点关联
</Checkbox>
</div>
<div class="py-2">
<Tree
v-if="treeData.length > 0"
v-model:check-strictly="innerCheckedStrictly"
v-model:checked-keys="checkedKeys"
:check-strictly="!checkStrictly"
v-model:checked-keys="computedCheckedKeys"
v-model:expanded-keys="expandedKeys"
:checkable="true"
:field-names="fieldNames"
:selectable="false"
:tree-data="treeData"
@check="handleChecked"
>
<template
v-for="slotName in Object.keys(slots)"
v-for="slotName in Object.keys($slots)"
:key="slotName"
#[slotName]="data"
>
@@ -219,3 +174,20 @@ const slots = useSlots() as SetupContext['slots'];
</div>
</div>
</template>
<style lang="scss" scoped>
:deep(.ant-tree) {
// 勾选框居中
& .ant-tree-checkbox {
margin: 0;
margin-right: 6px;
}
// 展开图标居中
& .ant-tree-switcher {
display: flex;
align-items: center;
justify-content: center;
}
}
</style>

View File

@@ -8,12 +8,14 @@ import { $t } from '#/locales';
const appName = computed(() => preferences.app.name);
const logo = computed(() => preferences.logo.source);
const logoDark = computed(() => preferences.logo.sourceDark);
</script>
<template>
<AuthPageLayout
:app-name="appName"
:logo="logo"
:logo-dark="logoDark"
:page-description="$t('authentication.pageDesc')"
:page-title="$t('authentication.pageTitle')"
>

View File

@@ -7,6 +7,7 @@
"document": "Document",
"antdv": "Ant Design Vue Version",
"naive-ui": "Naive UI Version",
"element-plus": "Element Plus Version"
"element-plus": "Element Plus Version",
"tdesign": "TDesign Vue Version"
}
}

View File

@@ -5,7 +5,8 @@
"codeLogin": "Code Login",
"qrcodeLogin": "Qr Code Login",
"forgetPassword": "Forget Password",
"oauthLogin": "Oauth Login"
"oauthLogin": "Oauth Login",
"profile": "Profile"
},
"dashboard": {
"title": "Dashboard",

View File

@@ -7,6 +7,7 @@
"document": "文档",
"antdv": "Ant Design Vue 版本",
"naive-ui": "Naive UI 版本",
"element-plus": "Element Plus 版本"
"element-plus": "Element Plus 版本",
"tdesign": "TDesign Vue 版本"
}
}

View File

@@ -5,7 +5,7 @@
"codeLogin": "验证码登录",
"qrcodeLogin": "二维码登录",
"forgetPassword": "忘记密码",
"oauthLogin": "第三方登录"
"profile": "个人中心"
},
"dashboard": {
"title": "概览",

View File

@@ -85,6 +85,16 @@ const routes: RouteRecordRaw[] = [
order: 9999,
},
},
{
name: 'Profile',
path: '/profile',
component: () => import('#/views/_core/profile/index.vue'),
meta: {
icon: 'lucide:user',
hideInMenu: true,
title: $t('page.auth.profile'),
},
},
];
export default routes;

View File

@@ -0,0 +1,87 @@
<script setup lang="ts">
import { Input } from 'ant-design-vue';
interface Props {
captcha?: string;
label?: string;
loading?: boolean;
placeholder?: string;
}
withDefaults(defineProps<Props>(), {
captcha: '',
label: '验证码',
loading: false,
placeholder: '请输入验证码',
});
defineEmits<{ captchaClick: [] }>();
const modelValue = defineModel<string>({ default: '' });
</script>
<!-- 图片验证码 -->
<template>
<div class="flex w-full">
<div class="flex-1">
<Input
size="large"
id="code"
name="code"
type="text"
autocomplete="off"
required
v-model:value="modelValue"
:class="$attrs?.class ?? {}"
:label="label"
:placeholder="placeholder"
/>
</div>
<div class="captcha-image--container relative">
<img
:src="captcha"
class="h-[40px] w-[115px] cursor-pointer rounded-r-lg"
:class="{ 'pointer-events-none': loading }"
@click="$emit('captchaClick')"
/>
<div
v-if="loading"
class="absolute inset-0 flex cursor-not-allowed items-center justify-center rounded-r-lg bg-black/30"
>
<span class="captcha-loading"></span>
</div>
</div>
</div>
</template>
<style lang="scss">
@keyframes loading-rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.captcha-loading {
box-sizing: border-box;
display: inline-block;
width: 18px;
height: 18px;
border: 2px solid #fff;
border-bottom-color: transparent;
border-radius: 50%;
animation: loading-rotation 1s linear infinite;
}
/**
验证码输入框样式
去除右边的圆角
*/
input[id='code'] {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
</style>

View File

@@ -4,12 +4,13 @@ import type { LoginAndRegisterParams, VbenFormSchema } from '@vben/common-ui';
import type { TenantResp } from '#/api';
import type { CaptchaResponse } from '#/api/core/captcha';
import { computed, onMounted, ref, useTemplateRef } from 'vue';
import { computed, markRaw, onMounted, ref, useTemplateRef } from 'vue';
import { AuthenticationLogin, z } from '@vben/common-ui';
import { DEFAULT_TENANT_ID } from '@vben/constants';
import { $t } from '@vben/locales';
import { Input, Select } from 'ant-design-vue';
import { omit } from 'lodash-es';
import { tenantList } from '#/api';
@@ -17,6 +18,7 @@ import { captchaImage } from '#/api/core/captcha';
import { useAuthStore } from '#/store';
import { useLoginTenantId } from '../oauth-common';
import InputCaptcha from './input-captcha.vue';
import OAuthLogin from './oauth-login.vue';
defineOptions({ name: 'Login' });
@@ -73,15 +75,18 @@ const { loginTenantId } = useLoginTenantId();
const formSchema = computed((): VbenFormSchema[] => {
return [
{
component: 'VbenSelect',
component: markRaw(Select),
modelPropName: 'value',
componentProps: {
class: 'bg-background h-[40px] focus:border-primary',
contentClass: 'max-h-[256px] overflow-y-auto',
class: 'w-full',
size: 'large',
showSearch: true,
optionFilterProp: 'label',
options: tenantInfo.value.voList?.map((item) => ({
label: item.companyName,
value: item.tenantId,
})),
placeholder: $t('authentication.selectAccount'),
placeholder: $t('ui.formRules.selectRequired'),
},
defaultValue: DEFAULT_TENANT_ID,
dependencies: {
@@ -98,10 +103,12 @@ const formSchema = computed((): VbenFormSchema[] => {
rules: z.string().min(1, { message: $t('authentication.selectAccount') }),
},
{
component: 'VbenInput',
component: markRaw(Input),
modelPropName: 'value',
componentProps: {
class: 'focus:border-primary',
size: 'large',
placeholder: $t('authentication.usernameTip'),
allowClear: true,
},
defaultValue: 'admin',
fieldName: 'username',
@@ -109,10 +116,11 @@ const formSchema = computed((): VbenFormSchema[] => {
rules: z.string().min(1, { message: $t('authentication.usernameTip') }),
},
{
component: 'VbenInputPassword',
component: markRaw(Input.Password),
modelPropName: 'value',
componentProps: {
class: 'focus:border-primary',
placeholder: $t('authentication.password'),
size: 'large',
placeholder: $t('authentication.passwordTip'),
},
defaultValue: 'admin123',
fieldName: 'password',
@@ -120,7 +128,7 @@ const formSchema = computed((): VbenFormSchema[] => {
rules: z.string().min(5, { message: $t('authentication.passwordTip') }),
},
{
component: 'VbenInputCaptcha',
component: markRaw(InputCaptcha),
componentProps: {
captcha: captchaInfo.value.img,
class: 'focus:border-primary',

View File

@@ -0,0 +1,65 @@
<script setup lang="ts">
import type { BasicOption } from '@vben/types';
import type { VbenFormSchema } from '#/adapter/form';
import { computed, onMounted, ref } from 'vue';
import { ProfileBaseSetting } from '@vben/common-ui';
import { getUserInfoApi } from '#/api';
const profileBaseSettingRef = ref();
const MOCK_ROLES_OPTIONS: BasicOption[] = [
{
label: '管理员',
value: 'super',
},
{
label: '用户',
value: 'user',
},
{
label: '测试',
value: 'test',
},
];
const formSchema = computed((): VbenFormSchema[] => {
return [
{
fieldName: 'realName',
component: 'Input',
label: '姓名',
},
{
fieldName: 'username',
component: 'Input',
label: '用户名',
},
{
fieldName: 'roles',
component: 'Select',
componentProps: {
mode: 'tags',
options: MOCK_ROLES_OPTIONS,
},
label: '角色',
},
{
fieldName: 'introduction',
component: 'Textarea',
label: '个人简介',
},
];
});
onMounted(async () => {
const data = await getUserInfoApi();
profileBaseSettingRef.value.getFormApi().setValues(data);
});
</script>
<template>
<ProfileBaseSetting ref="profileBaseSettingRef" :form-schema="formSchema" />
</template>

View File

@@ -0,0 +1,31 @@
<script setup lang="ts">
import { computed } from 'vue';
import { ProfileNotificationSetting } from '@vben/common-ui';
const formSchema = computed(() => {
return [
{
value: true,
fieldName: 'accountPassword',
label: '账户密码',
description: '其他用户的消息将以站内信的形式通知',
},
{
value: true,
fieldName: 'systemMessage',
label: '系统消息',
description: '系统消息将以站内信的形式通知',
},
{
value: true,
fieldName: 'todoTask',
label: '待办任务',
description: '待办任务将以站内信的形式通知',
},
];
});
</script>
<template>
<ProfileNotificationSetting :form-schema="formSchema" />
</template>

View File

@@ -0,0 +1,63 @@
<script setup lang="ts">
import type { VbenFormSchema } from '#/adapter/form';
import { computed } from 'vue';
import { ProfilePasswordSetting, z } from '@vben/common-ui';
import { message } from 'ant-design-vue';
const formSchema = computed((): VbenFormSchema[] => {
return [
{
fieldName: 'oldPassword',
label: '旧密码',
component: 'VbenInputPassword',
componentProps: {
placeholder: '请输入旧密码',
},
},
{
fieldName: 'newPassword',
label: '新密码',
component: 'VbenInputPassword',
componentProps: {
passwordStrength: true,
placeholder: '请输入新密码',
},
},
{
fieldName: 'confirmPassword',
label: '确认密码',
component: 'VbenInputPassword',
componentProps: {
passwordStrength: true,
placeholder: '请再次输入新密码',
},
dependencies: {
rules(values) {
const { newPassword } = values;
return z
.string({ required_error: '请再次输入新密码' })
.min(1, { message: '请再次输入新密码' })
.refine((value) => value === newPassword, {
message: '两次输入的密码不一致',
});
},
triggerFields: ['newPassword'],
},
},
];
});
function handleSubmit() {
message.success('密码修改成功');
}
</script>
<template>
<ProfilePasswordSetting
class="w-1/3"
:form-schema="formSchema"
@submit="handleSubmit"
/>
</template>

View File

@@ -0,0 +1,43 @@
<script setup lang="ts">
import { computed } from 'vue';
import { ProfileSecuritySetting } from '@vben/common-ui';
const formSchema = computed(() => {
return [
{
value: true,
fieldName: 'accountPassword',
label: '账户密码',
description: '当前密码强度:强',
},
{
value: true,
fieldName: 'securityPhone',
label: '密保手机',
description: '已绑定手机138****8293',
},
{
value: true,
fieldName: 'securityQuestion',
label: '密保问题',
description: '未设置密保问题,密保问题可有效保护账户安全',
},
{
value: true,
fieldName: 'securityEmail',
label: '备用邮箱',
description: '已绑定邮箱ant***sign.com',
},
{
value: false,
fieldName: 'securityMfa',
label: 'MFA 设备',
description: '未绑定 MFA 设备,绑定后,可以进行二次确认',
},
];
});
</script>
<template>
<ProfileSecuritySetting :form-schema="formSchema" />
</template>

View File

@@ -5,6 +5,7 @@ import { onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { DEFAULT_TENANT_ID, LOGIN_PATH } from '@vben/constants';
import { preferences } from '@vben/preferences';
import { useAccessStore } from '@vben/stores';
import { cn } from '@vben/utils';
@@ -62,6 +63,9 @@ onMounted(async () => {
if (accessStore.accessToken) {
await authCallback(data);
message.success(`${source}授权成功`);
setTimeout(() => {
router.push(preferences.app.defaultHomePath);
}, 1500);
} else {
// 这里内部已经做了跳转到首页的操作
await authStore.authLogin(data as any);

View File

@@ -1,9 +1,10 @@
<script lang="ts" setup>
import type { EchartsUIType } from '@vben/plugins/echarts';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
import { onMounted, ref } from 'vue';
import { EchartsUI, useEcharts } from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef);
@@ -23,7 +24,7 @@ onMounted(() => {
{ name: '定制', value: 310 },
{ name: '技术支持', value: 274 },
{ name: '远程', value: 400 },
].sort((a, b) => {
].toSorted((a, b) => {
return a.value - b.value;
}),
name: '商业占比',

View File

@@ -115,9 +115,14 @@ const [BasicDrawer, drawerApi] = useVbenDrawer({
if (id) {
await formApi.setFieldValue('parentId', id);
if (update) {
// 没有依赖关系 同时加载
const [record] = await Promise.all([menuInfo(id), setupMenuSelect()]);
// 创建元组(不是数组 元素位置固定)
const promise = [
update ? menuInfo(id) : null,
setupMenuSelect(),
] as const;
// 并行获取菜单树选择和菜单信息
const [record] = await Promise.all(promise);
if (record) {
await formApi.setValues(record);
}
} else {

View File

@@ -97,11 +97,19 @@ async function handleClosed() {
<BasicForm>
<template #tip>
<div class="ml-7 w-full">
<Alert
message="私有桶使用自定义域名无法预览, 但可以正常上传/下载"
show-icon
type="warning"
/>
<Alert show-icon type="warning">
<template #message>
私有桶(minio)使用自定义域名需要参考
<a
href="https://gitee.com/dromara/RuoYi-Vue-Plus/issues/IBQIKC"
target="_blank"
class="text-primary"
>
支持minio预览私有桶
</a>
, 否则无法预览
</template>
</Alert>
</div>
</template>
</BasicForm>

View File

@@ -1,11 +1,15 @@
import type { FormSchemaGetter } from '#/adapter/form';
import type { VxeGridProps } from '#/adapter/vxe-table';
import { markRaw } from 'vue';
import { DictEnum } from '@vben/constants';
import { getPopupContainer } from '@vben/utils';
import { Tag } from 'ant-design-vue';
import { DefaultSlot } from '#/components/global/slot';
import { TreeSelectPanel } from '#/components/tree';
import { getDictOptions } from '#/utils/dict';
/**
@@ -177,15 +181,6 @@ export const authModalSchemas: FormSchemaGetter = () => [
fieldName: 'roleId',
label: '角色ID',
},
{
component: 'Radio',
dependencies: {
show: () => false,
triggerFields: [''],
},
fieldName: 'deptCheckStrictly',
label: 'deptCheckStrictly',
},
{
component: 'Input',
componentProps: {
@@ -214,12 +209,39 @@ export const authModalSchemas: FormSchemaGetter = () => [
label: '权限范围',
},
{
component: 'TreeSelect',
component: 'Radio',
dependencies: {
show: () => false,
triggerFields: [''],
},
fieldName: 'deptCheckStrictly',
label: 'deptCheckStrictly',
},
{
// 这种的场景基本上是一个组件需要绑定两个或以上的场景
component: markRaw(DefaultSlot),
defaultValue: [],
componentProps: {
rootDivAttrs: {
class: 'w-full',
},
},
dependencies: {
show: (values) => values.dataScope === '2',
triggerFields: ['dataScope'],
},
renderComponentContent: (model) => ({
default: (attrs: any) => {
return (
<TreeSelectPanel
expand-all-on-init={true}
treeData={attrs.treeData}
v-model:checkStrictly={model.deptCheckStrictly}
v-model:value={model.deptIds}
/>
);
},
}),
fieldName: 'deptIds',
help: '更改后立即生效',
label: '部门权限',

View File

@@ -1,14 +1,13 @@
<script setup lang="ts">
import type { DeptOption } from '#/api/system/role/model';
import { ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { cloneDeep } from '@vben/utils';
import { cloneDeep, findGroupParentIds } from '@vben/utils';
import { uniq } from 'lodash-es';
import { useVbenForm } from '#/adapter/form';
import { roleDataScope, roleDeptTree, roleInfo } from '#/api/system/role';
import { TreeSelectPanel } from '#/components/tree';
import { defaultFormValueGetter, useBeforeCloseDiff } from '#/utils/popup';
import { authModalSchemas } from './data';
@@ -26,26 +25,32 @@ const [BasicForm, formApi] = useVbenForm({
showDefaultActions: false,
});
const deptTree = ref<DeptOption[]>([]);
/**
* 保存部门数据 用于获取祖先节点
*/
let treeData: DeptOption[] = [];
async function setupDeptTree(id: number | string) {
const resp = await roleDeptTree(id);
formApi.setFieldValue('deptIds', resp.checkedKeys);
// 设置菜单信息
deptTree.value = resp.depts;
}
const { checkedKeys, depts } = resp;
async function customFormValueGetter() {
const v = await defaultFormValueGetter(formApi)();
// 获取勾选信息
const menuIds = deptSelectRef.value?.[0]?.getCheckedKeys() ?? [];
const mixStr = v + menuIds.join(',');
return mixStr;
/**
* 设置部门树数据
*/
formApi.updateSchema([
{ fieldName: 'deptIds', componentProps: { treeData: depts } },
]);
/**
* 设置选中 必须先传递treeData
* Note: Tree missing follow keys: '1981565541727186945'
*/
await formApi.setFieldValue('deptIds', checkedKeys);
treeData = depts;
}
const { onBeforeClose, markInitialized, resetInitialized } = useBeforeCloseDiff(
{
initializedGetter: customFormValueGetter,
currentGetter: customFormValueGetter,
initializedGetter: defaultFormValueGetter(formApi),
currentGetter: defaultFormValueGetter(formApi),
},
);
@@ -56,14 +61,14 @@ const [BasicModal, modalApi] = useVbenModal({
onConfirm: handleConfirm,
onOpenChange: async (isOpen) => {
if (!isOpen) {
treeData = [];
return null;
}
modalApi.modalLoading(true);
const { id } = modalApi.getData() as { id: number | string };
setupDeptTree(id);
const record = await roleInfo(id);
const [record] = await Promise.all([roleInfo(id), setupDeptTree(id)]);
await formApi.setValues(record);
markInitialized();
@@ -71,11 +76,6 @@ const [BasicModal, modalApi] = useVbenModal({
},
});
/**
* 这里拿到的是一个数组ref
*/
const deptSelectRef = ref();
async function handleConfirm() {
try {
modalApi.lock(true);
@@ -87,7 +87,15 @@ async function handleConfirm() {
const data = cloneDeep(await formApi.getValues());
// 不为自定义权限的话 删除部门id
if (data.dataScope === '2') {
const deptIds = deptSelectRef.value?.[0]?.getCheckedKeys() ?? [];
let { deptIds, deptCheckStrictly } = data;
// 节点关联 需要拼接上祖级ID(获取的是不带的)
if (deptCheckStrictly) {
// 找到所有父级ID
const parentIds = findGroupParentIds(treeData, deptIds, { id: 'id' });
// 去重
deptIds = uniq([...parentIds, ...deptIds]);
}
// 赋值
data.deptIds = deptIds;
} else {
data.deptIds = [];
@@ -107,29 +115,10 @@ async function handleClosed() {
await formApi.resetForm();
resetInitialized();
}
/**
* 通过回调更新 无法通过v-model
* @param value 菜单选择是否严格模式
*/
function handleCheckStrictlyChange(value: boolean) {
formApi.setFieldValue('deptCheckStrictly', value);
}
</script>
<template>
<BasicModal class="min-h-[600px] w-[550px]" title="分配权限">
<BasicForm>
<template #deptIds="slotProps">
<TreeSelectPanel
ref="deptSelectRef"
v-bind="slotProps"
:check-strictly="formApi.form.values.deptCheckStrictly"
:expand-all-on-init="true"
:tree-data="deptTree"
@check-strictly-change="handleCheckStrictlyChange"
/>
</template>
</BasicForm>
<BasicForm />
</BasicModal>
</template>

View File

@@ -15,6 +15,7 @@ import { Modal, Popconfirm, Space } from 'ant-design-vue';
import { useVbenVxeGrid, vxeCheckboxChecked } from '#/adapter/vxe-table';
import {
dictSyncTenant,
syncTenantConfig,
tenantExport,
tenantList,
tenantRemove,
@@ -144,6 +145,18 @@ function handleSyncTenantDict() {
},
});
}
function handleSyncTenantConfig() {
Modal.confirm({
title: '提示',
iconType: 'warning',
content: '确认同步租户参数配置?',
onOk: async () => {
await syncTenantConfig();
await tableApi.query();
},
});
}
</script>
<template>
@@ -157,6 +170,12 @@ function handleSyncTenantDict() {
>
同步租户字典
</a-button>
<a-button
v-access:code="['system:tenant:edit']"
@click="handleSyncTenantConfig"
>
同步租户参数配置
</a-button>
<a-button
v-access:code="['system:tenant:export']"
@click="handleDownloadExcel"

View File

@@ -0,0 +1,420 @@
<script setup lang="ts">
import type { ApprovalType } from '../type';
import type { User } from '#/api/core/user';
import type { TaskInfo } from '#/api/workflow/task/model';
import { computed, h } from 'vue';
import { useRouter } from 'vue-router';
import { useVbenModal } from '@vben/common-ui';
import { cn, getPopupContainer } from '@vben/utils';
import {
ArrowLeftOutlined,
CheckOutlined,
EditOutlined,
ExclamationCircleOutlined,
MenuOutlined,
RollbackOutlined,
UsergroupAddOutlined,
UsergroupDeleteOutlined,
UserOutlined,
} from '@ant-design/icons-vue';
import { Dropdown, Menu, MenuItem, Modal, Space } from 'ant-design-vue';
import {
cancelProcessApply,
deleteByInstanceIds,
} from '#/api/workflow/instance';
import {
taskOperation,
terminationTask,
updateAssignee,
} from '#/api/workflow/task';
import { approvalModal, approvalRejectionModal, flowInterfereModal } from '..';
import { approveWithReasonModal } from '../helper';
import userSelectModal from '../user-select-modal.vue';
interface Props {
/**
* 行数据的taskInfo?
*/
task?: TaskInfo;
/**
* 审批类型 根据不同类型显示按钮
*/
type: ApprovalType;
/**
* 为审批类型时候 显示的按钮(按钮权限)
*/
buttonPermissions: Record<string, boolean>;
}
const props = defineProps<Props>();
const emit = defineEmits<{
reload: [];
}>();
// 是否显示 `其他` 按钮
const showButtonOther = computed(() => {
const moreCollections = new Set(['addSign', 'subSign', 'transfer', 'trust']);
return Object.keys(props.buttonPermissions).some(
(key) => moreCollections.has(key) && props.buttonPermissions[key],
);
});
// 进行中 可以撤销
const revocable = computed(() => props.task?.flowStatus === 'waiting');
async function handleCancel() {
Modal.confirm({
title: '提示',
content: '确定要撤销该申请吗?',
centered: true,
okButtonProps: { danger: true },
onOk: async () => {
await cancelProcessApply({
businessId: props.task!.businessId,
message: '申请人撤销流程!',
});
emit('reload');
},
});
}
/**
* 是否可编辑/删除
*/
const editableAndRemoveable = computed(() => {
if (!props.task) {
return false;
}
return ['back', 'cancel', 'draft'].includes(props.task.flowStatus);
});
const router = useRouter();
function handleEdit() {
const path = props.task?.formPath;
if (path) {
router.push({ path, query: { id: props.task!.businessId } });
}
}
function handleRemove() {
Modal.confirm({
title: '提示',
content: '确定删除该申请吗?',
centered: true,
okButtonProps: { danger: true },
onOk: async () => {
await deleteByInstanceIds([props.task!.id]);
emit('reload');
},
});
}
/**
* 审批驳回
*/
const [RejectionModal, rejectionModalApi] = useVbenModal({
connectedComponent: approvalRejectionModal,
});
function handleRejection() {
rejectionModalApi.setData({
taskId: props.task?.id,
definitionId: props.task?.definitionId,
nodeCode: props.task?.nodeCode,
});
rejectionModalApi.open();
}
/**
* 审批终止
*/
function handleTermination() {
approveWithReasonModal({
title: '审批终止',
description: '确定终止当前审批流程吗?',
onOk: async (reason) => {
await terminationTask({ taskId: props.task!.id, comment: reason });
emit('reload');
},
});
}
/**
* 审批通过
*/
const [ApprovalModal, approvalModalApi] = useVbenModal({
connectedComponent: approvalModal,
});
function handleApproval() {
const { buttonPermissions } = props;
// 是否具有抄送权限
const copyPermission = buttonPermissions?.copy ?? false;
// 是否具有选人权限
const assignPermission = buttonPermissions?.pop ?? false;
approvalModalApi.setData({
taskId: props.task?.id,
copyPermission,
assignPermission,
});
approvalModalApi.open();
}
/**
* 委托
*/
const [DelegationModal, delegationModalApi] = useVbenModal({
connectedComponent: userSelectModal,
});
function handleDelegation(userList: User[]) {
if (userList.length === 0) return;
const current = userList[0];
approveWithReasonModal({
title: '委托',
description: `确定委托给[${current?.nickName}]吗?`,
onOk: async (reason) => {
await taskOperation(
{ taskId: props.task!.id, userId: current!.userId, message: reason },
'delegateTask',
);
emit('reload');
},
});
}
/**
* 转办
*/
const [TransferModal, transferModalApi] = useVbenModal({
connectedComponent: userSelectModal,
});
function handleTransfer(userList: User[]) {
if (userList.length === 0) return;
const current = userList[0];
approveWithReasonModal({
title: '转办',
description: `确定转办给[${current?.nickName}]吗?`,
onOk: async (reason) => {
await taskOperation(
{ taskId: props.task!.id, userId: current!.userId, message: reason },
'transferTask',
);
emit('reload');
},
});
}
const [AddSignatureModal, addSignatureModalApi] = useVbenModal({
connectedComponent: userSelectModal,
});
function handleAddSignature(userList: User[]) {
if (userList.length === 0) return;
const userIds = userList.map((user) => user.userId);
Modal.confirm({
title: '提示',
content: '确认加签吗?',
centered: true,
onOk: async () => {
await taskOperation({ taskId: props.task!.id, userIds }, 'addSignature');
emit('reload');
},
});
}
const [ReductionSignatureModal, reductionSignatureModalApi] = useVbenModal({
connectedComponent: userSelectModal,
});
function handleReductionSignature(userList: User[]) {
if (userList.length === 0) return;
const userIds = userList.map((user) => user.userId);
Modal.confirm({
title: '提示',
content: '确认减签吗?',
centered: true,
onOk: async () => {
await taskOperation(
{ taskId: props.task!.id, userIds },
'reductionSignature',
);
emit('reload');
},
});
}
// 流程干预
const [FlowInterfereModal, flowInterfereModalApi] = useVbenModal({
connectedComponent: flowInterfereModal,
});
function handleFlowInterfere() {
flowInterfereModalApi.setData({ taskId: props.task?.id });
flowInterfereModalApi.open();
}
// 修改办理人
const [UpdateAssigneeModal, updateAssigneeModalApi] = useVbenModal({
connectedComponent: userSelectModal,
});
function handleUpdateAssignee(userList: User[]) {
if (userList.length === 0) return;
const current = userList[0];
if (!current) return;
Modal.confirm({
title: '修改办理人',
content: `确定修改办理人为${current?.nickName}吗?`,
centered: true,
onOk: async () => {
await updateAssignee([props.task!.id], current.userId);
emit('reload');
},
});
}
/**
* 是否显示 加签/减签操作
*/
const showMultiActions = computed(() => {
if (!props.task) {
return false;
}
if (Number(props.task.nodeRatio) > 0) {
return true;
}
return false;
});
</script>
<template>
<div
:class="
cn(
'absolute bottom-0 left-0',
'border-t-solid border-t-[1px]',
'bg-background w-full p-3',
)
"
>
<div class="flex justify-end">
<Space v-if="type === 'myself'">
<a-button
v-if="revocable"
danger
ghost
type="primary"
:icon="h(RollbackOutlined)"
@click="handleCancel"
>
撤销申请
</a-button>
<a-button
type="primary"
ghost
v-if="editableAndRemoveable"
:icon="h(EditOutlined)"
@click="handleEdit"
>
重新编辑
</a-button>
<a-button
v-if="editableAndRemoveable"
danger
ghost
type="primary"
:icon="h(EditOutlined)"
@click="handleRemove"
>
删除
</a-button>
</Space>
<Space v-if="type === 'approve'">
<a-button
type="primary"
ghost
:icon="h(CheckOutlined)"
@click="handleApproval"
>
通过
</a-button>
<a-button
v-if="buttonPermissions?.termination"
danger
ghost
type="primary"
:icon="h(ExclamationCircleOutlined)"
@click="handleTermination"
>
终止
</a-button>
<a-button
v-if="buttonPermissions?.back"
danger
ghost
type="primary"
:icon="h(ArrowLeftOutlined)"
@click="handleRejection"
>
驳回
</a-button>
<Dropdown
:get-popup-container="getPopupContainer"
placement="bottomRight"
>
<template #overlay>
<Menu>
<MenuItem
v-if="buttonPermissions?.trust"
key="1"
@click="() => delegationModalApi.open()"
>
<UserOutlined class="mr-2" />委托
</MenuItem>
<MenuItem
v-if="buttonPermissions?.transfer"
key="2"
@click="() => transferModalApi.open()"
>
<RollbackOutlined class="mr-2" /> 转办
</MenuItem>
<MenuItem
v-if="showMultiActions && buttonPermissions?.addSign"
key="3"
@click="() => addSignatureModalApi.open()"
>
<UsergroupAddOutlined class="mr-2" /> 加签
</MenuItem>
<MenuItem
v-if="showMultiActions && buttonPermissions?.subSign"
key="4"
@click="() => reductionSignatureModalApi.open()"
>
<UsergroupDeleteOutlined class="mr-2" /> 减签
</MenuItem>
</Menu>
</template>
<a-button v-if="showButtonOther" :icon="h(MenuOutlined)">
其他
</a-button>
</Dropdown>
<ApprovalModal @complete="$emit('reload')" />
<RejectionModal @complete="$emit('reload')" />
<DelegationModal mode="single" @finish="handleDelegation" />
<TransferModal mode="single" @finish="handleTransfer" />
<AddSignatureModal mode="multiple" @finish="handleAddSignature" />
<ReductionSignatureModal
mode="multiple"
@finish="handleReductionSignature"
/>
</Space>
<Space v-if="type === 'admin'">
<a-button @click="handleFlowInterfere"> 流程干预 </a-button>
<a-button @click="() => updateAssigneeModalApi.open()">
修改办理人
</a-button>
<FlowInterfereModal @complete="$emit('reload')" />
<UpdateAssigneeModal mode="single" @finish="handleUpdateAssignee" />
</Space>
</div>
</div>
</template>

View File

@@ -0,0 +1 @@
export { default as FlowActions } from './flow-actions.vue';

View File

@@ -43,6 +43,8 @@ const [BasicModal, modalApi] = useVbenModal({
if (!isOpen) {
return null;
}
modalApi.modalLoading(true);
const { taskId } = modalApi.getData() as ModalProps;
// 查询是否有按钮权限
@@ -63,6 +65,8 @@ const [BasicModal, modalApi] = useVbenModal({
},
},
]);
modalApi.modalLoading(false);
},
});
@@ -108,6 +112,11 @@ const [BasicForm, formApi] = useVbenForm({
component: 'Input',
defaultValue: [],
label: '抄送人',
// 默认不显示
dependencies: {
if: false,
triggerFields: [''],
},
},
],
showDefaultActions: false,

View File

@@ -20,8 +20,6 @@ defineOptions({
defineProps<{
currentFlowInfo: FlowInfoResponse;
iframeHeight: number;
iframeLoaded: boolean;
task: TaskInfo;
}>();
</script>

View File

@@ -1,117 +1,72 @@
<!-- 该文件需要重构 但我没空 -->
<!--
TODO: 优化项
会先加载流程信息 再加载业务表单信息
-->
<script setup lang="ts">
import type { User } from '#/api/core/user';
import type { ApprovalType } from './type';
import type { FlowInfoResponse } from '#/api/workflow/instance/model';
import type { TaskInfo } from '#/api/workflow/task/model';
import { computed, h, onUnmounted, ref, watch } from 'vue';
import { useRouter } from 'vue-router';
import { computed, ref, watch } from 'vue';
import { Fallback, useVbenModal, VbenAvatar } from '@vben/common-ui';
import { Fallback, VbenAvatar } from '@vben/common-ui';
import { DictEnum } from '@vben/constants';
import { cn, getPopupContainer } from '@vben/utils';
import { cn } from '@vben/utils';
import {
ArrowLeftOutlined,
CheckOutlined,
CopyOutlined,
EditOutlined,
ExclamationCircleOutlined,
MenuOutlined,
RollbackOutlined,
UsergroupAddOutlined,
UsergroupDeleteOutlined,
UserOutlined,
} from '@ant-design/icons-vue';
import { useClipboard, useEventListener } from '@vueuse/core';
import {
Card,
Divider,
Dropdown,
Menu,
MenuItem,
message,
Modal,
Space,
TabPane,
Tabs,
} from 'ant-design-vue';
import { isObject } from 'lodash-es';
import { CopyOutlined } from '@ant-design/icons-vue';
import { useClipboard } from '@vueuse/core';
import { Card, Divider, message, TabPane, Tabs } from 'ant-design-vue';
import {
cancelProcessApply,
deleteByInstanceIds,
flowInfo,
} from '#/api/workflow/instance';
import {
getTaskByTaskId,
taskOperation,
terminationTask,
updateAssignee,
} from '#/api/workflow/task';
import { flowInfo } from '#/api/workflow/instance';
import { getTaskByTaskId } from '#/api/workflow/task';
import { renderDict } from '#/utils/render';
import { approvalModal, approvalRejectionModal, flowInterfereModal } from '.';
import { FlowActions } from './actions';
import ApprovalDetails from './approval-details.vue';
import FlowPreview from './flow-preview.vue';
import { approveWithReasonModal } from './helper';
import userSelectModal from './user-select-modal.vue';
defineOptions({
name: 'ApprovalPanel',
inheritAttrs: false,
});
const props = defineProps<{ task?: TaskInfo; type: ApprovalType }>();
const props = defineProps<Props>();
/**
* 下面按钮点击后会触发的事件
*/
const emit = defineEmits<{ reload: [] }>();
defineEmits<{ reload: [] }>();
interface Props {
/**
* 行数据(list)的info
*/
task?: TaskInfo;
/**
* 审批类型
*/
type: ApprovalType;
}
const currentTask = ref<TaskInfo>();
/**
* 是否显示 加签/减签操作
* 目前的作用只为了获取按钮权限 因为list接口(行数据)获取为空
*/
const showMultiActions = computed(() => {
if (!currentTask.value) {
return false;
}
if (Number(currentTask.value.nodeRatio) > 0) {
return true;
}
return false;
});
const onlyForBtnPermissionTask = ref<TaskInfo>();
/**
* 按钮权限
*/
const buttonPermissions = computed(() => {
const record: Record<string, boolean> = {};
if (!currentTask.value) {
if (!onlyForBtnPermissionTask.value) {
return record;
}
currentTask.value.buttonList.forEach((item) => {
onlyForBtnPermissionTask.value.buttonList.forEach((item) => {
record[item.code] = item.show;
});
return record;
});
// 是否显示 `其他` 按钮
const showButtonOther = computed(() => {
const moreCollections = new Set(['addSign', 'subSign', 'transfer', 'trust']);
return Object.keys(buttonPermissions.value).some(
(key) => moreCollections.has(key) && buttonPermissions.value[key],
);
});
/**
* myself 我发起的
* readonly 只读 只用于查看
* approve 审批
* admin 流程监控 - 待办任务使用
*/
type ApprovalType = 'admin' | 'approve' | 'myself' | 'readonly';
const showFooter = computed(() => {
if (props.type === 'readonly') {
return false;
@@ -131,36 +86,34 @@ const currentFlowInfo = ref<FlowInfoResponse>();
* card的loading状态
*/
const loading = ref(false);
const iframeLoaded = ref(false);
const iframeHeight = ref(300);
useEventListener('message', (event) => {
const data = event.data as { [key: string]: any; type: string };
if (!isObject(data)) return;
/**
* iframe通信 加载完毕后才显示表单 解决卡顿问题
*/
if (data.type === 'mounted') {
iframeLoaded.value = true;
}
/**
* 高度与表单高度保持一致
*/
if (data.type === 'height') {
const height = data.height;
iframeHeight.value = height;
}
});
async function handleLoadInfo(task: TaskInfo | undefined) {
if (!task) {
return null;
}
try {
if (!task) return null;
loading.value = true;
iframeLoaded.value = false;
const resp = await flowInfo(task.businessId);
currentFlowInfo.value = resp;
const taskResp = await getTaskByTaskId(props.task!.id);
currentTask.value = taskResp;
/**
* 不为审批不需要调用`getTaskByTaskId`接口
*/
if (props.type !== 'approve') {
const flowResp = await flowInfo(task.businessId);
currentFlowInfo.value = flowResp;
return;
}
/**
* getTaskByTaskId主要为了获取按钮权限 目前没有其他功能
* 行数据(即props.task)获取的是没有按钮权限的
*/
const [flowResp, taskResp] = await Promise.all([
flowInfo(task.businessId),
getTaskByTaskId(task.id),
]);
currentFlowInfo.value = flowResp;
onlyForBtnPermissionTask.value = taskResp;
} catch (error) {
console.error(error);
} finally {
@@ -170,217 +123,6 @@ async function handleLoadInfo(task: TaskInfo | undefined) {
watch(() => props.task, handleLoadInfo);
onUnmounted(() => (currentFlowInfo.value = undefined));
// 进行中 可以撤销
const revocable = computed(() => props.task?.flowStatus === 'waiting');
async function handleCancel() {
Modal.confirm({
title: '提示',
content: '确定要撤销该申请吗?',
centered: true,
okButtonProps: { danger: true },
onOk: async () => {
await cancelProcessApply({
businessId: props.task!.businessId,
message: '申请人撤销流程!',
});
emit('reload');
},
});
}
/**
* 是否可编辑/删除
*/
const editableAndRemoveable = computed(() => {
if (!props.task) {
return false;
}
return ['back', 'cancel', 'draft'].includes(props.task.flowStatus);
});
const router = useRouter();
function handleEdit() {
const path = props.task?.formPath;
if (path) {
router.push({ path, query: { id: props.task!.businessId } });
}
}
function handleRemove() {
Modal.confirm({
title: '提示',
content: '确定删除该申请吗?',
centered: true,
okButtonProps: { danger: true },
onOk: async () => {
await deleteByInstanceIds([props.task!.id]);
emit('reload');
},
});
}
/**
* 审批驳回
*/
const [RejectionModal, rejectionModalApi] = useVbenModal({
connectedComponent: approvalRejectionModal,
});
function handleRejection() {
rejectionModalApi.setData({
taskId: props.task?.id,
definitionId: props.task?.definitionId,
nodeCode: props.task?.nodeCode,
});
rejectionModalApi.open();
}
/**
* 审批终止
*/
function handleTermination() {
approveWithReasonModal({
title: '审批终止',
description: '确定终止当前审批流程吗?',
onOk: async (reason) => {
await terminationTask({ taskId: props.task!.id, comment: reason });
emit('reload');
},
});
}
/**
* 审批通过
*/
const [ApprovalModal, approvalModalApi] = useVbenModal({
connectedComponent: approvalModal,
});
function handleApproval() {
// 是否具有抄送权限
const copyPermission = buttonPermissions.value?.copy ?? false;
// 是否具有选人权限
const assignPermission = buttonPermissions.value?.pop ?? false;
approvalModalApi.setData({
taskId: props.task?.id,
copyPermission,
assignPermission,
});
approvalModalApi.open();
}
/**
* TODO: 1提取公共函数 2原版是可以填写意见的(message参数)
*/
/**
* 委托
*/
const [DelegationModal, delegationModalApi] = useVbenModal({
connectedComponent: userSelectModal,
});
function handleDelegation(userList: User[]) {
if (userList.length === 0) return;
const current = userList[0];
approveWithReasonModal({
title: '委托',
description: `确定委托给[${current?.nickName}]吗?`,
onOk: async (reason) => {
await taskOperation(
{ taskId: props.task!.id, userId: current!.userId, message: reason },
'delegateTask',
);
emit('reload');
},
});
}
/**
* 转办
*/
const [TransferModal, transferModalApi] = useVbenModal({
connectedComponent: userSelectModal,
});
function handleTransfer(userList: User[]) {
if (userList.length === 0) return;
const current = userList[0];
approveWithReasonModal({
title: '转办',
description: `确定转办给[${current?.nickName}]吗?`,
onOk: async (reason) => {
await taskOperation(
{ taskId: props.task!.id, userId: current!.userId, message: reason },
'transferTask',
);
emit('reload');
},
});
}
const [AddSignatureModal, addSignatureModalApi] = useVbenModal({
connectedComponent: userSelectModal,
});
function handleAddSignature(userList: User[]) {
if (userList.length === 0) return;
const userIds = userList.map((user) => user.userId);
Modal.confirm({
title: '提示',
content: '确认加签吗?',
centered: true,
onOk: async () => {
await taskOperation({ taskId: props.task!.id, userIds }, 'addSignature');
emit('reload');
},
});
}
const [ReductionSignatureModal, reductionSignatureModalApi] = useVbenModal({
connectedComponent: userSelectModal,
});
function handleReductionSignature(userList: User[]) {
if (userList.length === 0) return;
const userIds = userList.map((user) => user.userId);
Modal.confirm({
title: '提示',
content: '确认减签吗?',
centered: true,
onOk: async () => {
await taskOperation(
{ taskId: props.task!.id, userIds },
'reductionSignature',
);
emit('reload');
},
});
}
// 流程干预
const [FlowInterfereModal, flowInterfereModalApi] = useVbenModal({
connectedComponent: flowInterfereModal,
});
function handleFlowInterfere() {
flowInterfereModalApi.setData({ taskId: props.task?.id });
flowInterfereModalApi.open();
}
// 修改办理人
const [UpdateAssigneeModal, updateAssigneeModalApi] = useVbenModal({
connectedComponent: userSelectModal,
});
function handleUpdateAssignee(userList: User[]) {
if (userList.length === 0) return;
const current = userList[0];
if (!current) return;
Modal.confirm({
title: '修改办理人',
content: `确定修改办理人为${current?.nickName}吗?`,
centered: true,
onOk: async () => {
await updateAssignee([props.task!.id], current.userId);
emit('reload');
},
});
}
/**
* 不加legacy在本地开发没有问题
* 打包后在一些设备会无法复制 使用legacy来保证兼容性
@@ -407,6 +149,7 @@ async function handleCopy(text: string) {
<CopyOutlined class="cursor-pointer" @click="handleCopy(task.id)" />
</div>
</template>
<template #extra>
<a-button size="small" @click="() => handleLoadInfo(task)">
<div class="flex items-center justify-center">
@@ -414,6 +157,7 @@ async function handleCopy(text: string) {
</div>
</a-button>
</template>
<div class="flex flex-col gap-5 p-4">
<div class="flex flex-col gap-3">
<div class="flex items-center gap-2">
@@ -426,19 +170,24 @@ async function handleCopy(text: string) {
/>
</div>
</div>
<div class="flex items-center gap-2">
<VbenAvatar
:alt="task?.createByName ?? ''"
class="bg-primary size-[28px] rounded-full text-white"
src=""
/>
<span>{{ task.createByName }}</span>
<div class="flex items-center opacity-50">
<div class="flex items-center gap-1">
<span class="icon-[bxs--category-alt] size-[16px]"></span>
流程分类: {{ task.categoryName }}
</div>
<Divider type="vertical" />
<div class="flex items-center gap-1">
<span class="icon-[mdi--clock-outline] size-[16px]"></span>
提交时间: {{ task.createTime }}
@@ -446,154 +195,32 @@ async function handleCopy(text: string) {
</div>
</div>
</div>
<Tabs v-if="currentFlowInfo" class="flex-1">
<TabPane key="1" tab="审批详情">
<ApprovalDetails
:current-flow-info="currentFlowInfo"
:iframe-loaded="iframeLoaded"
:iframe-height="iframeHeight"
:task="task"
/>
</TabPane>
<TabPane key="2" tab="审批流程图">
<FlowPreview :instance-id="currentFlowInfo.instanceId" />
</TabPane>
</Tabs>
</div>
<!-- 固定底部 -->
<!-- 固定底部 占位高度 -->
<div class="h-[58px]"></div>
<div
<FlowActions
v-if="showFooter"
:class="
cn(
'absolute bottom-0 left-0',
'border-t-solid border-t-[1px]',
'bg-background w-full p-3',
)
"
>
<div class="flex justify-end">
<Space v-if="type === 'myself'">
<a-button
v-if="revocable"
danger
ghost
type="primary"
:icon="h(RollbackOutlined)"
@click="handleCancel"
>
撤销申请
</a-button>
<a-button
type="primary"
ghost
v-if="editableAndRemoveable"
:icon="h(EditOutlined)"
@click="handleEdit"
>
重新编辑
</a-button>
<a-button
v-if="editableAndRemoveable"
danger
ghost
type="primary"
:icon="h(EditOutlined)"
@click="handleRemove"
>
删除
</a-button>
</Space>
<Space v-if="type === 'approve'">
<a-button
type="primary"
ghost
:icon="h(CheckOutlined)"
@click="handleApproval"
>
通过
</a-button>
<a-button
v-if="buttonPermissions?.termination"
danger
ghost
type="primary"
:icon="h(ExclamationCircleOutlined)"
@click="handleTermination"
>
终止
</a-button>
<a-button
v-if="buttonPermissions?.back"
danger
ghost
type="primary"
:icon="h(ArrowLeftOutlined)"
@click="handleRejection"
>
驳回
</a-button>
<Dropdown
:get-popup-container="getPopupContainer"
placement="bottomRight"
>
<template #overlay>
<Menu>
<MenuItem
v-if="buttonPermissions?.trust"
key="1"
@click="() => delegationModalApi.open()"
>
<UserOutlined class="mr-2" />委托
</MenuItem>
<MenuItem
v-if="buttonPermissions?.transfer"
key="2"
@click="() => transferModalApi.open()"
>
<RollbackOutlined class="mr-2" /> 转办
</MenuItem>
<MenuItem
v-if="showMultiActions && buttonPermissions?.addSign"
key="3"
@click="() => addSignatureModalApi.open()"
>
<UsergroupAddOutlined class="mr-2" /> 加签
</MenuItem>
<MenuItem
v-if="showMultiActions && buttonPermissions?.subSign"
key="4"
@click="() => reductionSignatureModalApi.open()"
>
<UsergroupDeleteOutlined class="mr-2" /> 减签
</MenuItem>
</Menu>
</template>
<a-button v-if="showButtonOther" :icon="h(MenuOutlined)">
其他
</a-button>
</Dropdown>
<ApprovalModal @complete="$emit('reload')" />
<RejectionModal @complete="$emit('reload')" />
<DelegationModal mode="single" @finish="handleDelegation" />
<TransferModal mode="single" @finish="handleTransfer" />
<AddSignatureModal mode="multiple" @finish="handleAddSignature" />
<ReductionSignatureModal
mode="multiple"
@finish="handleReductionSignature"
/>
</Space>
<Space v-if="type === 'admin'">
<a-button @click="handleFlowInterfere"> 流程干预 </a-button>
<a-button @click="() => updateAssigneeModalApi.open()">
修改办理人
</a-button>
<FlowInterfereModal @complete="$emit('reload')" />
<UpdateAssigneeModal mode="single" @finish="handleUpdateAssignee" />
</Space>
</div>
</div>
:type="type"
:task="task"
:button-permissions="buttonPermissions"
@reload="$emit('reload')"
/>
</Card>
<slot v-else name="empty">
<Fallback title="点击左侧选择" />
</slot>

View File

@@ -45,11 +45,8 @@ onMounted(async () => {
}));
});
/**
* 这里无法处理昵称中带,的情况
*/
const isMultiplePerson = computed(
() => props.item.approveName?.split(',').length > 1,
() => props.item.approver?.split(',').length > 1,
);
</script>
@@ -87,6 +84,7 @@ const isMultiplePerson = computed(
</div>
<div :class="cn('mt-2 flex flex-wrap gap-2')" v-if="isMultiplePerson">
<!-- 如果昵称中带, 这里的处理是不准确的 -->
<div
:class="cn('bg-foreground/5 flex items-center rounded-full', 'p-1')"
v-for="(name, index) in item.approveName.split(',')"

View File

@@ -1,21 +1,20 @@
<script setup lang="ts">
import type { Flow } from '#/api/workflow/instance/model';
import { Timeline } from 'ant-design-vue';
import { Empty, Timeline } from 'ant-design-vue';
import ApprovalTimelineItem from './approval-timeline-item.vue';
const props = defineProps<{
interface Props {
list: Flow[];
}>();
}
defineProps<Props>();
</script>
<template>
<Timeline v-if="props.list.length > 0">
<ApprovalTimelineItem
v-for="item in props.list"
:key="item.id"
:item="item"
/>
<Timeline v-if="list.length > 0">
<ApprovalTimelineItem v-for="item in list" :key="item.id" :item="item" />
</Timeline>
<Empty v-else />
</template>

View File

@@ -1,3 +1,5 @@
<!-- 流程图预览组件 -->
<script setup lang="ts">
import { useAppConfig } from '@vben/hooks';
import { stringify } from '@vben/request';
@@ -7,7 +9,14 @@ import { useWarmflowIframe } from './hook';
defineOptions({ name: 'FlowPreview' });
const props = defineProps<{ instanceId: string }>();
const props = defineProps<Props>();
interface Props {
/**
* 流程实例ID
*/
instanceId: string;
}
const { clientId } = useAppConfig(import.meta.env, import.meta.env.PROD);
@@ -21,6 +30,7 @@ const params = {
/**
* iframe地址
* 后端地址 + 固定flow地址拼接
*/
const url = `${import.meta.env.VITE_GLOB_API_URL}/warm-flow-ui/index.html?${stringify(params)}`;
@@ -28,5 +38,9 @@ const { iframeRef } = useWarmflowIframe();
</script>
<template>
<iframe ref="iframeRef" :src="url" class="h-[500px] w-full border"></iframe>
<iframe
ref="iframeRef"
:src="url"
class="h-[600px] w-full rounded-[6px] border"
></iframe>
</template>

View File

@@ -10,20 +10,26 @@ export function useWarmflowIframe() {
const iframeRef = useTemplateRef<HTMLIFrameElement>('iframeRef');
const { isDark } = usePreferences();
async function iframeLoadEvent() {
/**
* TODO: 这里可以优化 因为拿不到内部vue的mount状态
*/
await new Promise((resolve) => setTimeout(resolve, 500));
const theme = isDark.value ? 'theme-dark' : 'theme-light';
iframeRef.value?.contentWindow?.postMessage({ type: theme });
}
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 });
});
iframeRef.value?.addEventListener('load', iframeLoadEvent);
});
// onBeforeUnmount(() => {
// iframeRef.value?.removeEventListener('load', iframeLoadEvent);
// });
// 监听主题切换 通知iframe切换
watch(isDark, (dark) => {
if (!iframeRef.value) {

View File

@@ -0,0 +1,8 @@
export {};
/**
* myself 我发起的
* readonly 只读 只用于查看
* approve 审批(我的待办)
* admin 流程监控 - 待办任务使用
*/
export type ApprovalType = 'admin' | 'approve' | 'myself' | 'readonly';

View File

@@ -150,9 +150,10 @@ const [InstanceVariableModal, instanceVariableModalApi] = useVbenModal({
connectedComponent: instanceVariableModal,
});
function handleVariable(row: Recordable<any>) {
instanceVariableModalApi.setData({ record: row.variable });
instanceVariableModalApi.setData({ instanceId: row.id });
instanceVariableModalApi.open();
}
const [FlowInfoModal, flowInfoModalApi] = useVbenModal({
connectedComponent: flowInfoModal,
});

View File

@@ -1,28 +1,214 @@
<script setup lang="ts">
<script setup lang="tsx">
import { ref } from 'vue';
import { JsonPreview, useVbenModal } from '@vben/common-ui';
import { cn, getPopupContainer } from '@vben/utils';
import { message, Modal, Tag } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { instanceVariable, updateFlowVariable } from '#/api/workflow/instance';
interface ModalData {
/**
* 变量 json字符串
*/
record: string;
instanceId: string;
}
const data = ref({});
const [BasicModal, modalApi] = useVbenModal({
title: '流程变量',
fullscreenButton: false,
footer: false,
onOpenChange: (visible) => {
onOpenChange: async (visible) => {
if (!visible) {
data.value = {};
return null;
}
const recordString = modalApi.getData().record;
data.value = JSON.parse(recordString);
modalApi.modalLoading(true);
await loadData();
modalApi.modalLoading(false);
},
});
const fieldTypeColors = {
string: 'cyan',
number: 'blue',
boolean: 'orange',
object: 'purple',
};
function getFieldTypeColor(fieldType: string) {
return (
fieldTypeColors[fieldType as keyof typeof fieldTypeColors] ?? 'default'
);
}
async function loadData() {
const { instanceId } = modalApi.getData() as ModalData;
const resp = await instanceVariable(instanceId);
const jsonObj = JSON.parse(resp.variable);
data.value = jsonObj;
// 表单
const objEntry = Object.entries(jsonObj);
interface OptionsType {
label: string;
value: string;
fieldType: string;
}
formApi.updateSchema([
{
fieldName: 'key',
componentProps: {
options: objEntry.map(
([key, value]) =>
({
label: key,
value: key,
fieldType: typeof value,
}) as OptionsType,
),
},
renderComponentContent: () => ({
option: (option: OptionsType) => (
<div>
{option.label}
<Tag class="ml-1" color={getFieldTypeColor(option.fieldType)}>
{option.fieldType}
</Tag>
</div>
),
}),
},
]);
}
const [Form, formApi] = useVbenForm({
commonConfig: {
componentProps: {
class: 'w-full',
allowClear: true,
},
labelWidth: 80,
},
schema: [
{
fieldName: 'key',
component: 'Select',
label: '变量名称',
rules: 'selectRequired',
componentProps: {
getPopupContainer,
},
},
{
fieldName: 'valueType',
component: 'Select',
label: '变量类型',
rules: 'selectRequired',
componentProps: {
getPopupContainer,
options: [
{
label: 'string',
value: 'string',
},
{
label: 'boolean | number | object (使用JSON.parse)',
value: 'object',
},
],
},
},
{
fieldName: 'value',
component: 'Input',
label: '变量值',
rules: 'required',
},
],
resetButtonOptions: {
show: false,
},
submitButtonOptions: {
content: '修改',
},
handleSubmit: async (values) => {
console.log(values);
Modal.confirm({
title: '修改流程变量',
content: '确认修改流程变量吗?',
centered: true,
okButtonProps: {
danger: true,
},
onOk: async () => {
await handleSubmit(values);
},
});
},
});
async function handleSubmit(values: any) {
try {
modalApi.lock(true);
const { instanceId } = modalApi.getData() as ModalData;
let transformValue = values.value;
if (values.valueType !== 'string') {
try {
transformValue = JSON.parse(values.value);
} catch (error) {
console.error(error);
if (error instanceof Error) {
message.error(error.message);
}
throw error;
}
}
// 修改
const requestData = {
instanceId,
key: values.key,
value: transformValue,
};
await updateFlowVariable(requestData);
await formApi.resetForm();
// 查询修改后的
const resp = await instanceVariable(instanceId);
const jsonObj = JSON.parse(resp.variable);
data.value = jsonObj;
} catch (error) {
console.error(error);
} finally {
modalApi.lock(false);
}
}
</script>
<template>
<BasicModal>
<div class="min-h-[400px] overflow-y-auto">
<div
:class="cn('min-h-[400px] overflow-y-auto border', 'rounded-[4px] p-2')"
>
<JsonPreview :data="data" />
</div>
<div class="mt-2 break-all text-sm font-medium text-orange-500">
需要支持变量类型需要更改后端代码(原版只支持string类型)
<div>
ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/FlowVariableBo.java
</div>
将value的类型改为Object才能使用
</div>
<Form class="mt-2" />
</BasicModal>
</template>

View File

@@ -10,8 +10,7 @@ import { getVxePopupContainer } from '@vben/utils';
import { Modal, Popconfirm, Space } from 'ant-design-vue';
import { useVbenVxeGrid, vxeCheckboxChecked } from '#/adapter/vxe-table';
import { configRemove } from '#/api/system/config';
import { spelList } from '#/api/workflow/spel';
import { spelList,spelDelete } from '#/api/workflow/spel';
import { columns, querySchema } from './data';
import spelDrawer from './spel-drawer.vue';
@@ -75,7 +74,7 @@ async function handleEdit(record: Spel) {
}
async function handleDelete(row: Spel) {
await configRemove([row.id]);
await spelDelete([row.id]);
await tableApi.query();
}
@@ -87,7 +86,7 @@ function handleMultiDelete() {
okType: 'danger',
content: `确认删除选中的${ids.length}条记录吗?`,
onOk: async () => {
await configRemove(ids);
await spelDelete(ids);
await tableApi.query();
},
});

View File

@@ -7,14 +7,19 @@
"acmr",
"antd",
"antdv",
"archiver",
"astro",
"axios",
"brotli",
"clientid",
"cascader",
"clsx",
"defu",
"demi",
"dotenv",
"echarts",
"ependencies",
"esbuild",
"esno",
"etag",
"execa",
@@ -24,6 +29,8 @@
"intlify",
"ipaddr",
"jsencrypt",
"isequal",
"jspm",
"lockb",
"logininfor",
"lucide",
@@ -32,7 +39,9 @@
"mkdist",
"mockjs",
"naiveui",
"napi",
"nocheck",
"nolebase",
"noopener",
"noreferrer",
"nprogress",
@@ -45,11 +54,14 @@
"Qqchat",
"qrcode",
"ruoyi",
"reka",
"rollup",
"shadcn",
"sonner",
"sortablejs",
"styl",
"taze",
"tdesign",
"ui-kit",
"uicons",
"unplugin",
@@ -59,19 +71,20 @@
"vite",
"vitejs",
"vitepress",
"vitest",
"vnode",
"vueuse",
"yxxx"
],
"ignorePaths": [
"**/node_modules/**",
"**/dist/**",
"**/*-dist/**",
"**/icons/**",
"pnpm-lock.yaml",
"**/*.log",
"**/*.test.ts",
"**/*.spec.ts",
"**/__tests__/**"
"**/*.test.ts",
"**/__tests__/**",
"**/dist/**",
"**/icons/**",
"**/node_modules/**",
"pnpm-lock.yaml"
]
}

View File

@@ -19,15 +19,15 @@ const parsedFiles = computed(() => {
</script>
<template>
<div class="border-border shadow-float relative rounded-xl border">
<div class="relative rounded-xl border border-border shadow-float">
<div
class="not-prose relative w-full overflow-x-auto rounded-t-lg px-4 py-6"
>
<div class="flex w-full max-w-[700px] px-2">
<ClientOnly>
<slot v-if="parsedFiles.length > 0"></slot>
<div v-else class="text-destructive text-sm">
<span class="bg-destructive text-foreground rounded-sm px-1 py-1">
<div v-else class="text-sm text-destructive">
<span class="rounded-sm bg-destructive px-1 py-1 text-foreground">
ERROR:
</span>
The preview directory does not exist. Please check the 'dir'

View File

@@ -12,7 +12,7 @@ import {
TabsList,
TabsRoot,
TabsTrigger,
} from 'radix-vue';
} from 'reka-ui';
defineOptions({
inheritAttrs: false,
@@ -48,15 +48,15 @@ const toggleOpen = () => {
<template>
<TabsRoot
v-model="currentTab"
class="bg-background-deep border-border overflow-hidden rounded-b-xl border-t"
class="overflow-hidden rounded-b-xl border-t border-border bg-background-deep"
@update:model-value="open = true"
>
<div class="border-border bg-background flex border-b-2 pr-2">
<div class="flex border-b-2 border-border bg-background pr-2">
<div class="flex w-full items-center justify-between text-[13px]">
<TabsList class="relative flex">
<template v-if="open">
<TabsIndicator
class="absolute bottom-0 left-0 h-[2px] w-[--radix-tabs-indicator-size] translate-x-[--radix-tabs-indicator-position] rounded-full transition-[width,transform] duration-300"
class="absolute bottom-0 left-0 h-[2px] w-[--reka-tabs-indicator-size] translate-x-[--reka-tabs-indicator-position] rounded-full transition-[width,transform] duration-300"
>
<div class="size-full bg-[var(--vp-c-indigo-1)]"></div>
</TabsIndicator>
@@ -64,7 +64,7 @@ const toggleOpen = () => {
v-for="(tab, index) in tabs"
:key="index"
:value="tab.label"
class="border-box text-foreground px-4 py-3 data-[state=active]:text-[var(--vp-c-indigo-1)]"
class="border-box px-4 py-3 text-foreground data-[state=active]:text-[var(--vp-c-indigo-1)]"
tabindex="-1"
>
{{ tab.label }}
@@ -81,7 +81,7 @@ const toggleOpen = () => {
<VbenTooltip side="top">
<template #trigger>
<Code
class="hover:bg-accent size-7 cursor-pointer rounded-full p-1.5"
class="size-7 cursor-pointer rounded-full p-1.5 hover:bg-accent"
@click="toggleOpen"
/>
</template>
@@ -101,7 +101,7 @@ const toggleOpen = () => {
as-child
class="rounded-xl"
>
<div class="text-foreground relative rounded-xl">
<div class="relative rounded-xl text-foreground">
<component :is="tab.component" class="border-0" />
</div>
</TabsContent>

View File

@@ -84,7 +84,7 @@ export const demoPreviewPlugin = (md: MarkdownRenderer) => {
return '';
}
const firstString = 'index.vue';
childFiles = childFiles.sort((a, b) => {
childFiles = childFiles.toSorted((a, b) => {
if (a === firstString) return -1;
if (b === firstString) return 1;
return a.localeCompare(b, 'en', { sensitivity: 'base' });

View File

@@ -22,7 +22,7 @@
"ant-design-vue": "catalog:",
"lucide-vue-next": "catalog:",
"medium-zoom": "catalog:",
"radix-vue": "catalog:",
"reka-ui": "catalog:",
"vitepress-plugin-group-icons": "catalog:"
},
"devDependencies": {

View File

@@ -40,9 +40,10 @@ if (!import.meta.env.SSR) {
// 表格配置项可以用 cellRender: { name: 'CellImage' },
vxeUI.renderer.add('CellImage', {
renderTableDefault(_renderOpts, params) {
renderTableDefault(renderOpts, params) {
const { props } = renderOpts;
const { column, row } = params;
return h(Image, { src: row[column.field] });
return h(Image, { src: row[column.field], ...props });
},
});

View File

@@ -335,6 +335,7 @@ useVbenForm 返回的第二个参数,是一个对象,包含了一些表单
| handleReset | 表单重置回调 | `(values: Record<string, any>,) => Promise<void> \| void` | - |
| handleSubmit | 表单提交回调 | `(values: Record<string, any>,) => Promise<void> \| void` | - |
| handleValuesChange | 表单值变化回调 | `(values: Record<string, any>, fieldsChanged: string[]) => void` | - |
| handleCollapsedChange | 表单收起展开状态变化回调 | `(collapsed: boolean) => void` | - |
| actionButtonsReverse | 调换操作按钮位置 | `boolean` | `false` |
| resetButtonOptions | 重置按钮组件参数 | `ActionButtonOptions` | - |
| submitButtonOptions | 提交按钮组件参数 | `ActionButtonOptions` | - |

View File

@@ -32,7 +32,7 @@ function handleUpdate(len: number) {
<div
v-for="item in list"
:key="item"
class="even:bg-heavy bg-muted flex-center h-[220px] w-full"
class="flex-center h-[220px] w-full bg-muted even:bg-heavy"
>
{{ item }}
</div>

View File

@@ -32,7 +32,7 @@ function handleUpdate(len: number) {
<div
v-for="item in list"
:key="item"
class="even:bg-heavy bg-muted flex-center h-[220px] w-full"
class="flex-center h-[220px] w-full bg-muted even:bg-heavy"
>
{{ item }}
</div>

View File

@@ -92,7 +92,7 @@ const [Grid] = useVbenVxeGrid({ gridOptions });
<Image :src="row.imageUrl" height="30" width="30" />
</template>
<template #open="{ row }">
<Switch v-model:checked="row.open" />
<Switch v-model="row.open" />
</template>
<template #status="{ row }">
<Tag :color="row.color">{{ row.status }}</Tag>

View File

@@ -60,6 +60,8 @@ The execution command is: `pnpm run [script]` or `npm run [script]`.
"build:ele": "pnpm run build --filter=@vben/web-ele",
// Build the web-naive application separately
"build:naive": "pnpm run build --filter=@vben/naive",
// Build the web-tdesign application separately
"build:tdesign": "pnpm run build --filter=@vben/web-tdesign",
// Build the playground application separately
"build:play": "pnpm run build --filter=@vben/playground",
// Changeset version management

View File

@@ -261,6 +261,7 @@ const defaultPreferences: Preferences = {
enable: true,
fit: 'contain',
source: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
// sourceDark: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-dark.webp', // Optional: Dark theme logo
},
navigation: {
accordion: true,
@@ -457,6 +458,8 @@ interface LogoPreferences {
fit: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
/** Logo URL */
source: string;
/** Dark theme logo URL (optional, if not set, use source) */
sourceDark?: string;
}
interface NavigationPreferences {

View File

@@ -56,6 +56,7 @@ After slimming down, you may need to adjust commands according to your project.
"build:docs": "pnpm run build --filter=@vben/docs",
"build:ele": "pnpm run build --filter=@vben/web-ele",
"build:naive": "pnpm run build --filter=@vben/web-naive",
"build:tdesign": "pnpm run build --filter=@vben/web-tdesign",
"build:play": "pnpm run build --filter=@vben/playground",
"dev:antd": "pnpm -F @vben/web-antd run dev",
"dev:docs": "pnpm -F @vben/docs run dev",

View File

@@ -60,6 +60,8 @@ npm 脚本是项目常见的配置,用于执行一些常见的任务,比如
"build:ele": "pnpm run build --filter=@vben/web-ele",
// 单独构建 web-naive 应用
"build:naive": "pnpm run build --filter=@vben/naive",
// 单独构建 web-tdesign 应用
"build:tdesign": "pnpm run build --filter=@vben/web-tdesign",
// 单独构建 playground 应用
"build:play": "pnpm run build --filter=@vben/playground",
// changeset 版本管理

View File

@@ -260,6 +260,7 @@ const defaultPreferences: Preferences = {
enable: true,
fit: 'contain',
source: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
// sourceDark: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-dark.webp', // 可选暗色主题logo
},
navigation: {
accordion: true,
@@ -457,6 +458,8 @@ interface LogoPreferences {
fit: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
/** logo地址 */
source: string;
/** 暗色主题logo地址 (可选,若不设置则使用 source) */
sourceDark?: string;
}
interface NavigationPreferences {

View File

@@ -24,7 +24,7 @@ apps/web-naive
## 演示代码精简
如果你不需要演示代码,你可以直接删除`playground`文件夹。
如果你不需要演示代码,你可以直接删除 `playground` 文件夹。
## 文档精简
@@ -60,6 +60,7 @@ pnpm install
"build:docs": "pnpm run build --filter=@vben/docs",
"build:ele": "pnpm run build --filter=@vben/web-ele",
"build:naive": "pnpm run build --filter=@vben/web-naive",
"build:tdesign": "pnpm run build --filter=@vben/web-tdesign",
"build:play": "pnpm run build --filter=@vben/playground",
"dev:antd": "pnpm -F @vben/web-antd run dev",
"dev:docs": "pnpm -F @vben/docs run dev",
@@ -87,7 +88,7 @@ pnpm install
- 在应用的 `src/router/routes` 文件中,你可以删除不需要的路由。其中 `core` 文件夹内,如果只需要登录和忘记密码,你可以删除其他路由,如忘记密码、注册等。路由删除后,你可以删除对应的页面文件,在 `src/views/_core` 文件夹中。
- 在应用的 `src/router/routes` 文件中,你可以按需求删除不需要的路由,如`demos``vben` 目录等。路由删除后,你可以删除对应的页面文件,`src/views` 文件夹中。
- 在应用的 `src/router/routes` 文件中,你可以按需求删除不需要的路由,如`demos``vben` 目录等。路由删除后,你可以在 `src/views` 文件夹中删除对应的页面文件
### 删除不需要的组件

View File

@@ -21,7 +21,7 @@ const scopeComplete = execSync('git status --porcelain || true')
.trim()
.split('\n')
.find((r) => ~r.indexOf('M src'))
?.replace(/(\/)/g, '%%')
?.replaceAll(/(\/)/g, '%%')
?.match(/src%%((\w|-)*)/)?.[1]
?.replace(/s$/, '');

View File

@@ -1,6 +1,6 @@
{
"name": "@vben/eslint-config",
"version": "5.0.0",
"version": "5.5.9",
"private": true,
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
@@ -43,14 +43,17 @@
"eslint-plugin-n": "catalog:",
"eslint-plugin-no-only-tests": "catalog:",
"eslint-plugin-perfectionist": "catalog:",
"eslint-plugin-pnpm": "catalog:",
"eslint-plugin-prettier": "catalog:",
"eslint-plugin-regexp": "catalog:",
"eslint-plugin-unicorn": "catalog:",
"eslint-plugin-unused-imports": "catalog:",
"eslint-plugin-vitest": "catalog:",
"eslint-plugin-vue": "catalog:",
"eslint-plugin-yml": "catalog:",
"globals": "catalog:",
"jsonc-eslint-parser": "catalog:",
"vue-eslint-parser": "catalog:"
"vue-eslint-parser": "catalog:",
"yaml-eslint-parser": "catalog:"
}
}

View File

@@ -3,7 +3,6 @@ import createCommand from 'eslint-plugin-command/config';
export async function command() {
return [
{
// @ts-expect-error - no types
...createCommand(),
},
];

View File

@@ -46,6 +46,8 @@ export async function ignores(): Promise<Linter.Config[]> {
'**/*.sh',
'**/*.ttf',
'**/*.woff',
'**/.github',
'**/lefthook.yml',
],
},
];

View File

@@ -8,6 +8,7 @@ export * from './jsdoc';
export * from './jsonc';
export * from './node';
export * from './perfectionist';
export * from './pnpm';
export * from './prettier';
export * from './regexp';
export * from './test';
@@ -15,3 +16,4 @@ export * from './turbo';
export * from './typescript';
export * from './unicorn';
export * from './vue';
export * from './yaml';

View File

@@ -48,6 +48,7 @@ export async function jsonc(): Promise<Linter.Config[]> {
},
sortTsconfig(),
sortPackageJson(),
sortCspellJson(),
];
}
@@ -130,6 +131,21 @@ function sortPackageJson(): Linter.Config {
};
}
function sortCspellJson(): Linter.Config {
return {
files: ['**/cspell.json', '**/.cspell.json'],
rules: {
'jsonc/sort-array-values': [
'error',
{
order: { type: 'asc' },
pathPattern: '^words$|^ignorePaths$',
},
],
},
};
}
function sortTsconfig(): Linter.Config {
return {
files: [

View File

@@ -35,7 +35,7 @@ export async function node(): Promise<Linter.Config[]> {
'error',
{
ignores: [],
version: '>=18.0.0',
version: '>=20.12.0',
},
],
'n/prefer-global/buffer': ['error', 'never'],

View File

@@ -4,7 +4,6 @@ import { interopDefault } from '../util';
export async function perfectionist(): Promise<Linter.Config[]> {
const perfectionistPlugin = await interopDefault(
// @ts-expect-error - no types
import('eslint-plugin-perfectionist'),
);

View File

@@ -0,0 +1,41 @@
import type { Linter } from 'eslint';
import { interopDefault } from '../util';
export async function pnpm(): Promise<Linter.Config[]> {
const [pluginPnpm, parserPnpm, parserJsonc] = await Promise.all([
interopDefault(import('eslint-plugin-pnpm')),
interopDefault(import('yaml-eslint-parser')),
interopDefault(import('jsonc-eslint-parser')),
] as const);
return [
{
files: ['package.json', '**/package.json'],
languageOptions: {
parser: parserJsonc,
},
plugins: {
pnpm: pluginPnpm,
},
rules: {
'pnpm/json-enforce-catalog': 'error',
'pnpm/json-prefer-workspace-settings': 'error',
'pnpm/json-valid-catalog': 'error',
},
},
{
files: ['pnpm-workspace.yaml'],
languageOptions: {
parser: parserPnpm,
},
plugins: {
pnpm: pluginPnpm,
},
rules: {
'pnpm/yaml-no-duplicate-catalog-item': 'error',
'pnpm/yaml-no-unused-catalog-item': 'error',
},
},
];
}

View File

@@ -4,7 +4,6 @@ import { interopDefault } from '../util';
export async function turbo(): Promise<Linter.Config[]> {
const [pluginTurbo] = await Promise.all([
// @ts-expect-error - no types
interopDefault(import('eslint-config-turbo')),
] as const);

View File

@@ -5,7 +5,6 @@ import { interopDefault } from '../util';
export async function typescript(): Promise<Linter.Config[]> {
const [pluginTs, parserTs] = await Promise.all([
interopDefault(import('@typescript-eslint/eslint-plugin')),
// @ts-expect-error missing types
interopDefault(import('@typescript-eslint/parser')),
] as const);
@@ -27,11 +26,11 @@ export async function typescript(): Promise<Linter.Config[]> {
},
},
plugins: {
'@typescript-eslint': pluginTs,
'@typescript-eslint': pluginTs as any,
},
rules: {
...pluginTs.configs['eslint-recommended'].overrides?.[0].rules,
...pluginTs.configs.strict.rules,
...pluginTs.configs['eslint-recommended']?.overrides?.[0]?.rules,
...pluginTs.configs.strict?.rules,
'@typescript-eslint/ban-ts-comment': [
'error',
{

View File

@@ -6,7 +6,6 @@ export async function vue(): Promise<Linter.Config[]> {
const [pluginVue, parserVue, parserTs] = await Promise.all([
interopDefault(import('eslint-plugin-vue')),
interopDefault(import('vue-eslint-parser')),
// @ts-expect-error missing types
interopDefault(import('@typescript-eslint/parser')),
] as const);

View File

@@ -0,0 +1,87 @@
import type { Linter } from 'eslint';
import { interopDefault } from '../util';
export async function yaml(): Promise<Linter.Config[]> {
const [pluginYaml, parserYaml] = await Promise.all([
interopDefault(import('eslint-plugin-yml')),
interopDefault(import('yaml-eslint-parser')),
] as const);
return [
{
files: ['**/*.y?(a)ml'],
plugins: {
yaml: pluginYaml as any,
},
languageOptions: {
parser: parserYaml,
},
rules: {
'style/spaced-comment': 'off',
'yaml/block-mapping': 'error',
'yaml/block-sequence': 'error',
'yaml/no-empty-key': 'error',
'yaml/no-empty-sequence-entry': 'error',
'yaml/no-irregular-whitespace': 'error',
'yaml/plain-scalar': 'error',
'yaml/vue-custom-block/no-parsing-error': 'error',
'yaml/block-mapping-question-indicator-newline': 'error',
'yaml/block-sequence-hyphen-indicator-newline': 'error',
'yaml/flow-mapping-curly-newline': 'error',
'yaml/flow-mapping-curly-spacing': 'error',
'yaml/flow-sequence-bracket-newline': 'error',
'yaml/flow-sequence-bracket-spacing': 'error',
'yaml/indent': ['error', 2],
'yaml/key-spacing': 'error',
'yaml/no-tab-indent': 'error',
'yaml/quotes': [
'error',
{
avoidEscape: true,
prefer: 'single',
},
],
'yaml/spaced-comment': 'error',
},
},
{
files: ['pnpm-workspace.yaml'],
rules: {
'yaml/sort-keys': [
'error',
{
order: [
'packages',
'overrides',
'patchedDependencies',
'hoistPattern',
'catalog',
'catalogs',
'allowedDeprecatedVersions',
'allowNonAppliedPatches',
'configDependencies',
'ignoredBuiltDependencies',
'ignoredOptionalDependencies',
'neverBuiltDependencies',
'onlyBuiltDependencies',
'onlyBuiltDependenciesFile',
'packageExtensions',
'peerDependencyRules',
'supportedArchitectures',
],
pathPattern: '^$',
},
{
order: { type: 'asc' },
pathPattern: '.*',
},
],
},
},
];
}

View File

@@ -11,6 +11,7 @@ import {
jsonc,
node,
perfectionist,
pnpm,
prettier,
regexp,
test,
@@ -18,6 +19,7 @@ import {
typescript,
unicorn,
vue,
yaml,
} from './configs';
import { customConfig } from './custom-config';
@@ -48,6 +50,8 @@ async function defineConfig(config: FlatConfig[] = []) {
regexp(),
command(),
turbo(),
yaml(),
pnpm(),
...customConfig,
...config,
];

View File

@@ -1,6 +1,6 @@
{
"name": "@vben/prettier-config",
"version": "5.0.0",
"version": "5.5.9",
"private": true,
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",

View File

@@ -75,6 +75,7 @@ export default {
'import-notation': null,
'media-feature-range-notation': null,
'named-grid-areas-no-invalid': null,
'nesting-selector-no-missing-scoping-root': null,
'no-descending-specificity': null,
'no-empty-source': null,
'order/order': [

View File

@@ -53,6 +53,7 @@
"@tailwindcss/typography": "catalog:",
"autoprefixer": "catalog:",
"cssnano": "catalog:",
"jiti": "catalog:",
"postcss": "catalog:",
"postcss-antd-fixes": "catalog:",
"postcss-import": "catalog:",

View File

@@ -176,18 +176,18 @@ export default {
keyframes: {
'accordion-down': {
from: { height: '0' },
to: { height: 'var(--radix-accordion-content-height)' },
to: { height: 'var(--reka-accordion-content-height)' },
},
'accordion-up': {
from: { height: 'var(--radix-accordion-content-height)' },
from: { height: 'var(--reka-accordion-content-height)' },
to: { height: '0' },
},
'collapsible-down': {
from: { height: '0' },
to: { height: 'var(--radix-collapsible-content-height)' },
to: { height: 'var(--reka-collapsible-content-height)' },
},
'collapsible-up': {
from: { height: 'var(--radix-collapsible-content-height)' },
from: { height: 'var(--reka-collapsible-content-height)' },
to: { height: '0' },
},
float: {

View File

@@ -1,9 +1,6 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/node.json",
"compilerOptions": {
"moduleResolution": "bundler"
},
"include": ["src"],
"exclude": ["node_modules"]
}

View File

@@ -6,6 +6,7 @@
"composite": false,
"lib": ["ESNext"],
"baseUrl": "./",
"moduleResolution": "bundler",
"types": ["node"],
"noImplicitAny": true
}

View File

@@ -63,6 +63,7 @@ function defineApplicationConfig(userConfigPromise?: DefineApplicationOptions) {
assetFileNames: '[ext]/[name]-[hash].[ext]',
chunkFileNames: 'js/[name]-[hash].js',
entryFileNames: 'jse/index-[name]-[hash].js',
experimentalMinChunkSize: 20 * 1024,
},
},
target: 'es2015',
@@ -114,7 +115,7 @@ function createCssOptions(injectGlobalScss = true): CSSOptions {
}
return content;
},
api: 'modern',
// api: 'modern',
importers: [new NodePackageImporter()],
},
}

View File

@@ -1,4 +1,4 @@
import type { DefineConfig } from '../typing';
import type { DefineConfig, VbenViteConfig } from '../typing';
import { existsSync } from 'node:fs';
import { join } from 'node:path';
@@ -12,7 +12,7 @@ export * from './library';
function defineConfig(
userConfigPromise?: DefineConfig,
type: 'application' | 'auto' | 'library' = 'auto',
) {
): VbenViteConfig {
let projectType = type;
// 根据包是否存在 index.html,自动判断类型

View File

@@ -1,5 +1,10 @@
import type { PluginVisualizerOptions } from 'rollup-plugin-visualizer';
import type { ConfigEnv, PluginOption, UserConfig } from 'vite';
import type {
ConfigEnv,
PluginOption,
UserConfig,
UserConfigFnPromise,
} from 'vite';
import type { PluginOptions } from 'vite-plugin-dts';
import type { Options as PwaPluginOptions } from 'vite-plugin-pwa';
@@ -327,6 +332,8 @@ type DefineLibraryOptions = (config?: ConfigEnv) => Promise<{
*/
type DefineConfig = DefineApplicationOptions | DefineLibraryOptions;
type VbenViteConfig = Promise<UserConfig> | UserConfig | UserConfigFnPromise;
export type {
ApplicationPluginOptions,
ArchiverPluginOptions,
@@ -340,4 +347,5 @@ export type {
LibraryPluginOptions,
NitroMockPluginOptions,
PrintPluginOptions,
VbenViteConfig,
};

View File

@@ -92,27 +92,8 @@
"vue-tsc": "catalog:"
},
"engines": {
"node": ">=20.10.0",
"pnpm": ">=9.12.0"
"node": ">=20.19.0",
"pnpm": ">=10.0.0"
},
"packageManager": "pnpm@10.14.0",
"pnpm": {
"peerDependencyRules": {
"allowedVersions": {
"eslint": "*"
}
},
"overrides": {
"@ast-grep/napi": "catalog:",
"@ctrl/tinycolor": "catalog:",
"clsx": "catalog:",
"esbuild": "0.25.3",
"pinia": "catalog:",
"vue": "catalog:"
},
"neverBuiltDependencies": [
"canvas",
"node-gyp"
]
}
"packageManager": "pnpm@10.28.2"
}

View File

@@ -14,12 +14,13 @@
}
html {
@apply text-foreground bg-background font-sans text-[100%];
@apply text-foreground bg-background font-sans;
font-size: var(--font-size-base, 16px);
font-variation-settings: normal;
font-synthesis-weight: none;
line-height: 1.15;
text-size-adjust: 100%;
font-synthesis-weight: none;
scroll-behavior: smooth;
text-rendering: optimizelegibility;
-webkit-tap-highlight-color: transparent;

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