version: v5.2.4

This commit is contained in:
XiaoDaiGua-Ray 2025-11-09 18:07:37 +08:00
parent bebd3d53bd
commit d1cedda1f7
57 changed files with 1205 additions and 689 deletions

62
.cursorrules Normal file
View File

@ -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开发最佳实践。

View File

@ -17,4 +17,5 @@ module.exports = {
htmlWhitespaceSensitivity: 'css', // 根据显示样式决定 `html` 要不要折行
endOfLine: 'lf', // 换行符使用 `lf`,
singleAttributePerLine: false,
bracketSameLine: false,
}

View File

@ -1,3 +1,3 @@
{
"recommendations": ["vue.volar", "lokalise.i18n-ally"]
"recommendations": ["lokalise.i18n-ally"]
}

33
.vscode/settings.json vendored
View File

@ -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,

View File

@ -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 默认主题色,现在会以当前主题色为主色

View File

@ -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',

View File

@ -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;
}

View File

@ -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",

303
pnpm-lock.yaml generated
View File

@ -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: {}

View File

@ -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',

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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<string, AbortController>
constructor() {
this.pendingRequest = new Map<string, AbortController>()
}
/** 待处理请求的 Mapkey 为请求标识value 为 AbortController */
private readonly pendingRequest = new Map<string, AbortController>()
/**
*
* @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)
}
}

View File

@ -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<AxiosError<unknown, unknown>> = {
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<unknown, unknown>,
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<typeof useAxiosInterceptor>
export type AxiosInterceptor = ReturnType<typeof axiosInterceptor>

View File

@ -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<typeof barcodeProps>
// 扩展 BarcodeProps 以提供更好的类型提示
export type BarcodeProps = Omit<
ExtractPublicPropTypes<typeof barcodeProps>,
'width' | 'height'
> & {
width?: RBarcodeSize
height?: RBarcodeSize
}
export { RBarcode, barcodeProps }
export type {
RBarcodeSize,
RBarcodeRender,
RBarcodeFormat,
RBarcodeOptions,
} from './src/types'

View File

@ -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<HTMLCanvasElement | HTMLOrSVGElement>(
'barcodeRef',
)
const containerRef = useTemplateRef<HTMLDivElement>('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' ? (
<canvas class={c} style={cssVars} ref="barcodeRef" />
) : (
<svg class={c} style={cssVars} ref="barcodeRef" />
)
return (
<NSpin class="r-barcode-spin" show={loading}>
{barcodeRender === 'canvas' ? (
<canvas class={c} style={cssVars} ref="barcodeRef" />
{responsive ? (
<div class="r-barcode-container" ref="containerRef">
{barcodeElement}
</div>
) : (
<svg class={c} style={cssVars} ref="barcodeRef" />
barcodeElement
)}
</NSpin>
)

View File

@ -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;
}

View File

@ -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<string | number>,
default: 'auto',
type: [String, Number] as PropType<RBarcodeSize>,
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<string | number>,
default: 'auto',
type: [String, Number] as PropType<RBarcodeSize>,
default: 'auto' as const,
},
/**
*
@ -132,6 +153,19 @@ const props = {
onFinally: {
type: [Function, Array] as PropType<MaybeArray<() => void>>,
},
/**
*
* @description
*
*
* width height
*
* @default false
*/
responsive: {
type: Boolean,
default: false,
},
} as const
export default props

View File

@ -24,3 +24,6 @@ export type RBarcodeFormat =
| 'MSI1110'
| 'pharmacode'
| 'codabar'
// 使用模板字面量类型来保留字面量提示
export type RBarcodeSize = number | 'auto' | (string & {})

View File

@ -1,4 +1,4 @@
import RChart from './src'
import RChart from './src/Chart'
import chartProps from './src/props'
import useChart from './src/hooks/useChart'

View File

@ -88,7 +88,7 @@ export default defineComponent({
setup(props, { expose }) {
const { getAppTheme } = useSettingGetters()
// echart 容器实例
const rayChartRef = useTemplateRef<HTMLElement>('rayChartRef')
const chartRef = useTemplateRef<HTMLElement>('chartRef')
// echart 父容器实例
const rayChartWrapperRef = useTemplateRef<HTMLElement>('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(
<div class="ray-chart__container" ref="rayChartRef"></div>,
<div class="ray-chart__container" ref="chartRef"></div>,
),
header: renderNode(title, {
defaultElement: <div style="display: none;"></div>,
@ -557,7 +563,7 @@ export default defineComponent({
</NCard>
) : (
<div class="ray-chart" style={[this.cssVarsRef]} ref="rayChartWrapperRef">
<div class="ray-chart__container" ref="rayChartRef"></div>
<div class="ray-chart__container" ref="chartRef"></div>
</div>
)
},

View File

@ -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;

View File

@ -341,6 +341,17 @@ const props = {
type: Number,
default: 500,
},
/**
*
* @description
* options
*
* @default true
*/
watchDeep: {
type: Boolean,
default: true,
},
/**
*
* @description

View File

@ -1,4 +1,4 @@
import RIcon from './src'
import RIcon from './src/Icon'
import iconProps from './src/props'
import type { ExtractPublicPropTypes } from 'vue'

View File

@ -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 (
<span
class={['ray-icon', this.customClassName]}
class={['r-icon', this.customClassName]}
style={[this.cssVars]}
onClick={this.iconClick.bind(this)}
onClick={this.iconClick}
>
<svg
{...({
RayIconAttribute: 'ray-icon',
RIconAttribute: 'r-icon',
ariaHidden: true,
} as object)}
>

View File

@ -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;
}

View File

@ -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'

View File

@ -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

View File

@ -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%;

View File

@ -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()

View File

@ -1,4 +1,4 @@
import RMoreDropdown from './src'
import RMoreDropdown from './src/MoreDropdown'
import moreDropdownProps from './src/props'
import type { ExtractPublicPropTypes } from 'vue'

View File

@ -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>('rTableInst')
const wrapperRef = useTemplateRef<HTMLElement>('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<string, unknown>, 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 (
<NFlex align="center">
<Print {...p} />
<Size {...p} onChangeSize={changeTableSize.bind(this)} />
<Print {...props} />
<Size {...props} onChangeSize={changeTableSize} />
<Fullscreen />
<C {...p} onUpdateColumn={updateTableColumn.bind(this)} />
{needSettingComponent ? (
<C {...props} onUpdateColumn={updateTableColumn} />
) : null}
<TablePropsSelect
{...p}
onPopselectChange={popselectChange.bind(this)}
onInitialed={popselectChange.bind(this)}
{...props}
onPopselectChange={popselectChange}
onInitialed={popselectChange}
/>
</NFlex>
)
}
if (!props.toolOptions) {
return renderDefaultToolOptions
} else {
if (props.coverTool) {
return () => <NFlex align="center">{renderToolOptions()}</NFlex>
} else {
return () => (
<NFlex align="center">
{renderDefaultToolOptions()}
{renderToolOptions()}
</NFlex>
)
}
/**
*
* @param p props
*
* @description
* toolOptions toolOptions
*/
const tool = (p: typeof props) => {
if (!p.tool) {
return
}
if (!p.toolOptions) {
return renderDefaultToolOptions
}
if (p.coverTool) {
return () => <NFlex align="center">{renderToolOptions()}</NFlex>
}
return () => (
<NFlex align="center">
{renderDefaultToolOptions()}
{renderToolOptions()}
</NFlex>
)
}
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() {

View File

@ -177,7 +177,10 @@ export default defineComponent({
const { onUpdateColumn } = props
if (onUpdateColumn) {
call(onUpdateColumn, options)
// 使用 nextTick 确保 DOM 更新后再触发事件
nextTick(() => {
call(onUpdateColumn, options)
})
}
}

View File

@ -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
}

View File

@ -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);
}
}
}

View File

@ -35,6 +35,7 @@ export interface TableProvider {
uuidWrapper: string
uuidTable: string
wrapperRef: Readonly<ShallowRef<HTMLElement | null>>
tableRef: Readonly<ShallowRef<RTableInst | null>>
}
export interface C extends DataTableBaseColumn {

View File

@ -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'

View File

@ -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<TablePagination>(() => ({
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 = <T extends Recordable>(
extraConfig?: TableRequestConfig<T>,
) => {
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 (
<NFlex vertical class="flex-vertical">
{{
default: () => (
<>
{collapse?.()}
<RTable {...baseProps} {...rest} flexAutoHeight>
{{
...restSlots,
}}
</RTable>
</>
),
}}
<NFlex vertical class="h-full">
{collapse?.()}
<RTable {...baseProps}>{restSlots}</RTable>
</NFlex>
)
} else {
return (
<RTable {...baseProps} {...rest} flexAutoHeight={flexAutoHeight}>
{{
...restSlots,
}}
</RTable>
)
}
return <RTable {...baseProps}>{restSlots}</RTable>
},
})

View File

@ -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)
/**
*

View File

@ -30,7 +30,9 @@ export interface BasePagination {
*/
export type TablePagination = BasePagination
export type TablePaginationUpdate = (pagination: TablePagination) => void
export type TablePaginationUpdate = (
pagination: Readonly<TablePagination>,
) => 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<TableProps, 'pagination'>

View File

@ -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<HTMLElement>
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<HTMLElement>)
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,
}
}

View File

@ -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')

View File

@ -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<null>(zIndex, 'Null') ||
isValueType<undefined>(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<null>(zIndex, 'Null') ||
isValueType<undefined>(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<keyof typeof originalStyles, string> = {
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),
}
}

View File

@ -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 = <T extends AnyFC>(
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 = <T extends AnyFC>(
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<PaginationProps>(mergedOptions.value)
@ -171,7 +178,12 @@ export const usePagination = <T extends AnyFC>(
* @description
*
*/
const getCallback = callbackRef.value as T
const getCallback = <Fn extends AnyFC = T>(): Fn extends
| undefined
| null
| unknown
? T
: Fn => callbackRef.value
/**
*
@ -183,7 +195,7 @@ export const usePagination = <T extends AnyFC>(
* @example
* setCallback(() => {})
*/
const setCallback = (callback: AnyFC) => {
const setCallback = <Fn extends AnyFC = AnyFC>(callback: Fn) => {
callbackRef.value = callback
}

View File

@ -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);
}
}
}

View File

@ -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"
/>
),
}}

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -39,12 +39,13 @@ export default defineComponent({
</NCard>
</NGridItem>
<NGridItem span={1}>
<NCard title="基础条形码">
<NCard title="根据尺寸自动更新的条形码">
<RBarcode
text="RayTemplate"
options={{
...baseOptions,
}}
responsive
/>
</NCard>
</NGridItem>

View File

@ -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<ParamsRef>({
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({
<NButton type="primary" onClick={() => print()}>
</NButton>
<NButton type="primary" onClick={() => downloadCsv()}>
<NButton
type="primary"
onClick={() => {
infoDialog({
title: '下载 CSV',
content: '下载 CSV',
positiveText: '下载',
negativeText: '取消',
onPositiveClick: () => {
downloadCsv()
},
})
}}
>
csv
</NButton>
<NButton type="primary" onClick={() => setPage(2)}>

View File

@ -203,7 +203,7 @@ export default defineComponent({
maximize(!this.maximizeRef, {
scrollToOptions: {
left: 0,
top: 0,
top: -9999,
},
})
}}

View File

@ -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']
}
}

View File

@ -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: [