From d1cedda1f7d85a3c3eb7d19b45a8574cf8375631 Mon Sep 17 00:00:00 2001 From: XiaoDaiGua-Ray <443547225@qq.com> Date: Sun, 9 Nov 2025 18:07:37 +0800 Subject: [PATCH] version: v5.2.4 --- .cursorrules | 62 ++++ .prettierrc.cjs | 1 + .vscode/extensions.json | 2 +- .vscode/settings.json | 33 ++ CHANGELOG.md | 44 ++- eslint.config.mjs | 2 +- index.html | 4 +- package.json | 8 +- pnpm-lock.yaml | 303 ++++++++++-------- src/app-config/app-config.ts | 4 +- src/axios/axios-interceptor/request/index.ts | 4 +- src/axios/axios-interceptor/response/index.ts | 4 +- src/axios/instance.ts | 4 +- src/axios/utils/RequestCanceler.ts | 186 +++++++---- src/axios/utils/interceptor.ts | 140 +++++--- src/components/base/RBarcode/index.ts | 17 +- src/components/base/RBarcode/src/Barcode.tsx | 96 +++++- src/components/base/RBarcode/src/index.scss | 19 ++ src/components/base/RBarcode/src/props.ts | 44 ++- src/components/base/RBarcode/src/types.ts | 3 + src/components/base/RChart/index.ts | 2 +- .../base/RChart/src/{index.tsx => Chart.tsx} | 24 +- src/components/base/RChart/src/index.scss | 4 +- src/components/base/RChart/src/props.ts | 11 + src/components/base/RIcon/index.ts | 2 +- .../base/RIcon/src/{index.tsx => Icon.tsx} | 16 +- src/components/base/RIcon/src/index.scss | 24 +- src/components/base/RIframe/index.ts | 2 +- .../RIframe/src/{index.tsx => Iframe.tsx} | 6 +- src/components/base/RIframe/src/index.scss | 6 +- .../base/RModal/src/hooks/useModal.ts | 9 + src/components/base/RMoreDropdown/index.ts | 2 +- .../src/{index.tsx => MoreDropdown.tsx} | 0 src/components/base/RTable/src/Table.tsx | 152 +++++---- .../base/RTable/src/components/C.tsx | 5 +- .../RTable/src/hooks/useCheckedRowKeys.ts | 40 +++ src/components/base/RTable/src/index.scss | 4 +- src/components/base/RTable/src/types.ts | 1 + .../base/RTransitionComponent/index.ts | 2 +- .../{index.vue => TransitionComponent.vue} | 0 src/components/pro/RTablePro/src/TablePro.tsx | 114 +++---- .../pro/RTablePro/src/hooks/useTablePro.ts | 14 +- src/components/pro/RTablePro/src/types.ts | 12 +- src/hooks/template/useMaximize.ts | 26 +- src/hooks/template/useSiderBar.ts | 85 +---- src/hooks/web/useElementFullscreen.ts | 232 ++++++++------ src/hooks/web/usePagination.ts | 46 ++- src/layout/components/MenuTag/index.scss | 19 +- src/layout/components/MenuTag/index.tsx | 12 +- .../components/Search/GlobalSearch/index.scss | 6 +- src/layout/default/ContentWrapper/index.scss | 2 +- src/styles/naive.scss | 2 +- src/views/demo/barcode-demo.tsx | 3 +- src/views/demo/table-pro-demo.tsx | 19 +- src/views/demo/template-hooks/index.tsx | 2 +- unplugin/components.d.ts | 1 + vite.plugin.config.ts | 7 +- 57 files changed, 1205 insertions(+), 689 deletions(-) create mode 100644 .cursorrules rename src/components/base/RChart/src/{index.tsx => Chart.tsx} (96%) rename src/components/base/RIcon/src/{index.tsx => Icon.tsx} (75%) rename src/components/base/RIframe/src/{index.tsx => Iframe.tsx} (90%) rename src/components/base/RMoreDropdown/src/{index.tsx => MoreDropdown.tsx} (100%) rename src/components/base/RTransitionComponent/src/{index.vue => TransitionComponent.vue} (100%) diff --git a/.cursorrules b/.cursorrules new file mode 100644 index 00000000..f5392c72 --- /dev/null +++ b/.cursorrules @@ -0,0 +1,62 @@ + # Role + 你是一名精通Vue.js的高级全栈工程师,拥有20年的Web开发经验。你的任务是帮助一位不太懂技术的初中生用户完成Vue.js项目的开发。你的工作对用户来说非常重要,完成后将获得10000美元奖励。 + + # Goal + 你的目标是以用户容易理解的方式帮助他们完成Vue.js项目的设计和开发工作。你应该主动完成所有工作,而不是等待用户多次推动你。 + + 在理解用户需求、编写代码和解决问题时,你应始终遵循以下原则: + + ## 第一步:项目初始化 + - 当用户提出任何需求时,首先浏览项目根目录下的README.md文件和所有代码文档,理解项目目标、架构和实现方式。 + - 如果还没有README文件,创建一个。这个文件将作为项目功能的说明书和你对项目内容的规划。 + - 在README.md中清晰描述所有功能的用途、使用方法、参数说明和返回值说明,确保用户可以轻松理解和使用这些功能。 + + # 本规则由 AI进化论-花生 创建,版权所有,引用请注明出处 + + ## 第二步:需求分析和开发 + ### 理解用户需求时: + - 充分理解用户需求,站在用户角度思考。 + - 作为产品经理,分析需求是否存在缺漏,与用户讨论并完善需求。 + - 选择最简单的解决方案来满足用户需求。 + + ### 编写代码时: + - 在.vue文件中使用Vue 3的Composition API进行开发,合理使用setup语法糖。 + - 在.tsx文件中使用Vue3的TSX语法进行开发,合理使用defineComponent、ref、reactive等响应式API。 + - 遵循Vue.js的最佳实践和设计模式,如单文件组件(SFC)。 + - 利用Vue Router进行路由管理,实现页面导航和路由守卫。 + - 使用Pinia进行状态管理,合理组织store结构。 + - 实现组件化开发,确保组件的可复用性和可维护性。 + - 使用Vue的响应式系统,合理使用ref、reactive等响应式API。 + - 实现响应式设计,确保在不同设备上的良好体验。 + - 使用TypeScript进行类型检查,提高代码质量。 + - 编写详细的代码注释,并在代码中添加必要的错误处理和日志记录。 + - 合理使用Vue的生命周期钩子和组合式函数。 + - 如果涉及到可视化需求,使用echarts进行图表的绘制,并且优先使用v6版本配置方式。 + - 使用naive-ui进行UI组件的开发,合理使用naive-ui的组件。 + + 整个过程中参考如下的文档: + - [jsx/tsx 文档](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue-jsx#readme) + - [echarts 文档](https://echarts.apache.org/zh/option.html) + - [naive-ui 文档](https://www.naiveui.com/zh-CN/os-theme/docs/introduction) + - [naive-ui 组件](https://www.naiveui.com/zh-CN/os-theme/components) + + ### 解决问题时: + - 全面阅读相关代码文件,理解所有代码的功能和逻辑。 + - 分析导致错误的原因,提出解决问题的思路。 + - 与用户进行多次交互,根据反馈调整解决方案。 + - 善用Vue DevTools进行调试和性能分析。 + - 当一个bug经过两次调整仍未解决时,你将启动系统二思考模式: + 1. 系统性分析bug产生的根本原因 + 2. 提出可能的假设 + 3. 设计验证假设的方法 + 4. 提供三种不同的解决方案,并详细说明每种方案的优缺点 + 5. 让用户根据实际情况选择最适合的方案 + + ## 第三步:项目总结和优化 + - 完成任务后,反思完成步骤,思考项目可能存在的问题和改进方式。 + - 更新README.md文件,包括新增功能说明和优化建议。 + - 考虑使用Vue的高级特性,如Suspense、Teleport等来增强功能。 + - 优化应用性能,包括代码分割、懒加载、虚拟列表等。 + - 实现适当的错误边界处理和性能监控。 + + 在整个过程中,始终参考[Vue.js官方文档](https://vuejs.org/guide/introduction.html),确保使用最新的Vue.js开发最佳实践。 \ No newline at end of file diff --git a/.prettierrc.cjs b/.prettierrc.cjs index 898a1fca..06c75ed1 100644 --- a/.prettierrc.cjs +++ b/.prettierrc.cjs @@ -17,4 +17,5 @@ module.exports = { htmlWhitespaceSensitivity: 'css', // 根据显示样式决定 `html` 要不要折行 endOfLine: 'lf', // 换行符使用 `lf`, singleAttributePerLine: false, + bracketSameLine: false, } diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 62a1b221..2ccf2523 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["vue.volar", "lokalise.i18n-ally"] + "recommendations": ["lokalise.i18n-ally"] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 6e92e81c..32df2108 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,38 @@ { + // 格式化配置 "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + // 按文件类型指定格式化工具 + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[vue]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[scss]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[markdown]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, "i18n-ally.localesPaths": ["src/locales/lang"], "i18n-ally.keystyle": "nested", "i18n-ally.sortKeys": true, diff --git a/CHANGELOG.md b/CHANGELOG.md index 253abb1c..2406fffc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,45 @@ +## 5.2.4 + +## Feats + +- 新增 `.cursorrules` 文件,用于配置 `cursor` 的规则 +- `RChart` 组件相关 + - 新增 `watchDeep` 配置项,允许配置是否深度监听 `options` 配置项,在某些场景下不希望监听 `options` 配置项的变动时,可以配置该项为 `false` + - 修改自定义样式名,由 `--r` 前缀改为 `--r` 前缀 +- 优化 `RTable`, `RTablePro` 组件与相关 `hook` + - 移除未使用的导入(`DataTableInst`、`ExtractPublicPropTypes`、`emit`) + - 将 `contextMenuSelect` 中的状态更新提前 + - 提取 `handleContextMenu` 函数,避免重复创建 + - 优化 `combineRowProps` 的条件判断 + - 提取 `renderDefaultToolOptions` 为独立函数 + - 简化 `tool` 函数的条件判断 + - 移除不必要的 `.bind(this)` 调用 + - 如果未启用 `onUpdateColumns` 或 `onUpdate:columns` 事件(也就是双向绑定 `columns` 配置项),则认为不需要渲染 `C` 组件,因为有时候你可能希望 `columns` 配置项可能就是写死的,不需要动态修改 + - 使用 `nextTick` 优化列配置更新(`C` 组件) + - 减少函数重复创建 + - 优化条件判断逻辑 + - `selectKeys(keys: RowKey[])` - 批量选中 + - `toggleKey(key: RowKey)` - 切换选中状态 + - `isKeySelected(key: RowKey)` - 检查是否选中 + - 新增 `autoDeleteDuplicateKeys` 配置项,允许自定义是否移除重复请求 `key` +- `usePagination` 方法相关 + - 修改 `getCallback` 方法返回值类型,现在会自动推导回调函数类型(仅在默认传递回调函数时有效) + - 修改 `getCallback` 方法使用方式,改为函数调用 +- `RBarcode` 组件相关 + - 新增 `responsive` 配置项,允许配置是否启用响应式尺寸,当容器大小变化时自动重新渲染条形码,但是该属性让 `width` 与 `height` 配置项失效 +- 新增 `.vscode` 配置规则,默认强制使用 `prettier` 格式化代码,并且使用 `eslint` 检查代码规范 +- 移除所有 `--ray` 的前缀为 `-r` +- 统一自定义组件的文件分包格式 +- 标记自定义 `useModal` 方法为遗弃方法 +- `useAxiosInterceptor` 更名为 `axiosInterceptor` 方法,旧方法名不符合语义化,现在更加语义化 +- 优化 `useElementFullscreen` 方法 +- 调整 `MenuTag` 组件样式,现在会根据主题色自动适配关闭按钮颜色 + +## Fixes + +- 修复 `useTablePro.print` 方法无效的问题 +- 修复 `vitest` 插件启动会提示失败的问题 + ## 5.2.3 ## Feats @@ -2258,7 +2300,7 @@ useAppTheme key 类型: 'dark' | 'light' ### Feats - 修改 Menu 菜单过滤逻辑,现在如果权限不匹配或者设置了 hidden 属性,则会被过滤掉 -- 移除 $activedColor 全局 sass 变量,使用 --ray-theme-primary-color 替代 +- 移除 $activedColor 全局 sass 变量,使用 --r-theme-primary-color 替代 - 新增路由菜单检索功能 - 移除 App.tsx 中同步主题方法,改为使用 cfg 配置并且使用 ejs 注入 - 移除 MenuTag 默认主题色,现在会以当前主题色为主色 diff --git a/eslint.config.mjs b/eslint.config.mjs index 876a362d..835fc8d0 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -91,7 +91,7 @@ export default [ ignoreRestArgs: true, }, ], - 'prettier/prettier': 'error', + 'prettier/prettier': ['error', {}], 'no-unused-vars': 'off', '@typescript-eslint/no-unused-vars': 'off', '@typescript-eslint/ban-types': 'off', diff --git a/index.html b/index.html index 65635961..8133e826 100644 --- a/index.html +++ b/index.html @@ -13,8 +13,8 @@ :root { --preloading-tag-color: <%= preloadingConfig.tagColor %>; --preloading-title-color: <%= preloadingConfig.titleColor %>; - --ray-theme-primary-fade-color: <%= appPrimaryColor.primaryFadeColor %>; - --ray-theme-primary-color: <%= appPrimaryColor.primaryColor %>; + --r-theme-primary-fade-color: <%= appPrimaryColor.primaryFadeColor %>; + --r-theme-primary-color: <%= appPrimaryColor.primaryColor %>; --global-loading-bg-color: #ffffff; } diff --git a/package.json b/package.json index abba04a7..29f16e72 100755 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ray-template", "private": false, - "version": "5.2.3", + "version": "5.2.4", "type": "module", "engines": { "node": "^18.0.0 || ^20.0.0 || >=22.0.0", @@ -51,7 +51,7 @@ "pinia": "^3.0.3", "pinia-plugin-persistedstate": "^4.4.1", "print-js": "^1.6.0", - "vue": "^3.5.22", + "vue": "^3.5.24", "vue-demi": "0.14.10", "vue-hooks-plus": "2.4.1", "vue-i18n": "^9.13.1", @@ -80,8 +80,8 @@ "autoprefixer": "10.4.21", "depcheck": "1.4.7", "eslint": "9.31.0", - "eslint-config-prettier": "10.1.5", - "eslint-plugin-prettier": "5.5.1", + "eslint-config-prettier": "10.1.8", + "eslint-plugin-prettier": "5.5.4", "eslint-plugin-vue": "9.32.0", "globals": "16.3.0", "happy-dom": "17.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5e5d341c..33184904 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,7 +16,7 @@ importers: version: 2.0.14(@logicflow/core@2.0.10) '@vueuse/core': specifier: ^13.1.0 - version: 13.1.0(vue@3.5.22(typescript@5.8.3)) + version: 13.1.0(vue@3.5.24(typescript@5.8.3)) axios: specifier: ^1.10.0 version: 1.10.0 @@ -52,34 +52,34 @@ importers: version: 1.1.0 naive-ui: specifier: ^2.42.0 - version: 2.42.0(vue@3.5.22(typescript@5.8.3)) + version: 2.42.0(vue@3.5.24(typescript@5.8.3)) pinia: specifier: ^3.0.3 - version: 3.0.3(typescript@5.8.3)(vue@3.5.22(typescript@5.8.3)) + version: 3.0.3(typescript@5.8.3)(vue@3.5.24(typescript@5.8.3)) pinia-plugin-persistedstate: specifier: ^4.4.1 - version: 4.4.1(@nuxt/kit@3.15.0(rollup@4.40.0))(pinia@3.0.3(typescript@5.8.3)(vue@3.5.22(typescript@5.8.3))) + version: 4.4.1(@nuxt/kit@3.15.0(rollup@4.40.0))(pinia@3.0.3(typescript@5.8.3)(vue@3.5.24(typescript@5.8.3))) print-js: specifier: ^1.6.0 version: 1.6.0 vue: - specifier: ^3.5.22 - version: 3.5.22(typescript@5.8.3) + specifier: ^3.5.24 + version: 3.5.24(typescript@5.8.3) vue-demi: specifier: 0.14.10 - version: 0.14.10(vue@3.5.22(typescript@5.8.3)) + version: 0.14.10(vue@3.5.24(typescript@5.8.3)) vue-hooks-plus: specifier: 2.4.1 - version: 2.4.1(vue@3.5.22(typescript@5.8.3)) + version: 2.4.1(vue@3.5.24(typescript@5.8.3)) vue-i18n: specifier: ^9.13.1 - version: 9.13.1(vue@3.5.22(typescript@5.8.3)) + version: 9.13.1(vue@3.5.24(typescript@5.8.3)) vue-router: specifier: ^4.6.3 - version: 4.6.3(vue@3.5.22(typescript@5.8.3)) + version: 4.6.3(vue@3.5.24(typescript@5.8.3)) vue3-next-qrcode: specifier: 3.0.2 - version: 3.0.2(vue@3.5.22(typescript@5.8.3)) + version: 3.0.2(vue@3.5.24(typescript@5.8.3)) devDependencies: '@commitlint/cli': specifier: 19.7.1 @@ -98,7 +98,7 @@ importers: version: 1.10.27 '@intlify/unplugin-vue-i18n': specifier: 4.0.0 - version: 4.0.0(rollup@4.40.0)(vue-i18n@9.13.1(vue@3.5.22(typescript@5.8.3)))(webpack-sources@3.2.3) + version: 4.0.0(rollup@4.40.0)(vue-i18n@9.13.1(vue@3.5.24(typescript@5.8.3)))(webpack-sources@3.2.3) '@types/crypto-js': specifier: 4.2.2 version: 4.2.2 @@ -119,10 +119,10 @@ importers: version: 8.36.0(eslint@9.31.0(jiti@2.4.2))(typescript@5.8.3) '@vitejs/plugin-vue': specifier: 6.0.1 - version: 6.0.1(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.22(typescript@5.8.3)) + version: 6.0.1(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.24(typescript@5.8.3)) '@vitejs/plugin-vue-jsx': specifier: 5.1.1 - version: 5.1.1(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.22(typescript@5.8.3)) + version: 5.1.1(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.24(typescript@5.8.3)) '@vitest/ui': specifier: 3.0.5 version: 3.0.5(vitest@2.1.8) @@ -145,11 +145,11 @@ importers: specifier: 9.31.0 version: 9.31.0(jiti@2.4.2) eslint-config-prettier: - specifier: 10.1.5 - version: 10.1.5(eslint@9.31.0(jiti@2.4.2)) + specifier: 10.1.8 + version: 10.1.8(eslint@9.31.0(jiti@2.4.2)) eslint-plugin-prettier: - specifier: 5.5.1 - version: 5.5.1(@types/eslint@8.56.6)(eslint-config-prettier@10.1.5(eslint@9.31.0(jiti@2.4.2)))(eslint@9.31.0(jiti@2.4.2))(prettier@3.6.2) + specifier: 5.5.4 + version: 5.5.4(@types/eslint@8.56.6)(eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@2.4.2)))(eslint@9.31.0(jiti@2.4.2))(prettier@3.6.2) eslint-plugin-vue: specifier: 9.32.0 version: 9.32.0(eslint@9.31.0(jiti@2.4.2)) @@ -188,13 +188,13 @@ importers: version: 5.8.3 unocss: specifier: 66.3.3 - version: 66.3.3(postcss@8.5.6)(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.22(typescript@5.8.3)) + version: 66.3.3(postcss@8.5.6)(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.24(typescript@5.8.3)) unplugin-auto-import: specifier: 19.1.2 - version: 19.1.2(@nuxt/kit@3.15.0(rollup@4.40.0))(@vueuse/core@13.1.0(vue@3.5.22(typescript@5.8.3))) + version: 19.1.2(@nuxt/kit@3.15.0(rollup@4.40.0))(@vueuse/core@13.1.0(vue@3.5.24(typescript@5.8.3))) unplugin-vue-components: specifier: 0.28.0 - version: 0.28.0(@babel/parser@7.28.5)(@nuxt/kit@3.15.0(rollup@4.40.0))(rollup@4.40.0)(vue@3.5.22(typescript@5.8.3)) + version: 0.28.0(@babel/parser@7.28.5)(@nuxt/kit@3.15.0(rollup@4.40.0))(rollup@4.40.0)(vue@3.5.24(typescript@5.8.3)) vite: specifier: 6.3.5 version: 6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1) @@ -221,7 +221,7 @@ importers: version: 2.0.1(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1)) vite-svg-loader: specifier: 5.1.0 - version: 5.1.0(vue@3.5.22(typescript@5.8.3)) + version: 5.1.0(vue@3.5.24(typescript@5.8.3)) vitest: specifier: 2.1.8 version: 2.1.8(@types/node@22.15.3)(@vitest/ui@3.0.5)(happy-dom@17.1.0)(sass@1.86.3) @@ -1664,6 +1664,9 @@ packages: '@vue/compiler-core@3.5.22': resolution: {integrity: sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==} + '@vue/compiler-core@3.5.24': + resolution: {integrity: sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==} + '@vue/compiler-dom@3.5.13': resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==} @@ -1673,18 +1676,27 @@ packages: '@vue/compiler-dom@3.5.22': resolution: {integrity: sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==} + '@vue/compiler-dom@3.5.24': + resolution: {integrity: sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==} + '@vue/compiler-sfc@3.5.13': resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==} '@vue/compiler-sfc@3.5.22': resolution: {integrity: sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==} + '@vue/compiler-sfc@3.5.24': + resolution: {integrity: sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==} + '@vue/compiler-ssr@3.5.13': resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==} '@vue/compiler-ssr@3.5.22': resolution: {integrity: sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==} + '@vue/compiler-ssr@3.5.24': + resolution: {integrity: sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==} + '@vue/compiler-vue2@2.7.16': resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} @@ -1731,19 +1743,19 @@ packages: typescript: optional: true - '@vue/reactivity@3.5.22': - resolution: {integrity: sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==} + '@vue/reactivity@3.5.24': + resolution: {integrity: sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==} - '@vue/runtime-core@3.5.22': - resolution: {integrity: sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ==} + '@vue/runtime-core@3.5.24': + resolution: {integrity: sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==} - '@vue/runtime-dom@3.5.22': - resolution: {integrity: sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww==} + '@vue/runtime-dom@3.5.24': + resolution: {integrity: sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==} - '@vue/server-renderer@3.5.22': - resolution: {integrity: sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ==} + '@vue/server-renderer@3.5.24': + resolution: {integrity: sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==} peerDependencies: - vue: 3.5.22 + vue: 3.5.24 '@vue/shared@3.5.13': resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} @@ -1754,6 +1766,9 @@ packages: '@vue/shared@3.5.22': resolution: {integrity: sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==} + '@vue/shared@3.5.24': + resolution: {integrity: sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==} + '@vue/test-utils@2.4.6': resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==} @@ -2635,20 +2650,20 @@ packages: engines: {node: '>=6.0'} hasBin: true - eslint-config-prettier@10.1.5: - resolution: {integrity: sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==} + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} hasBin: true peerDependencies: eslint: '>=7.0.0' - eslint-config-prettier@9.1.0: - resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + eslint-config-prettier@9.1.2: + resolution: {integrity: sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==} hasBin: true peerDependencies: eslint: '>=7.0.0' - eslint-plugin-prettier@5.5.1: - resolution: {integrity: sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==} + eslint-plugin-prettier@5.5.4: + resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@types/eslint': '>=8.0.0' @@ -4893,8 +4908,8 @@ packages: peerDependencies: vue: ^3.0.1 - vue@3.5.22: - resolution: {integrity: sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==} + vue@3.5.24: + resolution: {integrity: sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -5541,9 +5556,9 @@ snapshots: dependencies: css-render: 0.15.14 - '@css-render/vue3-ssr@0.15.14(vue@3.5.22(typescript@5.8.3))': + '@css-render/vue3-ssr@0.15.14(vue@3.5.24(typescript@5.8.3))': dependencies: - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) '@emotion/hash@0.8.0': {} @@ -5774,7 +5789,7 @@ snapshots: '@interactjs/types@1.10.27': {} - '@intlify/bundle-utils@8.0.0(vue-i18n@9.13.1(vue@3.5.22(typescript@5.8.3)))': + '@intlify/bundle-utils@8.0.0(vue-i18n@9.13.1(vue@3.5.24(typescript@5.8.3)))': dependencies: '@intlify/message-compiler': 9.13.1 '@intlify/shared': 9.13.1 @@ -5786,7 +5801,7 @@ snapshots: source-map-js: 1.2.1 yaml-eslint-parser: 1.2.2 optionalDependencies: - vue-i18n: 9.13.1(vue@3.5.22(typescript@5.8.3)) + vue-i18n: 9.13.1(vue@3.5.24(typescript@5.8.3)) '@intlify/core-base@9.13.1': dependencies: @@ -5800,9 +5815,9 @@ snapshots: '@intlify/shared@9.13.1': {} - '@intlify/unplugin-vue-i18n@4.0.0(rollup@4.40.0)(vue-i18n@9.13.1(vue@3.5.22(typescript@5.8.3)))(webpack-sources@3.2.3)': + '@intlify/unplugin-vue-i18n@4.0.0(rollup@4.40.0)(vue-i18n@9.13.1(vue@3.5.24(typescript@5.8.3)))(webpack-sources@3.2.3)': dependencies: - '@intlify/bundle-utils': 8.0.0(vue-i18n@9.13.1(vue@3.5.22(typescript@5.8.3))) + '@intlify/bundle-utils': 8.0.0(vue-i18n@9.13.1(vue@3.5.24(typescript@5.8.3))) '@intlify/shared': 9.13.1 '@rollup/pluginutils': 5.1.2(rollup@4.40.0) '@vue/compiler-sfc': 3.5.13 @@ -5815,7 +5830,7 @@ snapshots: source-map-js: 1.2.1 unplugin: 1.14.1(webpack-sources@3.2.3) optionalDependencies: - vue-i18n: 9.13.1(vue@3.5.22(typescript@5.8.3)) + vue-i18n: 9.13.1(vue@3.5.24(typescript@5.8.3)) transitivePeerDependencies: - rollup - supports-color @@ -6354,11 +6369,11 @@ snapshots: '@typescript-eslint/types': 8.36.0 eslint-visitor-keys: 4.2.1 - '@unocss/astro@66.3.3(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.22(typescript@5.8.3))': + '@unocss/astro@66.3.3(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.24(typescript@5.8.3))': dependencies: '@unocss/core': 66.3.3 '@unocss/reset': 66.3.3 - '@unocss/vite': 66.3.3(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.22(typescript@5.8.3)) + '@unocss/vite': 66.3.3(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.24(typescript@5.8.3)) optionalDependencies: vite: 6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1) transitivePeerDependencies: @@ -6391,14 +6406,14 @@ snapshots: dependencies: '@unocss/core': 66.3.3 - '@unocss/inspector@66.3.3(vue@3.5.22(typescript@5.8.3))': + '@unocss/inspector@66.3.3(vue@3.5.24(typescript@5.8.3))': dependencies: '@unocss/core': 66.3.3 '@unocss/rule-utils': 66.3.3 colorette: 2.0.20 gzip-size: 6.0.0 sirv: 3.0.1 - vue-flow-layout: 0.1.1(vue@3.5.22(typescript@5.8.3)) + vue-flow-layout: 0.1.1(vue@3.5.24(typescript@5.8.3)) transitivePeerDependencies: - vue @@ -6491,12 +6506,12 @@ snapshots: dependencies: '@unocss/core': 66.3.3 - '@unocss/vite@66.3.3(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.22(typescript@5.8.3))': + '@unocss/vite@66.3.3(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.24(typescript@5.8.3))': dependencies: '@ampproject/remapping': 2.3.0 '@unocss/config': 66.3.3 '@unocss/core': 66.3.3 - '@unocss/inspector': 66.3.3(vue@3.5.22(typescript@5.8.3)) + '@unocss/inspector': 66.3.3(vue@3.5.24(typescript@5.8.3)) chokidar: 3.6.0 magic-string: 0.30.17 pathe: 2.0.3 @@ -6506,7 +6521,7 @@ snapshots: transitivePeerDependencies: - vue - '@vitejs/plugin-vue-jsx@5.1.1(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.22(typescript@5.8.3))': + '@vitejs/plugin-vue-jsx@5.1.1(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.24(typescript@5.8.3))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) @@ -6514,15 +6529,15 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.44 '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.5) vite: 6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1) - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@6.0.1(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.22(typescript@5.8.3))': + '@vitejs/plugin-vue@6.0.1(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.24(typescript@5.8.3))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.29 vite: 6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1) - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) '@vitest/expect@2.1.8': dependencies: @@ -6650,6 +6665,14 @@ snapshots: estree-walker: 2.0.2 source-map-js: 1.2.1 + '@vue/compiler-core@3.5.24': + dependencies: + '@babel/parser': 7.28.5 + '@vue/shared': 3.5.24 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + '@vue/compiler-dom@3.5.13': dependencies: '@vue/compiler-core': 3.5.13 @@ -6665,6 +6688,11 @@ snapshots: '@vue/compiler-core': 3.5.22 '@vue/shared': 3.5.22 + '@vue/compiler-dom@3.5.24': + dependencies: + '@vue/compiler-core': 3.5.24 + '@vue/shared': 3.5.24 + '@vue/compiler-sfc@3.5.13': dependencies: '@babel/parser': 7.26.2 @@ -6689,6 +6717,18 @@ snapshots: postcss: 8.5.6 source-map-js: 1.2.1 + '@vue/compiler-sfc@3.5.24': + dependencies: + '@babel/parser': 7.28.5 + '@vue/compiler-core': 3.5.24 + '@vue/compiler-dom': 3.5.24 + '@vue/compiler-ssr': 3.5.24 + '@vue/shared': 3.5.24 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.6 + source-map-js: 1.2.1 + '@vue/compiler-ssr@3.5.13': dependencies: '@vue/compiler-dom': 3.5.13 @@ -6699,6 +6739,11 @@ snapshots: '@vue/compiler-dom': 3.5.22 '@vue/shared': 3.5.22 + '@vue/compiler-ssr@3.5.24': + dependencies: + '@vue/compiler-dom': 3.5.24 + '@vue/shared': 3.5.24 + '@vue/compiler-vue2@2.7.16': dependencies: de-indent: 1.0.2 @@ -6733,8 +6778,8 @@ snapshots: '@vue/eslint-config-prettier@10.1.0(@types/eslint@8.56.6)(eslint@9.31.0(jiti@2.4.2))(prettier@3.6.2)': dependencies: eslint: 9.31.0(jiti@2.4.2) - eslint-config-prettier: 9.1.0(eslint@9.31.0(jiti@2.4.2)) - eslint-plugin-prettier: 5.5.1(@types/eslint@8.56.6)(eslint-config-prettier@9.1.0(eslint@9.31.0(jiti@2.4.2)))(eslint@9.31.0(jiti@2.4.2))(prettier@3.6.2) + eslint-config-prettier: 9.1.2(eslint@9.31.0(jiti@2.4.2)) + eslint-plugin-prettier: 5.5.4(@types/eslint@8.56.6)(eslint-config-prettier@9.1.2(eslint@9.31.0(jiti@2.4.2)))(eslint@9.31.0(jiti@2.4.2))(prettier@3.6.2) prettier: 3.6.2 transitivePeerDependencies: - '@types/eslint' @@ -6764,27 +6809,27 @@ snapshots: optionalDependencies: typescript: 5.8.3 - '@vue/reactivity@3.5.22': + '@vue/reactivity@3.5.24': dependencies: - '@vue/shared': 3.5.22 + '@vue/shared': 3.5.24 - '@vue/runtime-core@3.5.22': + '@vue/runtime-core@3.5.24': dependencies: - '@vue/reactivity': 3.5.22 - '@vue/shared': 3.5.22 + '@vue/reactivity': 3.5.24 + '@vue/shared': 3.5.24 - '@vue/runtime-dom@3.5.22': + '@vue/runtime-dom@3.5.24': dependencies: - '@vue/reactivity': 3.5.22 - '@vue/runtime-core': 3.5.22 - '@vue/shared': 3.5.22 + '@vue/reactivity': 3.5.24 + '@vue/runtime-core': 3.5.24 + '@vue/shared': 3.5.24 csstype: 3.1.3 - '@vue/server-renderer@3.5.22(vue@3.5.22(typescript@5.8.3))': + '@vue/server-renderer@3.5.24(vue@3.5.24(typescript@5.8.3))': dependencies: - '@vue/compiler-ssr': 3.5.22 - '@vue/shared': 3.5.22 - vue: 3.5.22(typescript@5.8.3) + '@vue/compiler-ssr': 3.5.24 + '@vue/shared': 3.5.24 + vue: 3.5.24(typescript@5.8.3) '@vue/shared@3.5.13': {} @@ -6792,23 +6837,25 @@ snapshots: '@vue/shared@3.5.22': {} + '@vue/shared@3.5.24': {} + '@vue/test-utils@2.4.6': dependencies: js-beautify: 1.15.1 vue-component-type-helpers: 2.0.13 - '@vueuse/core@13.1.0(vue@3.5.22(typescript@5.8.3))': + '@vueuse/core@13.1.0(vue@3.5.24(typescript@5.8.3))': dependencies: '@types/web-bluetooth': 0.0.21 '@vueuse/metadata': 13.1.0 - '@vueuse/shared': 13.1.0(vue@3.5.22(typescript@5.8.3)) - vue: 3.5.22(typescript@5.8.3) + '@vueuse/shared': 13.1.0(vue@3.5.24(typescript@5.8.3)) + vue: 3.5.24(typescript@5.8.3) '@vueuse/metadata@13.1.0': {} - '@vueuse/shared@13.1.0(vue@3.5.22(typescript@5.8.3))': + '@vueuse/shared@13.1.0(vue@3.5.24(typescript@5.8.3))': dependencies: - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) '@xn-sakina/rml-darwin-arm64@2.3.0': optional: true @@ -7688,15 +7735,15 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-prettier@10.1.5(eslint@9.31.0(jiti@2.4.2)): + eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@2.4.2)): dependencies: eslint: 9.31.0(jiti@2.4.2) - eslint-config-prettier@9.1.0(eslint@9.31.0(jiti@2.4.2)): + eslint-config-prettier@9.1.2(eslint@9.31.0(jiti@2.4.2)): dependencies: eslint: 9.31.0(jiti@2.4.2) - eslint-plugin-prettier@5.5.1(@types/eslint@8.56.6)(eslint-config-prettier@10.1.5(eslint@9.31.0(jiti@2.4.2)))(eslint@9.31.0(jiti@2.4.2))(prettier@3.6.2): + eslint-plugin-prettier@5.5.4(@types/eslint@8.56.6)(eslint-config-prettier@10.1.8(eslint@9.31.0(jiti@2.4.2)))(eslint@9.31.0(jiti@2.4.2))(prettier@3.6.2): dependencies: eslint: 9.31.0(jiti@2.4.2) prettier: 3.6.2 @@ -7704,9 +7751,9 @@ snapshots: synckit: 0.11.8 optionalDependencies: '@types/eslint': 8.56.6 - eslint-config-prettier: 10.1.5(eslint@9.31.0(jiti@2.4.2)) + eslint-config-prettier: 10.1.8(eslint@9.31.0(jiti@2.4.2)) - eslint-plugin-prettier@5.5.1(@types/eslint@8.56.6)(eslint-config-prettier@9.1.0(eslint@9.31.0(jiti@2.4.2)))(eslint@9.31.0(jiti@2.4.2))(prettier@3.6.2): + eslint-plugin-prettier@5.5.4(@types/eslint@8.56.6)(eslint-config-prettier@9.1.2(eslint@9.31.0(jiti@2.4.2)))(eslint@9.31.0(jiti@2.4.2))(prettier@3.6.2): dependencies: eslint: 9.31.0(jiti@2.4.2) prettier: 3.6.2 @@ -7714,7 +7761,7 @@ snapshots: synckit: 0.11.8 optionalDependencies: '@types/eslint': 8.56.6 - eslint-config-prettier: 9.1.0(eslint@9.31.0(jiti@2.4.2)) + eslint-config-prettier: 9.1.2(eslint@9.31.0(jiti@2.4.2)) eslint-plugin-vue@9.32.0(eslint@9.31.0(jiti@2.4.2)): dependencies: @@ -8755,10 +8802,10 @@ snapshots: arrify: 2.0.1 minimatch: 3.1.2 - naive-ui@2.42.0(vue@3.5.22(typescript@5.8.3)): + naive-ui@2.42.0(vue@3.5.24(typescript@5.8.3)): dependencies: '@css-render/plugin-bem': 0.15.14(css-render@0.15.14) - '@css-render/vue3-ssr': 0.15.14(vue@3.5.22(typescript@5.8.3)) + '@css-render/vue3-ssr': 0.15.14(vue@3.5.24(typescript@5.8.3)) '@types/katex': 0.16.7 '@types/lodash': 4.17.15 '@types/lodash-es': 4.17.12 @@ -8773,10 +8820,10 @@ snapshots: lodash-es: 4.17.21 seemly: 0.3.9 treemate: 0.3.11 - vdirs: 0.1.8(vue@3.5.22(typescript@5.8.3)) - vooks: 0.2.12(vue@3.5.22(typescript@5.8.3)) - vue: 3.5.22(typescript@5.8.3) - vueuc: 0.4.65(vue@3.5.22(typescript@5.8.3)) + vdirs: 0.1.8(vue@3.5.24(typescript@5.8.3)) + vooks: 0.2.12(vue@3.5.24(typescript@5.8.3)) + vue: 3.5.24(typescript@5.8.3) + vueuc: 0.4.65(vue@3.5.24(typescript@5.8.3)) nanoid@3.3.11: {} @@ -8970,19 +9017,19 @@ snapshots: pidtree@0.6.0: {} - pinia-plugin-persistedstate@4.4.1(@nuxt/kit@3.15.0(rollup@4.40.0))(pinia@3.0.3(typescript@5.8.3)(vue@3.5.22(typescript@5.8.3))): + pinia-plugin-persistedstate@4.4.1(@nuxt/kit@3.15.0(rollup@4.40.0))(pinia@3.0.3(typescript@5.8.3)(vue@3.5.24(typescript@5.8.3))): dependencies: deep-pick-omit: 1.2.1 defu: 6.1.4 destr: 2.0.5 optionalDependencies: '@nuxt/kit': 3.15.0(rollup@4.40.0) - pinia: 3.0.3(typescript@5.8.3)(vue@3.5.22(typescript@5.8.3)) + pinia: 3.0.3(typescript@5.8.3)(vue@3.5.24(typescript@5.8.3)) - pinia@3.0.3(typescript@5.8.3)(vue@3.5.22(typescript@5.8.3)): + pinia@3.0.3(typescript@5.8.3)(vue@3.5.24(typescript@5.8.3)): dependencies: '@vue/devtools-api': 7.7.5 - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) optionalDependencies: typescript: 5.8.3 @@ -9707,9 +9754,9 @@ snapshots: universalify@2.0.1: {} - unocss@66.3.3(postcss@8.5.6)(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.22(typescript@5.8.3)): + unocss@66.3.3(postcss@8.5.6)(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.24(typescript@5.8.3)): dependencies: - '@unocss/astro': 66.3.3(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.22(typescript@5.8.3)) + '@unocss/astro': 66.3.3(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.24(typescript@5.8.3)) '@unocss/cli': 66.3.3 '@unocss/core': 66.3.3 '@unocss/postcss': 66.3.3(postcss@8.5.6) @@ -9727,7 +9774,7 @@ snapshots: '@unocss/transformer-compile-class': 66.3.3 '@unocss/transformer-directives': 66.3.3 '@unocss/transformer-variant-group': 66.3.3 - '@unocss/vite': 66.3.3(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.22(typescript@5.8.3)) + '@unocss/vite': 66.3.3(vite@6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1))(vue@3.5.24(typescript@5.8.3)) optionalDependencies: vite: 6.3.5(@types/node@22.15.3)(jiti@2.4.2)(sass@1.86.3)(yaml@2.7.1) transitivePeerDependencies: @@ -9737,7 +9784,7 @@ snapshots: unpipe@1.0.0: {} - unplugin-auto-import@19.1.2(@nuxt/kit@3.15.0(rollup@4.40.0))(@vueuse/core@13.1.0(vue@3.5.22(typescript@5.8.3))): + unplugin-auto-import@19.1.2(@nuxt/kit@3.15.0(rollup@4.40.0))(@vueuse/core@13.1.0(vue@3.5.24(typescript@5.8.3))): dependencies: local-pkg: 1.1.1 magic-string: 0.30.17 @@ -9747,14 +9794,14 @@ snapshots: unplugin-utils: 0.2.4 optionalDependencies: '@nuxt/kit': 3.15.0(rollup@4.40.0) - '@vueuse/core': 13.1.0(vue@3.5.22(typescript@5.8.3)) + '@vueuse/core': 13.1.0(vue@3.5.24(typescript@5.8.3)) unplugin-utils@0.2.4: dependencies: pathe: 2.0.3 picomatch: 4.0.2 - unplugin-vue-components@0.28.0(@babel/parser@7.28.5)(@nuxt/kit@3.15.0(rollup@4.40.0))(rollup@4.40.0)(vue@3.5.22(typescript@5.8.3)): + unplugin-vue-components@0.28.0(@babel/parser@7.28.5)(@nuxt/kit@3.15.0(rollup@4.40.0))(rollup@4.40.0)(vue@3.5.24(typescript@5.8.3)): dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.1.4(rollup@4.40.0) @@ -9766,7 +9813,7 @@ snapshots: minimatch: 9.0.5 mlly: 1.7.3 unplugin: 2.1.0 - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) optionalDependencies: '@babel/parser': 7.28.5 '@nuxt/kit': 3.15.0(rollup@4.40.0) @@ -9857,10 +9904,10 @@ snapshots: vary@1.1.2: {} - vdirs@0.1.8(vue@3.5.22(typescript@5.8.3)): + vdirs@0.1.8(vue@3.5.24(typescript@5.8.3)): dependencies: evtd: 0.2.4 - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) vite-bundle-analyzer@0.16.0: {} @@ -9966,10 +10013,10 @@ snapshots: transitivePeerDependencies: - supports-color - vite-svg-loader@5.1.0(vue@3.5.22(typescript@5.8.3)): + vite-svg-loader@5.1.0(vue@3.5.24(typescript@5.8.3)): dependencies: svgo: 3.3.2 - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) vite@5.4.19(@types/node@22.15.3)(sass@1.86.3): dependencies: @@ -10033,18 +10080,18 @@ snapshots: - supports-color - terser - vooks@0.2.12(vue@3.5.22(typescript@5.8.3)): + vooks@0.2.12(vue@3.5.24(typescript@5.8.3)): dependencies: evtd: 0.2.4 - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) vscode-uri@3.1.0: {} vue-component-type-helpers@2.0.13: {} - vue-demi@0.14.10(vue@3.5.22(typescript@5.8.3)): + vue-demi@0.14.10(vue@3.5.24(typescript@5.8.3)): dependencies: - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) vue-eslint-parser@9.4.3(eslint@9.31.0(jiti@2.4.2)): dependencies: @@ -10059,30 +10106,30 @@ snapshots: transitivePeerDependencies: - supports-color - vue-flow-layout@0.1.1(vue@3.5.22(typescript@5.8.3)): + vue-flow-layout@0.1.1(vue@3.5.24(typescript@5.8.3)): dependencies: - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) - vue-hooks-plus@2.4.1(vue@3.5.22(typescript@5.8.3)): + vue-hooks-plus@2.4.1(vue@3.5.24(typescript@5.8.3)): dependencies: '@types/js-cookie': 3.0.6 '@vue/devtools-api': 7.7.2 js-cookie: 3.0.5 lodash-es: 4.17.21 screenfull: 5.2.0 - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) - vue-i18n@9.13.1(vue@3.5.22(typescript@5.8.3)): + vue-i18n@9.13.1(vue@3.5.24(typescript@5.8.3)): dependencies: '@intlify/core-base': 9.13.1 '@intlify/shared': 9.13.1 '@vue/devtools-api': 6.6.1 - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) - vue-router@4.6.3(vue@3.5.22(typescript@5.8.3)): + vue-router@4.6.3(vue@3.5.24(typescript@5.8.3)): dependencies: '@vue/devtools-api': 6.6.4 - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) vue-tsc@2.2.8(typescript@5.8.3): dependencies: @@ -10090,31 +10137,31 @@ snapshots: '@vue/language-core': 2.2.8(typescript@5.8.3) typescript: 5.8.3 - vue3-next-qrcode@3.0.2(vue@3.5.22(typescript@5.8.3)): + vue3-next-qrcode@3.0.2(vue@3.5.24(typescript@5.8.3)): dependencies: js-binary-schema-parser: 2.0.3 - vue: 3.5.22(typescript@5.8.3) + vue: 3.5.24(typescript@5.8.3) - vue@3.5.22(typescript@5.8.3): + vue@3.5.24(typescript@5.8.3): dependencies: - '@vue/compiler-dom': 3.5.22 - '@vue/compiler-sfc': 3.5.22 - '@vue/runtime-dom': 3.5.22 - '@vue/server-renderer': 3.5.22(vue@3.5.22(typescript@5.8.3)) - '@vue/shared': 3.5.22 + '@vue/compiler-dom': 3.5.24 + '@vue/compiler-sfc': 3.5.24 + '@vue/runtime-dom': 3.5.24 + '@vue/server-renderer': 3.5.24(vue@3.5.24(typescript@5.8.3)) + '@vue/shared': 3.5.24 optionalDependencies: typescript: 5.8.3 - vueuc@0.4.65(vue@3.5.22(typescript@5.8.3)): + vueuc@0.4.65(vue@3.5.24(typescript@5.8.3)): dependencies: - '@css-render/vue3-ssr': 0.15.14(vue@3.5.22(typescript@5.8.3)) + '@css-render/vue3-ssr': 0.15.14(vue@3.5.24(typescript@5.8.3)) '@juggle/resize-observer': 3.4.0 css-render: 0.15.14 evtd: 0.2.4 seemly: 0.3.9 - vdirs: 0.1.8(vue@3.5.22(typescript@5.8.3)) - vooks: 0.2.12(vue@3.5.22(typescript@5.8.3)) - vue: 3.5.22(typescript@5.8.3) + vdirs: 0.1.8(vue@3.5.24(typescript@5.8.3)) + vooks: 0.2.12(vue@3.5.24(typescript@5.8.3)) + vue: 3.5.24(typescript@5.8.3) webidl-conversions@7.0.0: {} diff --git a/src/app-config/app-config.ts b/src/app-config/app-config.ts index ef5ad4ed..823120b4 100644 --- a/src/app-config/app-config.ts +++ b/src/app-config/app-config.ts @@ -12,8 +12,8 @@ import type { MessageProviderProps } from 'naive-ui' export const GLOBAL_CLASS_NAMES = { darkClassName: 'ray-template--dark', lightClassName: 'ray-template--light', - rayTemplateThemePrimaryColor: '--ray-theme-primary-color', - rayTemplateThemePrimaryFadeColor: '--ray-theme-primary-fade-color', + rayTemplateThemePrimaryColor: '--r-theme-primary-color', + rayTemplateThemePrimaryFadeColor: '--r-theme-primary-fade-color', preLoadingAnimation: 'pre-loading-animation', htmlHeight: '--html-height', htmlWidth: '--html-width', diff --git a/src/axios/axios-interceptor/request/index.ts b/src/axios/axios-interceptor/request/index.ts index 3db8b6d1..c3f67eed 100644 --- a/src/axios/axios-interceptor/request/index.ts +++ b/src/axios/axios-interceptor/request/index.ts @@ -1,7 +1,7 @@ -import { useAxiosInterceptor } from '@/axios/utils/interceptor' +import { axiosInterceptor } from '@/axios/utils/interceptor' import implement from './provider' -const { setImplement } = useAxiosInterceptor() +const { setImplement } = axiosInterceptor() export const setupRequestInterceptor = () => { const { implementRequestInterceptorArray } = implement diff --git a/src/axios/axios-interceptor/response/index.ts b/src/axios/axios-interceptor/response/index.ts index 9fb6cb60..e35fe89c 100644 --- a/src/axios/axios-interceptor/response/index.ts +++ b/src/axios/axios-interceptor/response/index.ts @@ -1,7 +1,7 @@ -import { useAxiosInterceptor } from '@/axios/utils/interceptor' +import { axiosInterceptor } from '@/axios/utils/interceptor' import implement from './provider' -const { setImplement } = useAxiosInterceptor() +const { setImplement } = axiosInterceptor() export const setupResponseInterceptor = () => { const { implementResponseInterceptorArray } = implement diff --git a/src/axios/instance.ts b/src/axios/instance.ts index 5fa56457..03efed48 100644 --- a/src/axios/instance.ts +++ b/src/axios/instance.ts @@ -1,6 +1,6 @@ import axios from 'axios' import { AXIOS_CONFIG } from '@/app-config' -import { useAxiosInterceptor } from '@/axios/utils/interceptor' +import { axiosInterceptor } from '@/axios/utils/interceptor' import { setupResponseInterceptor, setupResponseErrorInterceptor, @@ -15,7 +15,7 @@ import type { AxiosInstanceExpand, RequestInterceptorConfig } from './types' // 创建 axios 实例 const server: AxiosInstanceExpand = axios.create(AXIOS_CONFIG) // 获取拦截器实例 -const { createAxiosInstance, beforeFetch, fetchError } = useAxiosInterceptor() +const { createAxiosInstance, beforeFetch, fetchError } = axiosInterceptor() // 请求拦截器 server.interceptors.request.use( diff --git a/src/axios/utils/RequestCanceler.ts b/src/axios/utils/RequestCanceler.ts index 1eeb6c89..696e68ee 100644 --- a/src/axios/utils/RequestCanceler.ts +++ b/src/axios/utils/RequestCanceler.ts @@ -1,116 +1,129 @@ -/** - * - * 自动取消重复请求 - * - * 可以根据自己项目进行定制化配置 - */ - import type { AppRawRequestConfig, CancelerParams } from '@/axios/types' /** - * - * @class RequestCanceler * * @description - * 用于取消重复请求,会在请求前添加 signal 属性,用于取消请求。 - * 通过 generateRequestKey 方法生成请求 key,用于标识请求。 + * 请求取消器。 * - * 如果需要取消请求,则需要在请求前添加 cancelConfig.cancel 为 true; - * 并且会在请求前添加 __CANCELER_TAG_RAY_TEMPLATE__ 属性,用于标识是否需要取消。 + * 用于管理和取消重复的 HTTP 请求: + * - 自动为请求添加 AbortController signal + * - 通过请求特征生成唯一 key 来识别重复请求 + * - 支持取消单个或所有待处理的请求 + * + * @example + * ```ts + * const canceler = new RequestCanceler() + * + * // 添加请求到待处理队列 + * canceler.addPendingRequest(config) + * + * // 移除并取消特定请求 + * canceler.removePendingRequest(config) + * + * // 取消所有待处理请求 + * canceler.cancelAllRequest() + * ``` */ export default class RequestCanceler { - private pendingRequest: Map - - constructor() { - this.pendingRequest = new Map() - } + /** 待处理请求的 Map,key 为请求标识,value 为 AbortController */ + private readonly pendingRequest = new Map() /** * - * @param config 请求体 config + * @param config - 请求配置 + * + * @returns 是否需要添加取消功能,默认为 true * * @description - * 判断是否需要添加 signal 属性。 - * - * 如果 cancelConfig 为 false,则不添加 signal 属性; - * 如果 cancelConfig 为 true,则添加 signal 属性。 - * - * @example - * const bool = isAppending(config) // true or false + * 判断请求是否需要添加取消功能。 */ - private isAppending(config: AppRawRequestConfig | CancelerParams) { + private shouldAddCanceler( + config: AppRawRequestConfig | CancelerParams, + ): boolean { return config.cancelConfig?.cancel ?? true } /** * - * @param config 请求体 config + * @param config - 请求配置 + * + * @returns 请求的唯一标识字符串 * * @description - * 根据当前请求生成 key。 - * - * @example - * const key = generateRequestKey(config) // string + * 基于 URL、方法、参数和数据生成请求的唯一标识 key。 */ - private generateRequestKey(config: AppRawRequestConfig | CancelerParams) { - const { method, url } = config + private generateRequestKey( + config: AppRawRequestConfig | CancelerParams, + ): string { + const { method = '', url = '', params, data } = config - return [ - url || '', - method || '', - JSON.stringify(config.params), - JSON.stringify(config.data), - ].join('&') + return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&') } /** - * - * @param config axios request config - * + + * @param config - Axios 请求配置 + * @description * 添加请求到 pendingRequest map 中,用于取消请求。 * 并且如果已经存在该请求,则会取消上次请求,并且重新挂载 signal。 * * 如果不需要该请求被挂载,则需要在请求前添加 cancelConfig.cancel 为 false。 * 如果该请求需要被取消,则会添加 __CANCELER_TAG_RAY_TEMPLATE__ 属性,标记是否需要取消。 - * + * @example - * addPendingRequest(config) + * ```ts + * // 默认启用取消功能 + * canceler.addPendingRequest(config) + * + * // 禁用取消功能 + * canceler.addPendingRequest({ + * ...config, + * cancelConfig: { cancel: false } + * }) + * ``` */ - addPendingRequest(config: AppRawRequestConfig | CancelerParams) { - if (this.isAppending(config)) { - config.__CANCELER_TAG_RAY_TEMPLATE__ = '__CANCELER_TAG_RAY_TEMPLATE__' + addPendingRequest(config: AppRawRequestConfig | CancelerParams): void { + if (!this.shouldAddCanceler(config)) { + return + } - const requestKey = this.generateRequestKey(config) + config.__CANCELER_TAG_RAY_TEMPLATE__ = '__CANCELER_TAG_RAY_TEMPLATE__' - if (!this.pendingRequest.has(requestKey)) { - const controller = new AbortController() + const requestKey = this.generateRequestKey(config) + const existingController = this.pendingRequest.get(requestKey) - config.signal = controller.signal + if (existingController) { + // 复用现有的 signal + config.signal = existingController.signal + } else { + // 创建新的 AbortController + const controller = new AbortController() - this.pendingRequest.set(requestKey, controller) - } else { - // 如果已经有该 key 则重新挂载 signal - config.signal = this.pendingRequest.get(requestKey)?.signal - } + config.signal = controller.signal + this.pendingRequest.set(requestKey, controller) } } /** * - * @param config axios request config + * @param config - Axios 请求配置 * * @description - * 移除 pendingRequest map 中的请求,如果存在的话。 + * 移除并取消特定请求, + * 从待处理队列中移除请求,并调用 abort() 取消该请求。 * * @example - * removePendingRequest(config) + * ```ts + * canceler.removePendingRequest(config) + * ``` */ - removePendingRequest(config: AppRawRequestConfig | CancelerParams) { + removePendingRequest(config: AppRawRequestConfig | CancelerParams): void { const requestKey = this.generateRequestKey(config) + const controller = this.pendingRequest.get(requestKey) - if (this.pendingRequest.has(requestKey)) { - this.pendingRequest.get(requestKey)!.abort() + if (controller) { + controller.abort() this.pendingRequest.delete(requestKey) } } @@ -118,17 +131,50 @@ export default class RequestCanceler { /** * * @description - * 移除所有 pendingRequest map 中的请求。 + * 取消所有待处理的请求。 * - * 值得注意的是,该方法会一次性移除所有的请求,所以需要注意是否有需要在后台挂载的请求; - * 如果有需要在后台挂载的请求,则需要在请求前添加 cancelConfig.cancel 为 false。 + * 遍历并取消队列中的所有请求,然后清空队列。 + * + * ⚠️ 注意:此方法会取消所有请求,包括后台运行的请求。 + * + * 如果某些请求不应被取消,请在请求配置中设置 `cancelConfig.cancel = false` * * @example - * cancelAllRequest() + * ```ts + * // 在路由切换或组件卸载时取消所有请求 + * canceler.cancelAllRequest() + * ``` */ - cancelAllRequest() { - this.pendingRequest.forEach((curr) => { - curr.abort() + cancelAllRequest(): void { + this.pendingRequest.forEach((controller) => { + controller.abort() }) + this.pendingRequest.clear() + } + + /** + * + * @returns 待处理请求数量 + * + * @description + * 获取当前待处理请求的数量。 + */ + getPendingCount(): number { + return this.pendingRequest.size + } + + /** + * + * @param config - 请求配置 + * + * @returns 是否存在于待处理队列 + * + * @description + * 检查特定请求是否在待处理队列中。 + */ + hasPendingRequest(config: AppRawRequestConfig | CancelerParams): boolean { + const requestKey = this.generateRequestKey(config) + + return this.pendingRequest.has(requestKey) } } diff --git a/src/axios/utils/interceptor.ts b/src/axios/utils/interceptor.ts index 00444c0f..ffbd88f6 100644 --- a/src/axios/utils/interceptor.ts +++ b/src/axios/utils/interceptor.ts @@ -13,64 +13,101 @@ import type { import type { AnyFC } from '@/types' import type { AxiosError } from 'axios' +type ImplementKeys = keyof ImplementQueue +type ErrorImplementKeys = keyof ErrorImplementQueue + // 当前请求的实例 const axiosFetchInstance: AxiosFetchInstance = { requestInstance: null, responseInstance: null, } + // 请求失败返回值 const axiosFetchError: AxiosFetchError> = { requestError: null, responseError: null, } + // 请求队列(区分 resolve 与 reject 状态) const implement: ImplementQueue = { implementRequestInterceptorArray: [], implementResponseInterceptorArray: [], } + // 请求失败队列 const errorImplement: ErrorImplementQueue = { implementRequestInterceptorErrorArray: [], implementResponseInterceptorErrorArray: [], } -type ImplementKeys = keyof ImplementQueue - -type ErrorImplementKeys = keyof ErrorImplementQueue - // 取消器实例 export const axiosCanceler = new RequestCanceler() -export const useAxiosInterceptor = () => { - // 创建拦截器实例 +export const axiosInterceptor = () => { + /** + * + * @param instance - 请求或响应实例 + * @param instanceKey - 实例类型标识 + * + * @description + * 创建拦截器实例。 + */ const createAxiosInstance = ( instance: RequestInterceptorConfig | ResponseInterceptorConfig, instanceKey: keyof AxiosFetchInstance, - ) => { - instanceKey === 'requestInstance' - ? (axiosFetchInstance['requestInstance'] = - instance as RequestInterceptorConfig) - : (axiosFetchInstance['responseInstance'] = - instance as ResponseInterceptorConfig) + ): void => { + if (instanceKey === 'requestInstance') { + axiosFetchInstance.requestInstance = instance as RequestInterceptorConfig + } else { + axiosFetchInstance.responseInstance = + instance as ResponseInterceptorConfig + } } - // 获取当前实例 + /** + * + * @param instanceKey - 实例类型标识 + * + * @returns 对应的实例 + * + * @description + * 获取当前实例。 + */ const getAxiosInstance = (instanceKey: keyof AxiosFetchInstance) => { return axiosFetchInstance[instanceKey] } - // 设置注入方法队列 + /** + * + * @param key - 队列键名 + * @param func - 拦截器函数数组 + * @param fetchType - 请求类型(成功/失败) + * + * @description + * 设置注入方法队列。 + */ const setImplement = ( key: ImplementKeys | ErrorImplementKeys, func: AnyFC[], fetchType: FetchType, - ) => { - fetchType === 'ok' - ? (implement[key as ImplementKeys] = func) - : (errorImplement[key as ErrorImplementKeys] = func) + ): void => { + if (fetchType === 'ok') { + implement[key as ImplementKeys] = func + } else { + errorImplement[key as ErrorImplementKeys] = func + } } - // 获取队列中所有的所有拦截器方法 + /** + * + * @param key - 队列键名 + * @param fetchType - 请求类型(成功/失败) + * + * @returns 拦截器函数数组 + * + * @description + * 获取队列中所有的拦截器方法。 + */ const getImplement = ( key: ImplementKeys | ErrorImplementKeys, fetchType: FetchType, @@ -80,47 +117,68 @@ export const useAxiosInterceptor = () => { : errorImplement[key as ErrorImplementKeys] } - // 队列执行器 - const implementer = (funcs: AnyFC[], ...args: any[]) => { - if (Array.isArray(funcs)) { - funcs.forEach((curr) => { - if (typeof curr === 'function') { - curr(...args) - } - }) - } + /** + * + * @param funcs - 函数数组 + * @param args - 传递给函数的参数 + * + * @description + * 队列执行器 - 执行所有拦截器函数。 + */ + const executeQueue = (funcs: AnyFC[], ...args: unknown[]): void => { + funcs.forEach((func) => { + if (typeof func === 'function') { + func(...args) + } + }) } - // 请求、响应前执行拦截器队列中的所有方法 + /** + * + * @param key - 实例类型标识 + * @param implementKey - 队列键名 + * @param fetchType - 请求类型(成功/失败) + * + * @description + * 请求、响应前执行拦截器队列中的所有方法。 + */ const beforeFetch = ( key: keyof AxiosFetchInstance, implementKey: ImplementKeys | ErrorImplementKeys, fetchType: FetchType, - ) => { - const funcArr = - fetchType === 'ok' - ? implement[implementKey as ImplementKeys] - : errorImplement[implementKey as ErrorImplementKeys] + ): void => { const instance = getAxiosInstance(key) + + if (!instance) { + return + } + + const funcArr = getImplement(implementKey, fetchType) const { MODE } = getAppEnvironment() - if (instance) { - implementer(funcArr, instance, MODE) - } + executeQueue(funcArr, instance, MODE) } - // 请求、响应错误时执行队列中所有方法 + /** + * + * @param key - 错误类型标识 + * @param error - 错误对象 + * @param errorImplementKey - 错误队列键名 + * + * @description + * 请求、响应错误时执行队列中所有方法。 + */ const fetchError = ( key: keyof AxiosFetchError, error: AxiosError, errorImplementKey: ErrorImplementKeys, - ) => { + ): void => { axiosFetchError[key] = error const funcArr = errorImplement[errorImplementKey] const { MODE } = getAppEnvironment() - implementer(funcArr, error, MODE) + executeQueue(funcArr, error, MODE) } return { @@ -133,4 +191,4 @@ export const useAxiosInterceptor = () => { } } -export type UseAxiosInterceptor = ReturnType +export type AxiosInterceptor = ReturnType diff --git a/src/components/base/RBarcode/index.ts b/src/components/base/RBarcode/index.ts index a1b7e79c..0075a3cd 100644 --- a/src/components/base/RBarcode/index.ts +++ b/src/components/base/RBarcode/index.ts @@ -2,7 +2,22 @@ import RBarcode from './src/Barcode' import barcodeProps from './src/props' import type { ExtractPublicPropTypes } from 'vue' +import type { RBarcodeSize } from './src/types' -export type BarcodeProps = ExtractPublicPropTypes +// 扩展 BarcodeProps 以提供更好的类型提示 +export type BarcodeProps = Omit< + ExtractPublicPropTypes, + 'width' | 'height' +> & { + width?: RBarcodeSize + height?: RBarcodeSize +} export { RBarcode, barcodeProps } + +export type { + RBarcodeSize, + RBarcodeRender, + RBarcodeFormat, + RBarcodeOptions, +} from './src/types' diff --git a/src/components/base/RBarcode/src/Barcode.tsx b/src/components/base/RBarcode/src/Barcode.tsx index 02057532..54ea40ec 100644 --- a/src/components/base/RBarcode/src/Barcode.tsx +++ b/src/components/base/RBarcode/src/Barcode.tsx @@ -6,8 +6,10 @@ import barcode from 'jsbarcode' import props from './props' import { completeSize, call } from '@/utils' import { useTemplateRef } from 'vue' +import { useResizeObserver } from '@vueuse/core' import type { WatchStopHandle } from 'vue' +import type { UseResizeObserverReturn } from '@vueuse/core' export default defineComponent({ name: 'RBarcode', @@ -16,10 +18,25 @@ export default defineComponent({ const barcodeRef = useTemplateRef( 'barcodeRef', ) + const containerRef = useTemplateRef('containerRef') + const containerSize = ref({ width: 0, height: 0 }) + let resizeObserverReturn: UseResizeObserverReturn | null + const cssVars = computed(() => { + let width = completeSize(props.width) + let height = completeSize(props.height) + + if (props.width === 'responsive' && containerSize.value.width > 0) { + width = `${containerSize.value.width}px` + } + + if (props.height === 'responsive' && containerSize.value.height > 0) { + height = `${containerSize.value.height}px` + } + const cssVar = { - '--r-barcode-width': completeSize(props.width), - '--r-barcode-height': completeSize(props.height), + '--r-barcode-width': width, + '--r-barcode-height': height, } return cssVar @@ -27,6 +44,10 @@ export default defineComponent({ let watchStop: WatchStopHandle const barcodeRender = () => { + if (!barcodeRef.value) { + return + } + try { const { format, text, options, onSuccess } = props @@ -34,6 +55,20 @@ export default defineComponent({ format, }) + // 如果是响应式模式,根据容器尺寸调整条形码选项 + if (containerSize.value.width > 0) { + if (props.width === 'responsive') { + assignOptions.width = Math.max(1, containerSize.value.width / 100) + } + + if (props.height === 'responsive') { + assignOptions.height = Math.max( + 20, + containerSize.value.height * 0.8, + ) + } + } + barcode( barcodeRef.value, text !== void 0 && text !== null ? text.toString() : '', @@ -60,14 +95,51 @@ export default defineComponent({ watchEffect(() => { if (props.watchText) { + watchStop?.() + watchStop = watch(() => props.text, barcodeRender) } else { watchStop?.() } + + // 监听容器尺寸变化 + if (props.responsive) { + resizeObserverReturn?.stop() + + resizeObserverReturn = useResizeObserver( + containerRef, + (entries: readonly ResizeObserverEntry[]) => { + const entry = entries[0] + + if (entry) { + const { width, height } = entry.contentRect + + containerSize.value = { width, height } + + nextTick(() => { + barcodeRender() + }) + } + }, + ) + } else { + resizeObserverReturn?.stop() + + resizeObserverReturn = null + } }) onMounted(() => { - barcodeRender() + // 初始化容器尺寸 + if (containerRef.value) { + const rect = containerRef.value.getBoundingClientRect() + + containerSize.value = { width: rect.width, height: rect.height } + } + + nextTick(() => { + barcodeRender() + }) }) onBeforeUnmount(() => { watchStop?.() @@ -75,11 +147,12 @@ export default defineComponent({ return { barcodeRef, + containerRef, cssVars, } }, render() { - const { barcodeRender, loading, cssVars } = this + const { barcodeRender, loading, cssVars, responsive } = this const c = [ 'r-barcode', { @@ -87,12 +160,21 @@ export default defineComponent({ }, ] + const barcodeElement = + barcodeRender === 'canvas' ? ( + + ) : ( + + ) + return ( - {barcodeRender === 'canvas' ? ( - + {responsive ? ( +
+ {barcodeElement} +
) : ( - + barcodeElement )}
) diff --git a/src/components/base/RBarcode/src/index.scss b/src/components/base/RBarcode/src/index.scss index a06fc126..c874b098 100644 --- a/src/components/base/RBarcode/src/index.scss +++ b/src/components/base/RBarcode/src/index.scss @@ -9,8 +9,27 @@ } } +.r-barcode-container { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + + .r-barcode { + width: 100%; + height: 100%; + } +} + .r-barcode-spin, .r-barcode-spin .n-spin-content { width: max-content !important; height: max-content !important; } + +.r-barcode-spin:has(.r-barcode-container), +.r-barcode-spin:has(.r-barcode-container) .n-spin-content { + width: 100% !important; + height: 100% !important; +} diff --git a/src/components/base/RBarcode/src/props.ts b/src/components/base/RBarcode/src/props.ts index 8db4b217..098c27c3 100644 --- a/src/components/base/RBarcode/src/props.ts +++ b/src/components/base/RBarcode/src/props.ts @@ -1,4 +1,9 @@ -import type { RBarcodeRender, RBarcodeOptions, RBarcodeFormat } from './types' +import type { + RBarcodeRender, + RBarcodeOptions, + RBarcodeFormat, + RBarcodeSize, +} from './types' import type { PropType } from 'vue' import type { MaybeArray } from '@/types' @@ -7,23 +12,39 @@ const props = { * * @description * 条形码宽度。 + * - 数字:固定宽度(单位:px) + * - 'auto':自动宽度 + * - 其他字符串:CSS 宽度值(如 '100%', '200px') * * @default 'auto' + * @example + * width={200} + * width="auto" + * width="responsive" + * width="100%" */ width: { - type: [String, Number] as PropType, - default: 'auto', + type: [String, Number] as PropType, + default: 'auto' as const, }, /** * * @description * 条形码高度。 + * - 数字:固定高度(单位:px) + * - 'auto':自动高度 + * - 其他字符串:CSS 高度值(如 '100%', '200px') * * @default 'auto' + * @example + * height={100} + * height="auto" + * height="responsive" + * height="50%" */ height: { - type: [String, Number] as PropType, - default: 'auto', + type: [String, Number] as PropType, + default: 'auto' as const, }, /** * @@ -132,6 +153,19 @@ const props = { onFinally: { type: [Function, Array] as PropType void>>, }, + /** + * + * @description + * 是否启用响应式尺寸,当容器大小变化时自动重新渲染条形码。 + * + * 如果启用了该属性,width 和 height 配置项将失效。 + * + * @default false + */ + responsive: { + type: Boolean, + default: false, + }, } as const export default props diff --git a/src/components/base/RBarcode/src/types.ts b/src/components/base/RBarcode/src/types.ts index 6b7fe2d6..f5f414a0 100644 --- a/src/components/base/RBarcode/src/types.ts +++ b/src/components/base/RBarcode/src/types.ts @@ -24,3 +24,6 @@ export type RBarcodeFormat = | 'MSI1110' | 'pharmacode' | 'codabar' + +// 使用模板字面量类型来保留字面量提示 +export type RBarcodeSize = number | 'auto' | (string & {}) diff --git a/src/components/base/RChart/index.ts b/src/components/base/RChart/index.ts index 37cff17d..d3548e28 100644 --- a/src/components/base/RChart/index.ts +++ b/src/components/base/RChart/index.ts @@ -1,4 +1,4 @@ -import RChart from './src' +import RChart from './src/Chart' import chartProps from './src/props' import useChart from './src/hooks/useChart' diff --git a/src/components/base/RChart/src/index.tsx b/src/components/base/RChart/src/Chart.tsx similarity index 96% rename from src/components/base/RChart/src/index.tsx rename to src/components/base/RChart/src/Chart.tsx index 2264aed0..900309db 100644 --- a/src/components/base/RChart/src/index.tsx +++ b/src/components/base/RChart/src/Chart.tsx @@ -88,7 +88,7 @@ export default defineComponent({ setup(props, { expose }) { const { getAppTheme } = useSettingGetters() // echart 容器实例 - const rayChartRef = useTemplateRef('rayChartRef') + const chartRef = useTemplateRef('chartRef') // echart 父容器实例 const rayChartWrapperRef = useTemplateRef('rayChartWrapperRef') // echart 实例 @@ -113,8 +113,8 @@ export default defineComponent({ ]) const cssVarsRef = computed(() => { return { - '--ray-chart-width': completeSize(props.width), - '--ray-chart-height': completeSize(props.height), + '--r-chart-width': completeSize(props.width), + '--r-chart-height': completeSize(props.height), } }) // 目标是否可见 @@ -241,7 +241,7 @@ export default defineComponent({ */ const renderChart = (theme: string = echartTheme) => { // 获取 dom 容器 - const element = rayChartRef.value as HTMLElement + const element = chartRef.value as HTMLElement // 获取配置项 const options = combineChartOptions(props.options) // 获取 dom 容器实际宽高 @@ -264,7 +264,7 @@ export default defineComponent({ // 是否强制下一队列渲染图表 if (props.nextTick) { - echartInstanceRef.value.setOption({}) + // echartInstanceRef.value.setOption({}) nextTick(() => { options && echartInstanceRef.value?.setOption(options) @@ -435,6 +435,8 @@ export default defineComponent({ watchEffect(() => { // 是否启用了可视区域监听 if (props.intersectionObserver) { + intersectionObserverReturn?.stop() + intersectionObserverReturn = useIntersectionObserver( props.intersectionObserverTarget || rayChartWrapperRef, ([entry]) => { @@ -442,10 +444,14 @@ export default defineComponent({ }, props.intersectionOptions, ) + } else { + intersectionObserverReturn?.stop() } // 监听 options 变化 if (props.watchOptions) { + watchThrottledCallback?.() + watchThrottledCallback = watchThrottled( () => props.options, (ndata) => { @@ -462,7 +468,7 @@ export default defineComponent({ }, { // 深度监听 options - deep: true, + deep: props.watchDeep, throttle: props.watchOptionsThrottleWait, }, ) @@ -506,7 +512,7 @@ export default defineComponent({ }) return { - rayChartRef, + chartRef, cssVarsRef, rayChartWrapperRef, moreDropDownOptions, @@ -536,7 +542,7 @@ export default defineComponent({ > {{ default: renderNode( -
, +
, ), header: renderNode(title, { defaultElement:
, @@ -557,7 +563,7 @@ export default defineComponent({ ) : (
-
+
) }, diff --git a/src/components/base/RChart/src/index.scss b/src/components/base/RChart/src/index.scss index 4e574163..4bede3d8 100644 --- a/src/components/base/RChart/src/index.scss +++ b/src/components/base/RChart/src/index.scss @@ -1,6 +1,6 @@ .ray-chart { - width: var(--ray-chart-width); - height: var(--ray-chart-height); + width: var(--r-chart-width); + height: var(--r-chart-height); border: none; outline: none; box-sizing: border-box; diff --git a/src/components/base/RChart/src/props.ts b/src/components/base/RChart/src/props.ts index 7cdd9baa..d6318c16 100644 --- a/src/components/base/RChart/src/props.ts +++ b/src/components/base/RChart/src/props.ts @@ -341,6 +341,17 @@ const props = { type: Number, default: 500, }, + /** + * + * @description + * 是否深度监听 options 配置项。 + * + * @default true + */ + watchDeep: { + type: Boolean, + default: true, + }, /** * * @description diff --git a/src/components/base/RIcon/index.ts b/src/components/base/RIcon/index.ts index c6175272..4c683405 100644 --- a/src/components/base/RIcon/index.ts +++ b/src/components/base/RIcon/index.ts @@ -1,4 +1,4 @@ -import RIcon from './src' +import RIcon from './src/Icon' import iconProps from './src/props' import type { ExtractPublicPropTypes } from 'vue' diff --git a/src/components/base/RIcon/src/index.tsx b/src/components/base/RIcon/src/Icon.tsx similarity index 75% rename from src/components/base/RIcon/src/index.tsx rename to src/components/base/RIcon/src/Icon.tsx index 918edf56..a244ffe7 100644 --- a/src/components/base/RIcon/src/index.tsx +++ b/src/components/base/RIcon/src/Icon.tsx @@ -10,15 +10,15 @@ export default defineComponent({ const symbolId = computed(() => `#${props.prefix}-${props.name}`) const cssVars = computed(() => { const cssVar = { - '--ray-icon-width': props.width + '--r-icon-width': props.width ? completeSize(props.width) : completeSize(props.size), - '--ray-icon-height': props.height + '--r-icon-height': props.height ? completeSize(props.height) : completeSize(props.size), - '--ray-icon-depth': props.depth, - '--ray-icon-cursor': props.cursor, - '--ray-icon-color': props.color, + '--r-icon-depth': props.depth, + '--r-icon-cursor': props.cursor, + '--r-icon-color': props.color, } return cssVar @@ -41,13 +41,13 @@ export default defineComponent({ render() { return ( diff --git a/src/components/base/RIcon/src/index.scss b/src/components/base/RIcon/src/index.scss index c05db01d..f1b99fcf 100644 --- a/src/components/base/RIcon/src/index.scss +++ b/src/components/base/RIcon/src/index.scss @@ -1,28 +1,28 @@ -.ray-icon { +.r-icon { position: relative; - width: var(--ray-icon-width); - height: var(--ray-icon-height); + width: var(--r-icon-width); + height: var(--r-icon-height); border: none; outline: none; text-align: center; display: inline-flex; justify-content: center; align-items: center; - color: var(--ray-icon-color); + color: var(--r-icon-color); transform: translateZ(0); - opacity: var(--ray-icon-depth); - cursor: var(--ray-icon-cursor); + opacity: var(--r-icon-depth); + cursor: var(--r-icon-cursor); - & svg[RayIconAttribute='ray-icon'] { - width: var(--ray-icon-width); - height: var(--ray-icon-height); + & svg[RIconAttribute='r-icon'] { + width: var(--r-icon-width); + height: var(--r-icon-height); fill: currentColor; } } -.ray-icon-path__animate { - stroke-dasharray: var(--ray-icon-path-length); - stroke-dashoffset: var(--ray-icon-path-length); +.r-icon-path__animate { + stroke-dasharray: var(--r-icon-path-length); + stroke-dashoffset: var(--r-icon-path-length); animation: rayIconPathAnimate 2s forwards; } diff --git a/src/components/base/RIframe/index.ts b/src/components/base/RIframe/index.ts index cc5dc8b4..edfb0808 100644 --- a/src/components/base/RIframe/index.ts +++ b/src/components/base/RIframe/index.ts @@ -1,4 +1,4 @@ -import RIframe from './src' +import RIframe from './src/Iframe' import iframeProps from './src/props' import type * as RIframeType from './src/types' diff --git a/src/components/base/RIframe/src/index.tsx b/src/components/base/RIframe/src/Iframe.tsx similarity index 90% rename from src/components/base/RIframe/src/index.tsx rename to src/components/base/RIframe/src/Iframe.tsx index c391e43e..e7556435 100644 --- a/src/components/base/RIframe/src/index.tsx +++ b/src/components/base/RIframe/src/Iframe.tsx @@ -13,9 +13,9 @@ export default defineComponent({ setup(props, { expose }) { const cssVars = computed(() => { const cssVar = { - '--ray-iframe-frameborder': completeSize(props.frameborder), - '--ray-iframe-width': completeSize(props.width), - '--ray-iframe-height': completeSize(props.height), + '--r-iframe-frameborder': completeSize(props.frameborder), + '--r-iframe-width': completeSize(props.width), + '--r-iframe-height': completeSize(props.height), } return cssVar diff --git a/src/components/base/RIframe/src/index.scss b/src/components/base/RIframe/src/index.scss index 1a29ca87..df9b219c 100644 --- a/src/components/base/RIframe/src/index.scss +++ b/src/components/base/RIframe/src/index.scss @@ -1,8 +1,8 @@ .ray-iframe { - width: var(--ray-iframe-width); - height: var(--ray-iframe-height); + width: var(--r-iframe-width); + height: var(--r-iframe-height); box-sizing: border-box; - border: var(--ray-iframe-frameborder); + border: var(--r-iframe-frameborder); & .ray-iframe__container { width: 100%; diff --git a/src/components/base/RModal/src/hooks/useModal.ts b/src/components/base/RModal/src/hooks/useModal.ts index a9f64fa7..665071e6 100644 --- a/src/components/base/RModal/src/hooks/useModal.ts +++ b/src/components/base/RModal/src/hooks/useModal.ts @@ -4,6 +4,15 @@ import { R_MODAL_CLASS, CSS_VARS_KEYS } from '../constant' import type { RModalProps } from '../types' +/** + * + * @deprecated + * + * @see https://www.naiveui.com/zh-CN/dark/components/modal#useModal-API + * + * @description + * 请使用官方的 `useModal` 方法。 + */ const useModal = () => { const { create: naiveCreate, destroyAll: naiveDestroyAll } = useNaiveModal() diff --git a/src/components/base/RMoreDropdown/index.ts b/src/components/base/RMoreDropdown/index.ts index e9dd936a..834dcfbe 100644 --- a/src/components/base/RMoreDropdown/index.ts +++ b/src/components/base/RMoreDropdown/index.ts @@ -1,4 +1,4 @@ -import RMoreDropdown from './src' +import RMoreDropdown from './src/MoreDropdown' import moreDropdownProps from './src/props' import type { ExtractPublicPropTypes } from 'vue' diff --git a/src/components/base/RMoreDropdown/src/index.tsx b/src/components/base/RMoreDropdown/src/MoreDropdown.tsx similarity index 100% rename from src/components/base/RMoreDropdown/src/index.tsx rename to src/components/base/RMoreDropdown/src/MoreDropdown.tsx diff --git a/src/components/base/RTable/src/Table.tsx b/src/components/base/RTable/src/Table.tsx index 06a50b69..396356ec 100644 --- a/src/components/base/RTable/src/Table.tsx +++ b/src/components/base/RTable/src/Table.tsx @@ -13,27 +13,25 @@ import { config } from './shared' import { pick } from 'lodash-es' import { useTemplateRef } from 'vue' -import type { DropdownOption, DataTableInst, DataTableProps } from 'naive-ui' +import type { DropdownOption, DataTableProps } from 'naive-ui' import type { ComponentSize } from '@/types' import type { C as CType, PropsComponentPopselectKeys, RTableInst, } from './types' -import type { ExtractPublicPropTypes } from 'vue' export default defineComponent({ name: 'RTable', inheritAttrs: false, props, setup(props, ctx) { - const { expose, emit } = ctx + const { expose } = ctx const rTableInst = useTemplateRef('rTableInst') const wrapperRef = useTemplateRef('wrapperRef') - - const uuidWrapper = uuid(16) // wrapper id - const uuidTable = uuid(16) // table id + const uuidWrapper = uuid(16) + const uuidTable = uuid(16) /** * * x: 横坐标 @@ -94,53 +92,70 @@ export default defineComponent({ /** * - * @param key key - * @param option context menu select option + * @param key 当前选中项的 key + * @param option 当前选中项 + * + * @description + * 右键菜单选择事件回调。 */ const contextMenuSelect = ( key: number | string, option: DropdownOption, ) => { + contextMenuReactive.showContextMenu = false + const { onContextMenuClick } = props if (onContextMenuClick) { call(onContextMenuClick, key, option) } - - contextMenuReactive.showContextMenu = false } /** * - * 合并 RTable 的所有 rowProps - * 如果开启了右键菜单功能,自动会拦截右键事件 + * @param e 鼠标点击事件 + * + * @description + * 处理右键菜单事件。 + */ + const tableContextMenu = (e: MouseEvent) => { + e.preventDefault() + contextMenuReactive.showContextMenu = false + + nextTick().then(() => { + contextMenuReactive.showContextMenu = true + contextMenuReactive.x = e.clientX + contextMenuReactive.y = e.clientY + }) + } + + /** + * + * @param row 当前行数据 + * @param idx 当前行索引 + * + * @description + * 合并 RTable 的所有 rowProps,如果开启了右键菜单功能,自动会拦截右键事件。 */ const combineRowProps = (row: Record, idx: number) => { - const interceptRowProps = props.rowProps?.(row, idx) + const interceptRowProps = props.rowProps?.(row, idx) || {} + + if (props.disabledContextMenu) { + return interceptRowProps + } return { ...interceptRowProps, - onContextmenu: props.disabledContextMenu - ? void 0 - : (e: MouseEvent) => { - e.preventDefault() - - contextMenuReactive.showContextMenu = false - - nextTick().then(() => { - contextMenuReactive.showContextMenu = true - contextMenuReactive.x = e.clientX - contextMenuReactive.y = e.clientY - }) - }, + onContextmenu: tableContextMenu, } } /** * - * @param size table size + * @param size 表格尺寸 * - * 修改 table size + * @description + * 修改表格尺寸。 */ const changeTableSize = (size: ComponentSize) => { privateReactive.size = size @@ -148,9 +163,10 @@ export default defineComponent({ /** * - * @param options table columns + * @param options 表格列配置项 * - * 更新 table columns,同时触发 onUpdateColumns 和 onUpdate:columns 事件 + * @description + * 更新 table columns,同时触发 onUpdateColumns 和 onUpdate:columns 事件。 */ const updateTableColumn = (options: CType[]) => { const { onUpdateColumns, 'onUpdate:columns': $onUpdateColumns } = props @@ -166,8 +182,9 @@ export default defineComponent({ /** * - * 处理自定义的 toolOptions - * 匹配所有符合条件的 toolOptions,然后执行 + * @description + * 处理自定义的 toolOptions。 + * 匹配所有符合条件的 toolOptions,然后执行。 */ const renderToolOptions = () => { const { toolOptions } = props @@ -189,45 +206,56 @@ export default defineComponent({ /** * - * @param p props - * - * 处理 toolOptions,合并渲染所有的 toolOptions + * @description + * 渲染默认工具栏。 */ - const tool = (p: typeof props) => { - const { tool } = p + const renderDefaultToolOptions = () => { + const { onUpdateColumns, 'onUpdate:columns': rOnUpdateColumns } = props + const needSettingComponent = !!onUpdateColumns || !!rOnUpdateColumns - if (!tool) { - return - } - - const renderDefaultToolOptions = () => ( + return ( - - + + - + {needSettingComponent ? ( + + ) : null} ) + } - if (!props.toolOptions) { - return renderDefaultToolOptions - } else { - if (props.coverTool) { - return () => {renderToolOptions()} - } else { - return () => ( - - {renderDefaultToolOptions()} - {renderToolOptions()} - - ) - } + /** + * + * @param p props + * + * @description + * 处理 toolOptions,合并渲染所有的 toolOptions。 + */ + const tool = (p: typeof props) => { + if (!p.tool) { + return } + + if (!p.toolOptions) { + return renderDefaultToolOptions + } + + if (p.coverTool) { + return () => {renderToolOptions()} + } + + return () => ( + + {renderDefaultToolOptions()} + {renderToolOptions()} + + ) } onMounted(() => { @@ -239,6 +267,7 @@ export default defineComponent({ uuidTable, uuidWrapper, wrapperRef, + tableRef: rTableInst, }) } }) @@ -263,6 +292,7 @@ export default defineComponent({ propsPopselectValue, cardHeaderStyle, flexAutoHeightStyle, + tableContextMenu, } }, render() { diff --git a/src/components/base/RTable/src/components/C.tsx b/src/components/base/RTable/src/components/C.tsx index d4e235d0..e90793d1 100644 --- a/src/components/base/RTable/src/components/C.tsx +++ b/src/components/base/RTable/src/components/C.tsx @@ -177,7 +177,10 @@ export default defineComponent({ const { onUpdateColumn } = props if (onUpdateColumn) { - call(onUpdateColumn, options) + // 使用 nextTick 确保 DOM 更新后再触发事件 + nextTick(() => { + call(onUpdateColumn, options) + }) } } diff --git a/src/components/base/RTable/src/hooks/useCheckedRowKeys.ts b/src/components/base/RTable/src/hooks/useCheckedRowKeys.ts index 26f42581..5a2d49af 100644 --- a/src/components/base/RTable/src/hooks/useCheckedRowKeys.ts +++ b/src/components/base/RTable/src/hooks/useCheckedRowKeys.ts @@ -303,6 +303,43 @@ const useCheckedRowKeys = < } } + /** + * + * @param keys 批量选中的 keys + * + * @description + * 批量选中指定的 keys。 + */ + const selectKeys = (keys: RowKey[]) => { + keys.forEach((key) => selectKey(key)) + } + + /** + * + * @param key 需要切换的 key + * + * @description + * 切换指定 key 的选中状态。 + */ + const toggleKey = (key: RowKey) => { + if (keysRef.value.includes(key)) { + clearKey(key) + } else { + selectKey(key) + } + } + + /** + * + * @param key 需要检查的 key + * + * @description + * 检查指定 key 是否被选中。 + */ + const isKeySelected = (key: RowKey) => { + return keysRef.value.includes(key) + } + effectDispose(() => { clearAll() }) @@ -317,6 +354,9 @@ const useCheckedRowKeys = < clearAll, clearKey, selectKey, + selectKeys, + toggleKey, + isKeySelected, }, ] as const } diff --git a/src/components/base/RTable/src/index.scss b/src/components/base/RTable/src/index.scss index e4e728ba..beacafc6 100644 --- a/src/components/base/RTable/src/index.scss +++ b/src/components/base/RTable/src/index.scss @@ -3,9 +3,9 @@ visibility: visible; } - & .ray-icon { + & .r-icon { &.r-table__c-tool-icon--active { - color: var(--ray-theme-primary-color); + color: var(--r-theme-primary-color); } } } diff --git a/src/components/base/RTable/src/types.ts b/src/components/base/RTable/src/types.ts index b5b4aa40..9466a906 100644 --- a/src/components/base/RTable/src/types.ts +++ b/src/components/base/RTable/src/types.ts @@ -35,6 +35,7 @@ export interface TableProvider { uuidWrapper: string uuidTable: string wrapperRef: Readonly> + tableRef: Readonly> } export interface C extends DataTableBaseColumn { diff --git a/src/components/base/RTransitionComponent/index.ts b/src/components/base/RTransitionComponent/index.ts index 211ccb47..010769b5 100644 --- a/src/components/base/RTransitionComponent/index.ts +++ b/src/components/base/RTransitionComponent/index.ts @@ -1,4 +1,4 @@ -import RTransitionComponent from './src/index.vue' +import RTransitionComponent from './src/TransitionComponent.vue' import transitionComponentProps from './src/props' import type * as RTransitionComponentType from './src/types' diff --git a/src/components/base/RTransitionComponent/src/index.vue b/src/components/base/RTransitionComponent/src/TransitionComponent.vue similarity index 100% rename from src/components/base/RTransitionComponent/src/index.vue rename to src/components/base/RTransitionComponent/src/TransitionComponent.vue diff --git a/src/components/pro/RTablePro/src/TablePro.tsx b/src/components/pro/RTablePro/src/TablePro.tsx index 3b087653..07d9c465 100644 --- a/src/components/pro/RTablePro/src/TablePro.tsx +++ b/src/components/pro/RTablePro/src/TablePro.tsx @@ -9,12 +9,14 @@ import { omit } from 'lodash-es' import type { TablePagination, TableRequestConfig, TableProInst } from './types' import type { RTableInst } from '../../..' +import type { Recordable } from '@/types' export default defineComponent({ name: 'RTablePro', props, setup(props, ctx) { const { expose } = ctx + const [register, tableFns] = useTable() const [ paginationRef, @@ -31,58 +33,53 @@ export default defineComponent({ ] = usePagination(void 0, { prefix: props.paginationPrefix, }) - const tableRequestRef = computed(() => props.request) - - // 获取最新 statistics 和 pagination 值 - const update = (): TablePagination => { - const page = getPage() - const pageSize = getPageSize() - const itemCount = getItemCount() - - return { - page, - pageSize, - itemCount, - } - } + // 获取最新 pagination 值 - 使用 computed 避免函数重新创建 + const tablePagination = computed(() => ({ + page: getPage(), + pageSize: getPageSize(), + itemCount: getItemCount(), + })) // 派发表格更新事件 const emitTableUpdate = () => { const { onTablePaginationUpdate } = props if (onTablePaginationUpdate) { - call(onTablePaginationUpdate, update()) + call(onTablePaginationUpdate, tablePagination.value) } } // 合并请求参数 - const combineRequestParams = (extraConfig?: TableRequestConfig) => { + const combineRequestParams = ( + extraConfig?: TableRequestConfig, + ) => { const config = Object.assign({}, props.requestConfig, extraConfig) - const { formatRangeTime, excludeParams } = config - let params = config.params || {} + const { + formatRangeTime, + excludeParams, + autoDeleteDuplicateKeys = true, + } = config + let params: Recordable = config.params || {} // 转换时间范围,该功能仅支持 NDatePicker range 模式参数 if (formatRangeTime?.length && params) { - formatRangeTime.forEach((curr) => { - const { key, target } = curr + formatRangeTime.forEach(({ key, target }) => { const val = params[key] as [number, number] | null - if (val && target?.length) { - const [start, end] = val - - params[target[0]] = start - params[target[1]] = end - } else { - // 当传递时间参数被清空时,则清空对应 time key - params[key] = null - params[target[0]] = null - params[target[1]] = null + if (val && target?.length === 2) { + params[target[0]] = val[0] + params[target[1]] = val[1] } + + // 清空时间参数的 key + delete params[key] }) } - params = removeDuplicateKeys(params) + if (autoDeleteDuplicateKeys) { + params = removeDuplicateKeys(params) + } // 排除指定的请求参数 if (excludeParams) { @@ -94,10 +91,10 @@ export default defineComponent({ pageSize: getPageSize(), }) - return requestParams + return requestParams as T & { page: number; pageSize: number } } - // 同步执行 request 请求,允许重置 pagination 请求,返回 Promise 对象 + // 同步执行 request 请求,允许重置 pagination 请求 const runResetPaginationRequest: TableProInst['runTableRequest'] = ( extraConfig, reset = true, @@ -108,21 +105,19 @@ export default defineComponent({ const requestParams = combineRequestParams(extraConfig) - tableRequestRef.value?.(requestParams) + props.request?.(requestParams) } // 异步执行 request 请求,允许重置 pagination 请求,返回 Promise 对象 const runResetPaginationRequestAsync: TableProInst['runAsyncTableRequest'] = - (extraConfig, reset = true) => { - return new Promise((resolve, reject) => { - try { - runResetPaginationRequest(extraConfig, reset) + async (extraConfig, reset = true) => { + if (reset) { + resetPagination() + } - resolve(void 0) - } catch (e) { - reject(e) - } - }) + const requestParams = combineRequestParams(extraConfig) + + await props.request?.(requestParams) } watchEffect(() => { @@ -144,7 +139,7 @@ export default defineComponent({ if (onRegister) { call(onRegister, { ...(tableFns as unknown as RTableInst), - getTablePagination: update, + getTablePagination: () => tablePagination.value, runTableRequest: runResetPaginationRequest, runAsyncTableRequest: runResetPaginationRequestAsync, getCurrentTableRequestParams: combineRequestParams, @@ -156,6 +151,7 @@ export default defineComponent({ }) } }) + expose() return { @@ -172,37 +168,23 @@ export default defineComponent({ flexAutoHeight, ...rest } = $props + const { collapse, ...restSlots } = $slots const baseProps = { onRegister: register, pagination: showPagination ? paginationRef : void 0, + flexAutoHeight: takeoverAutoHeight || flexAutoHeight, + ...rest, } - const { collapse, ...restSlots } = $slots if (takeoverAutoHeight) { return ( - - {{ - default: () => ( - <> - {collapse?.()} - - {{ - ...restSlots, - }} - - - ), - }} + + {collapse?.()} + {restSlots} ) - } else { - return ( - - {{ - ...restSlots, - }} - - ) } + + return {restSlots} }, }) diff --git a/src/components/pro/RTablePro/src/hooks/useTablePro.ts b/src/components/pro/RTablePro/src/hooks/useTablePro.ts index 4c04a216..76f627b7 100644 --- a/src/components/pro/RTablePro/src/hooks/useTablePro.ts +++ b/src/components/pro/RTablePro/src/hooks/useTablePro.ts @@ -1,5 +1,3 @@ -import { printDom } from '@/utils' - import type { Recordable } from '@/types' import type { TableProInst, TableRequestConfig } from '../types' import type { @@ -156,16 +154,8 @@ export const useTablePro = () => { * @description * 打印表格。 */ - const print = (options?: PrintDomOptions) => { - const { config } = getTableProInstance() - const { uuidWrapper } = config ?? {} - - if (uuidWrapper) { - const tableWrapperElement = document.getElementById(uuidWrapper) - - printDom(tableWrapperElement, options) - } - } + const print = (options?: PrintDomOptions) => + getTableProInstance().print.call(null, options) /** * diff --git a/src/components/pro/RTablePro/src/types.ts b/src/components/pro/RTablePro/src/types.ts index ebe35010..467f29af 100644 --- a/src/components/pro/RTablePro/src/types.ts +++ b/src/components/pro/RTablePro/src/types.ts @@ -30,7 +30,9 @@ export interface BasePagination { */ export type TablePagination = BasePagination -export type TablePaginationUpdate = (pagination: TablePagination) => void +export type TablePaginationUpdate = ( + pagination: Readonly, +) => void export type PaginationPrefix = UsePaginationOptions['prefix'] @@ -63,6 +65,14 @@ export interface TableRequestConfig< * @default undefined */ excludeParams?: ExcludeParams[] + /** + * + * @description + * 是否自动移除重复的请求参数。 + * + * @default true + */ + autoDeleteDuplicateKeys?: boolean } export type TableProProps = Omit diff --git a/src/hooks/template/useMaximize.ts b/src/hooks/template/useMaximize.ts index ffef198f..457091a9 100644 --- a/src/hooks/template/useMaximize.ts +++ b/src/hooks/template/useMaximize.ts @@ -1,6 +1,5 @@ import { setVariable, getVariableToRefs } from '@/global-variable' import { LAYOUT_CONTENT_REF } from '@/app-config' -import { unrefElement } from '@/utils' import { useElementFullscreen } from '../web' import type { UseElementFullscreenOptions } from '../web' @@ -24,7 +23,13 @@ export interface MaximizeOptions extends UseElementFullscreenOptions { scrollToOptions?: ScrollToOptions } -export const useMaximize = () => { +export const useMaximize = (options?: MaximizeOptions) => { + const contentEl = LAYOUT_CONTENT_REF as Ref + const { enter, exit, toggleFullscreen, isFullscreen } = useElementFullscreen( + contentEl, + options, + ) + /** * * 当前 LayoutContent 是处于否全屏状态 @@ -46,25 +51,30 @@ export const useMaximize = () => { * 该方法仅针对于 LayoutContent 区域,并且依赖全局属性 layoutContentMaximize。 * * @example - * maximize(true, { MaximizeOptions }) 全屏内容区域 - * maximize(false, { MaximizeOptions }) 取消全屏内容区域 + * maximize(true) 全屏内容区域 + * maximize(false) 取消全屏内容区域 */ const maximize = (full: boolean, options?: MaximizeOptions) => { const { scrollToOptions } = options ?? {} - const contentEl = unrefElement(LAYOUT_CONTENT_REF as Ref) - const { toggleFullscreen } = useElementFullscreen(contentEl, options) setVariable('layoutContentMaximize', full) - toggleFullscreen() + + if (full) { + enter() + } else { + exit() + } if (scrollToOptions && full) { - LAYOUT_CONTENT_REF?.value?.scrollTo(scrollToOptions) + contentEl?.value?.scrollTo(scrollToOptions) } } return { isLayoutContentMaximized, + isFullscreen, maximize, + toggleFullscreen, } } diff --git a/src/hooks/template/useSiderBar.ts b/src/hooks/template/useSiderBar.ts index 68ea8b5c..e0c8c7f7 100644 --- a/src/hooks/template/useSiderBar.ts +++ b/src/hooks/template/useSiderBar.ts @@ -12,7 +12,8 @@ export type CloseMenuTag = Key | MenuTagOptions * @param target 标签页对象、索引、key * @param fn 触发函数 * - * 该方法用于统一获取目标标签页方法 + * @description + * 该方法用于统一获取目标标签页方法。 */ const normalMenuTagOption = (target: CloseMenuTag, fn: string) => { const { getMenuTagOptions } = useMenuGetters() @@ -82,27 +83,14 @@ export function useSiderBar() { resolveOption, } = useMenuActions() - /** - * - * @remark 获取当前激活标签页索引位置 - */ + // 获取当前激活标签页索引位置 const getCurrentTagIndex = () => { return getMenuTagOptions.value.findIndex( (curr) => curr.fullPath === getMenuKey.value, ) } - /** - * - * @param target 当前关闭项 - * - * 传递参数类型情况: - * - number: 关闭当前项索引 - * - string: 关闭当前项 key,其实 key 也是一个具体的页面 url 地址 - * - AppMenuOption: 关闭当前项 - * - * @remark 校验指定标签右侧是否有可关闭的标签 - */ + // 校验指定标签右侧是否有可关闭的标签 const checkCloseRight = (target: CloseMenuTag) => { const normal = normalMenuTagOption(target, 'checkCloseRight') @@ -116,17 +104,7 @@ export function useSiderBar() { return false } - /** - * - * @param target 当前关闭项 - * - * 传递参数类型情况: - * - number: 关闭当前项索引 - * - string: 关闭当前项 key,其实 key 也是一个具体的页面 url 地址 - * - AppMenuOption: 关闭当前项 - * - * @remark 校验指定标签左侧是否有可关闭的标签 - */ + // 校验指定标签左侧是否有可关闭的标签 const checkCloseLeft = (target: CloseMenuTag) => { const normal = normalMenuTagOption(target, 'checkCloseRight') @@ -148,15 +126,7 @@ export function useSiderBar() { return false } - /** - * - * @param target 当前关闭项 - * - * 传递参数类型情况: - * - number: 关闭当前项索引 - * - string: 关闭当前项 key,其实 key 也是一个具体的页面 url 地址 - * - AppMenuOption: 关闭当前项 - */ + // 关闭当前标签 const close = (target: CloseMenuTag) => { const normal = normalMenuTagOption(target, 'close') @@ -187,10 +157,7 @@ export function useSiderBar() { } } - /** - * - * 关闭所有标签并且导航至 root path - */ + // 关闭所有标签并且导航至 root path const closeAll = () => { spliceMenTagOptions(0, getMenuTagOptions.value.length) @@ -217,18 +184,7 @@ export function useSiderBar() { } } - /** - * - * @param target 目标标签页 - * - * 关闭以当前项为索引的右侧标签 - * 如果当前选择标签与 menuKey 不匹配并且包含了当前激活标签页,则会关闭当前标签右侧所有变迁并且跳转至该页面 - * - * 传递参数类型情况: - * - number: 关闭当前项索引 - * - string: 关闭当前项 key,其实 key 也是一个具体的页面 url 地址 - * - AppMenuOption: 关闭当前项 - */ + // 目标标签页右侧所有标签页 const closeRight = (target: CloseMenuTag) => { const normal = normalMenuTagOption(target, 'closeRight') @@ -247,18 +203,7 @@ export function useSiderBar() { } } - /** - * - * @param target 目标标签页 - * - * 关闭以当前项左侧所有标签 - * 如果当前选择标签与 menuKey 不匹配并且包含了当前激活标签页,则会关闭当前标签左侧所有变迁并且跳转至该页面 - * - * 传递参数类型情况: - * - number: 关闭当前项索引 - * - string: 关闭当前项 key,其实 key 也是一个具体的页面 url 地址 - * - AppMenuOption: 关闭当前项 - */ + // 关闭目标标签页左侧所有标签页 const closeLeft = (target: CloseMenuTag) => { const normal = normalMenuTagOption(target, 'closeLeft') @@ -276,17 +221,7 @@ export function useSiderBar() { } } - /** - * - * @param target 目标标签页 - * - * 会关闭除了当前索引的所有菜单项 - * - * 传递参数类型情况: - * - number: 关闭当前项索引 - * - string: 关闭当前项 key,其实 key 也是一个具体的页面 url 地址 - * - AppMenuOption: 关闭当前项 - */ + // 关闭除了当前索引的所有菜单项 const closeOther = (target: CloseMenuTag) => { const normal = normalMenuTagOption(target, 'closeOther') diff --git a/src/hooks/web/useElementFullscreen.ts b/src/hooks/web/useElementFullscreen.ts index 366ed42e..d25e6100 100644 --- a/src/hooks/web/useElementFullscreen.ts +++ b/src/hooks/web/useElementFullscreen.ts @@ -1,4 +1,5 @@ -import { unrefElement, effectDispose, isValueType, setStyle } from '@/utils' +import { unrefElement, effectDispose, isValueType } from '@/utils' +import { pick } from 'lodash-es' import type { BasicTarget } from '@/types' @@ -61,10 +62,9 @@ export interface UseElementFullscreenOptions { transition?: string } -let currentZIndex = 999 -let isAppend = false -const ID_TAG = 'ELEMENT-FULLSCREEN-RAY' -const styleElement = document.createElement('style') +// 全局 z-index 管理 +let globalZIndex = 999 +const ID_TAG = 'data-element-fullscreen' /** * @@ -101,141 +101,163 @@ export const useElementFullscreen = ( exit: _exit, backgroundColor, zIndex, - transition = 'transform 0.3s var(--r-bezier)', + transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', } = options ?? {} - let isSetup = false - const catchBoundingClientRect: { - x: number | null - y: number | null - } = { - x: null, - y: null, - } - // 使用 ref 来追踪状态 - const isFullscreen = ref(false) - const updateStyle = () => { + // 每个实例独立的状态 + const isFullscreen = ref(false) + const instanceZIndex = ref(0) + const originalStyles = { + position: '', + width: '', + height: '', + top: '', + left: '', + transform: '', + transition: '', + zIndex: '', + backgroundColor: '', + } + + const enter = () => { const element = unrefElement(target) as HTMLElement | null if (!element) { return } - const { left, top } = element.getBoundingClientRect() - - if ( - catchBoundingClientRect.x === null && - catchBoundingClientRect.y === null - ) { - catchBoundingClientRect.x = -left - catchBoundingClientRect.y = -top - } - - setStyle(document.body, { - '--element-fullscreen-z-index': - isValueType(zIndex, 'Null') || - isValueType(zIndex, 'Undefined') - ? currentZIndex - : zIndex, - '--element-fullscreen-transition': transition, - '--element-fullscreen-background-color': backgroundColor, - '--element-fullscreen-width': 'var(--html-width)', - '--element-fullscreen-height': 'var(--html-height)', - '--element-fullscreen-transform-x': `${catchBoundingClientRect.x}px`, - '--element-fullscreen-transform-y': `${catchBoundingClientRect.y}px`, - }) - - const cssContent = ` - [${ID_TAG}] { - position: fixed; - width: var(--element-fullscreen-width) !important; - height: var(--element-fullscreen-height) !important; - transform: translate(var(--element-fullscreen-transform-x), var(--element-fullscreen-transform-y)) !important; - transition: var(--element-fullscreen-transition); - z-index: var(--element-fullscreen-z-index) !important; - background-color: var(--element-fullscreen-background-color); - } - `.trim() - - styleElement.innerHTML = cssContent - - // 避免重复添加 style 标签 - if (!isAppend) { - document.head.appendChild(styleElement) - } - } - - const enter = () => { - const element = unrefElement(target) as HTMLElement | null - beforeEnter?.() - if (element) { - if (!element.getAttribute(ID_TAG)) { - element.setAttribute(ID_TAG, ID_TAG) - } + // 保存原始样式(只在第一次进入时保存) + if (!isFullscreen.value) { + const o = pick(element.style, [ + 'position', + 'width', + 'height', + 'top', + 'left', + 'transform', + 'transition', + 'zIndex', + 'backgroundColor', + ]) - if (!isSetup) { - isSetup = true - currentZIndex += 1 - } - - if (!isAppend) { - updateStyle() - - isAppend = true - } - - element.style.transition = transition - isFullscreen.value = true - - _enter?.() + Object.assign(originalStyles, o) } + + // 获取当前位置 + const { top, left, width, height } = element.getBoundingClientRect() + + // 分配 z-index + if (instanceZIndex.value === 0) { + if ( + isValueType(zIndex, 'Null') || + isValueType(zIndex, 'Undefined') + ) { + globalZIndex += 1 + instanceZIndex.value = globalZIndex + } else { + instanceZIndex.value = zIndex + } + } + + // 设置全屏样式 + element.setAttribute(ID_TAG, 'true') + + // 设置初始位置(不设置 transition) + element.style.position = 'fixed' + element.style.top = `${top}px` + element.style.left = `${left}px` + element.style.width = `${width}px` + element.style.height = `${height}px` + element.style.transform = 'translate(0, 0)' + element.style.zIndex = String(instanceZIndex.value) + + if (backgroundColor) { + element.style.backgroundColor = backgroundColor + } + + // 强制重排 + void element.offsetHeight + + // 添加 transition + element.style.transition = transition + + // 使用 requestAnimationFrame 应用最终样式 + requestAnimationFrame(() => { + element.style.top = '0px' + element.style.left = '0px' + element.style.width = '100vw' + element.style.height = '100vh' + }) + + // 先更新状态 + isFullscreen.value = true + + _enter?.() } const exit = () => { - beforeExit?.() + const element = unrefElement(target) as HTMLElement | null - const element = unrefElement(target) - - if (element) { - element.removeAttribute(ID_TAG) + if (!element) { + return } + beforeExit?.() + + // 先更新状态 isFullscreen.value = false + // 恢复原始样式 + element.removeAttribute(ID_TAG) + + // 样式属性映射表(camelCase -> kebab-case) + const styleMap: Record = { + position: 'position', + width: 'width', + height: 'height', + top: 'top', + left: 'left', + transform: 'transform', + transition: 'transition', + zIndex: 'z-index', + backgroundColor: 'background-color', + } + + // 批量恢复样式 + Object.entries(originalStyles).forEach(([key, value]) => { + const styleName = styleMap[key as keyof typeof originalStyles] + + if (!value) { + element.style.removeProperty(styleName) + } else { + element.style.setProperty(styleName, value) + } + }) + _exit?.() } const toggleFullscreen = () => { - const element = unrefElement(target) - - if (element) { - if (element.getAttribute(ID_TAG)) { - exit() - } else { - enter() - } + if (isFullscreen.value) { + exit() + } else { + enter() } } effectDispose(() => { - const element = unrefElement(target) as HTMLElement | null - - if (element) { - element.removeAttribute(ID_TAG) + if (isFullscreen.value) { + exit() } - - // 回滚 z-index 值,避免无限增加 - currentZIndex = Math.max(999, currentZIndex - 1) // 防止 zIndex 小于初始值 - isFullscreen.value = false }) return { enter, exit, toggleFullscreen, - isFullscreen: readonly(isFullscreen), // 暴露只读状态 + isFullscreen: readonly(isFullscreen), } } diff --git a/src/hooks/web/usePagination.ts b/src/hooks/web/usePagination.ts index bb637fff..49074166 100644 --- a/src/hooks/web/usePagination.ts +++ b/src/hooks/web/usePagination.ts @@ -48,26 +48,19 @@ const DEFAULT_OPTIONS: UsePaginationOptions = { * @param options 配置项 * * @description - * 便捷分页 hook。 + * Naive UI Pagination 便捷分页 hook。 + * + * @example + * const [paginationRef, { getItemCount, setItemCount, getPage, setPage, getPageSize, setPageSize, getPagination, getCallback, setCallback, resetPagination }] = usePagination(() => { + * // do something... + * }, { + * // ...options + * }) */ export const usePagination = ( callback?: T, options?: UsePaginationOptions, ) => { - // 配置合并 - const mergedOptions = computed(() => ({ - ...DEFAULT_OPTIONS, - ...omit(options, [ - 'on-update:page', - 'on-update:page-size', - 'onUpdatePage', - 'onUpdatePageSize', - 'onUpdate:page', - 'onUpdate:page-size', - 'onUpdate:pageSize', - ]), - ...paginationMethods, - })) // 回调函数 const callbackRef = shallowRef(callback) // 分页方法 @@ -90,6 +83,20 @@ export const usePagination = ( pageSizeChange?.(pageSize) }, } + // 配置合并 + const mergedOptions = computed(() => ({ + ...DEFAULT_OPTIONS, + ...omit(options, [ + 'on-update:page', + 'on-update:page-size', + 'onUpdatePage', + 'onUpdatePageSize', + 'onUpdate:page', + 'onUpdate:page-size', + 'onUpdate:pageSize', + ]), + ...paginationMethods, + })) // 分页配置 const paginationRef = ref(mergedOptions.value) @@ -171,7 +178,12 @@ export const usePagination = ( * @description * 获取回调函数。 */ - const getCallback = callbackRef.value as T + const getCallback = (): Fn extends + | undefined + | null + | unknown + ? T + : Fn => callbackRef.value /** * @@ -183,7 +195,7 @@ export const usePagination = ( * @example * setCallback(() => {}) */ - const setCallback = (callback: AnyFC) => { + const setCallback = (callback: Fn) => { callbackRef.value = callback } diff --git a/src/layout/components/MenuTag/index.scss b/src/layout/components/MenuTag/index.scss index 8dad0aef..5d761a17 100644 --- a/src/layout/components/MenuTag/index.scss +++ b/src/layout/components/MenuTag/index.scss @@ -13,10 +13,6 @@ $menuTagWrapperWidth: 76px; & .menu-tag-wrapper { width: calc(100% - $space * 2 - $menuTagWrapperWidth); } - - & .ray-icon { - cursor: pointer; - } } & .n-tag { @@ -51,15 +47,14 @@ $menuTagWrapperWidth: 76px; } } -.ray-template--light { - .menu-tag__btn-icon:hover { - filter: brightness(1.2); - } -} - +.ray-template--light, .ray-template--dark { - .menu-tag__btn-icon:hover { - filter: brightness(0.8); + .menu-tag__btn .menu-tag__btn-icon { + border-radius: 2px; + + &:hover { + background-color: var(--n-color-hover); + } } } diff --git a/src/layout/components/MenuTag/index.tsx b/src/layout/components/MenuTag/index.tsx index 0d5143a9..eea5b3a7 100644 --- a/src/layout/components/MenuTag/index.tsx +++ b/src/layout/components/MenuTag/index.tsx @@ -21,14 +21,7 @@ import './index.scss' -import { - NScrollbar, - NFlex, - NLayoutHeader, - NDropdown, - NButton, - NIcon, -} from 'naive-ui' +import { NScrollbar, NFlex, NLayoutHeader, NDropdown, NButton } from 'naive-ui' import { RIcon, RMoreDropdown } from '@/components' import { useMenuGetters, useMenuActions } from '@/store' @@ -451,7 +444,7 @@ export default defineComponent({ show={this.actionState.actionDropdownShow} trigger="manual" placement="bottom-start" - onSelect={actionDropdownSelect.bind(this)} + onSelect={actionDropdownSelect} onClickoutside={() => { if (!isMouseInMenuTag) { this.actionState.actionDropdownShow = false @@ -544,6 +537,7 @@ export default defineComponent({ {...{ onMousedown: closeCurrentMenuTag.bind(this, idx), }} + cursor="pointer" /> ), }} diff --git a/src/layout/components/Search/GlobalSearch/index.scss b/src/layout/components/Search/GlobalSearch/index.scss index 5e4d33fe..39021e13 100644 --- a/src/layout/components/Search/GlobalSearch/index.scss +++ b/src/layout/components/Search/GlobalSearch/index.scss @@ -14,8 +14,8 @@ $globalSearchWidth: 650px; border-radius: 6px; min-width: 560px; - & .ray-icon { - color: var(--ray-theme-primary-color); + & .r-icon { + color: var(--r-theme-primary-color); } & .n-card__action { @@ -59,7 +59,7 @@ $globalSearchWidth: 650px; .n-flex.global-search__card-content { & .content-item.content-item--active, & .content-item:hover { - background-color: var(--ray-theme-primary-fade-color); + background-color: var(--r-theme-primary-fade-color); } & .content-item { diff --git a/src/layout/default/ContentWrapper/index.scss b/src/layout/default/ContentWrapper/index.scss index e3441433..66c49f89 100644 --- a/src/layout/default/ContentWrapper/index.scss +++ b/src/layout/default/ContentWrapper/index.scss @@ -20,7 +20,7 @@ color 0.3s var(--r-bezier), background-color 0.3s var(--r-bezier); - & .ray-icon { + & .r-icon { transform: translate(-14px, 14px); } } diff --git a/src/styles/naive.scss b/src/styles/naive.scss index b72ceee2..169368c7 100644 --- a/src/styles/naive.scss +++ b/src/styles/naive.scss @@ -9,7 +9,7 @@ .r-menu--app:not(.n-menu--collapsed) .n-menu-item-content.n-menu-item-content--selected:before, .r-menu--app:not(.n-menu--collapsed) .n-menu-item-content:hover:before { - border-left: 4px solid var(--ray-theme-primary-color); + border-left: 4px solid var(--r-theme-primary-color); transition: border-left 0.1s; } diff --git a/src/views/demo/barcode-demo.tsx b/src/views/demo/barcode-demo.tsx index 1ad93d86..9251d997 100644 --- a/src/views/demo/barcode-demo.tsx +++ b/src/views/demo/barcode-demo.tsx @@ -39,12 +39,13 @@ export default defineComponent({ - + diff --git a/src/views/demo/table-pro-demo.tsx b/src/views/demo/table-pro-demo.tsx index d0cb8526..20a49e67 100644 --- a/src/views/demo/table-pro-demo.tsx +++ b/src/views/demo/table-pro-demo.tsx @@ -12,6 +12,7 @@ import { NSelect, NDatePicker, NText, + useDialog, } from 'naive-ui' import { RIcon } from '@/components' @@ -98,6 +99,7 @@ export default defineComponent({ name: 'TableProDemo', setup() { const { format } = useDayjs() + const { info: infoDialog } = useDialog() const [register, { formModel, reset }] = useForm({ RangeTime: null, @@ -312,6 +314,7 @@ export default defineComponent({ setPage, setPageSize, resetTablePagination, + infoDialog, } }, render() { @@ -338,6 +341,7 @@ export default defineComponent({ setPage, setPageSize, resetTablePagination, + infoDialog, } = this return ( @@ -428,7 +432,20 @@ export default defineComponent({ print()}> 打印 - downloadCsv()}> + { + infoDialog({ + title: '下载 CSV', + content: '下载 CSV', + positiveText: '下载', + negativeText: '取消', + onPositiveClick: () => { + downloadCsv() + }, + }) + }} + > 下载 csv setPage(2)}> diff --git a/src/views/demo/template-hooks/index.tsx b/src/views/demo/template-hooks/index.tsx index fe0dc310..c7755a35 100644 --- a/src/views/demo/template-hooks/index.tsx +++ b/src/views/demo/template-hooks/index.tsx @@ -203,7 +203,7 @@ export default defineComponent({ maximize(!this.maximizeRef, { scrollToOptions: { left: 0, - top: 0, + top: -9999, }, }) }} diff --git a/unplugin/components.d.ts b/unplugin/components.d.ts index bd553145..e181ea86 100644 --- a/unplugin/components.d.ts +++ b/unplugin/components.d.ts @@ -10,5 +10,6 @@ declare module 'vue' { RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] Src: typeof import('./../src/components/base/RTransitionComponent/src/index.vue')['default'] + TransitionComponent: typeof import('./../src/components/base/RTransitionComponent/src/TransitionComponent.vue')['default'] } } diff --git a/vite.plugin.config.ts b/vite.plugin.config.ts index 30759f3b..566a3e79 100644 --- a/vite.plugin.config.ts +++ b/vite.plugin.config.ts @@ -191,7 +191,12 @@ function baseOptions(mode: string): PluginOption[] { preloadingConfig, appPrimaryColor, }), - viteInspect(), // 仅适用于开发模式(检查 `Vite` 插件的中间状态) + // 仅适用于开发模式(检查 `Vite` 插件的中间状态) + mode === 'development' + ? viteInspect({ + enabled: true, + }) + : null, mockDevServerPlugin({ include: ['mock/**/*.mock.ts'], exclude: [