diff --git a/apps/vben5/.changeset/.prettierrc.json b/apps/vben5/.changeset/.prettierrc.json
new file mode 100644
index 000000000..544138be4
--- /dev/null
+++ b/apps/vben5/.changeset/.prettierrc.json
@@ -0,0 +1,3 @@
+{
+ "singleQuote": true
+}
diff --git a/apps/vben5/.changeset/fancy-ears-walk.md b/apps/vben5/.changeset/fancy-ears-walk.md
new file mode 100644
index 000000000..f73cb596b
--- /dev/null
+++ b/apps/vben5/.changeset/fancy-ears-walk.md
@@ -0,0 +1,7 @@
+---
+'@vben/styles': patch
+'@vben-core/form-ui': patch
+'@vben/web-naive': patch
+---
+
+feat(@core/form-ui): 新增 useVbenForm 数组编辑器 VbenFormFieldArray
diff --git a/apps/vben5/.gitignore b/apps/vben5/.gitignore
index 8cb16e7e0..12c3e492a 100644
--- a/apps/vben5/.gitignore
+++ b/apps/vben5/.gitignore
@@ -23,7 +23,8 @@ pnpm-lock.yaml
package-lock.json
.VSCodeCounter
**/backend-mock/data
-
+.omx
+.pnpm-store
# local env files
.env.local
.env.*.local
@@ -51,3 +52,12 @@ vite.config.ts.*
*.sw?
.history
.cursor
+
+# AI
+.agent
+.agents
+.claude
+.codex
+skills-lock.json
+.atomcode
+datalog
diff --git a/apps/vben5/.node-version b/apps/vben5/.node-version
index 85e502778..b832e4001 100644
--- a/apps/vben5/.node-version
+++ b/apps/vben5/.node-version
@@ -1 +1 @@
-22.22.0
+24.16.0
diff --git a/apps/vben5/.npmrc b/apps/vben5/.npmrc
index aeac1ae91..7549542d7 100644
--- a/apps/vben5/.npmrc
+++ b/apps/vben5/.npmrc
@@ -1,13 +1 @@
registry=https://registry.npmmirror.com
-public-hoist-pattern[]=lefthook
-public-hoist-pattern[]=eslint
-public-hoist-pattern[]=prettier
-public-hoist-pattern[]=prettier-plugin-tailwindcss
-public-hoist-pattern[]=stylelint
-public-hoist-pattern[]=*postcss*
-public-hoist-pattern[]=@commitlint/*
-public-hoist-pattern[]=czg
-
-strict-peer-dependencies=false
-auto-install-peers=true
-dedupe-peer-dependents=true
diff --git a/apps/vben5/.prettierignore b/apps/vben5/.prettierignore
deleted file mode 100644
index 7c572fd66..000000000
--- a/apps/vben5/.prettierignore
+++ /dev/null
@@ -1,20 +0,0 @@
-dist
-dev-dist
-.local
-.output.js
-node_modules
-.nvmrc
-coverage
-CODEOWNERS
-.nitro
-.output
-
-
-**/*.svg
-**/*.sh
-
-public
-.npmrc
-*-lock.yaml
-
-packages/@abp/components
diff --git a/apps/vben5/.prettierrc.mjs b/apps/vben5/.prettierrc.mjs
deleted file mode 100644
index 3e25d2cfa..000000000
--- a/apps/vben5/.prettierrc.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@vben/prettier-config';
diff --git a/apps/vben5/.stylelintignore b/apps/vben5/.stylelintignore
index 339bcf6c2..3adb33b22 100644
--- a/apps/vben5/.stylelintignore
+++ b/apps/vben5/.stylelintignore
@@ -2,4 +2,7 @@ dist
public
__tests__
coverage
-packages/@abp/components
+.codex
+.claude
+.agent
+.agents
diff --git a/apps/vben5/.vscode/extensions.json b/apps/vben5/.vscode/extensions.json
index e8dc9ed9b..d4e28662b 100644
--- a/apps/vben5/.vscode/extensions.json
+++ b/apps/vben5/.vscode/extensions.json
@@ -2,14 +2,18 @@
"recommendations": [
// Vue 3 的语言支持
"Vue.volar",
- // 将 ESLint JavaScript 集成到 VS Code 中。
+ // 将 eslint 集成到 VS Code 中。
"dbaeumer.vscode-eslint",
+ // 将 oxlint 集成到 VS Code 中。
+ "oxc.oxc-vscode",
// Visual Studio Code 的官方 Stylelint 扩展
"stylelint.vscode-stylelint",
- // 使用 Prettier 的代码格式化程序
- "esbenp.prettier-vscode",
+ // 使用 oxfmt 的代码格式化程序
+ "oxc.oxc-vscode",
// 支持 dotenv 文件语法
"mikestead.dotenv",
+ // YAML 语言支持,供 ESLint 校验 pnpm-workspace.yaml 等文件
+ "redhat.vscode-yaml",
// 源代码的拼写检查器
"streetsidesoftware.code-spell-checker",
// Tailwind CSS 的官方 VS Code 插件
diff --git a/apps/vben5/.vscode/settings.json b/apps/vben5/.vscode/settings.json
index 8da37dc96..993b52039 100644
--- a/apps/vben5/.vscode/settings.json
+++ b/apps/vben5/.vscode/settings.json
@@ -1,5 +1,6 @@
{
- "tailwindCSS.experimental.configFile": "internal/tailwind-config/src/index.ts",
+ "tailwindCSS.experimental.configFile": "internal/tailwind-config/src/theme.css",
+ "tailwindCSS.lint.suggestCanonicalClasses": "ignore",
// workbench
"workbench.list.smoothScrolling": true,
"workbench.startupEditor": "newUntitledFile",
@@ -31,39 +32,51 @@
"editor.autoClosingOvertype": "always",
"editor.autoClosingQuotes": "beforeWhitespace",
"editor.wordSeparators": "`~!@#%^&*()=+[{]}\\|;:'\",.<>/?",
+ "editor.quickSuggestions": {
+ "strings": "on"
+ },
+
+ // lint && format
+ "oxc.enable": true,
+ "oxc.typeAware": true,
+ "oxc.configPath": "oxlint.config.ts",
+ "oxc.fmt.configPath": "oxfmt.config.ts",
+ "eslint.useFlatConfig": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
+ "source.fixAll.oxc": "explicit",
"source.fixAll.stylelint": "explicit",
"source.organizeImports": "never"
},
- "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.defaultFormatter": "oxc.oxc-vscode",
"[html]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode"
+ "editor.defaultFormatter": "oxc.oxc-vscode"
},
"[css]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode"
+ "editor.defaultFormatter": "oxc.oxc-vscode"
},
"[scss]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode"
+ "editor.defaultFormatter": "oxc.oxc-vscode"
},
"[javascript]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode"
+ "editor.defaultFormatter": "oxc.oxc-vscode"
},
"[typescript]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode"
+ "editor.defaultFormatter": "oxc.oxc-vscode"
},
"[json]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode"
+ "editor.defaultFormatter": "oxc.oxc-vscode"
},
"[markdown]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode"
+ "editor.defaultFormatter": "oxc.oxc-vscode"
},
"[jsonc]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode"
+ "editor.defaultFormatter": "oxc.oxc-vscode"
},
"[vue]": {
- "editor.defaultFormatter": "esbenp.prettier-vscode"
+ "editor.defaultFormatter": "oxc.oxc-vscode"
},
+
// extensions
"extensions.ignoreRecommendations": true,
@@ -79,6 +92,7 @@
"files.insertFinalNewline": true,
"files.simpleDialog.enable": true,
"files.associations": {
+ "*.css": "tailwindcss",
"*.ejs": "html",
"*.art": "html",
"**/tsconfig.json": "jsonc",
@@ -118,7 +132,7 @@
// search
"search.searchEditor.singleClickBehaviour": "peekDefinition",
"search.followSymlinks": false,
- // 在使用搜索功能时,将这些文件夹/文件排除在外
+ // 使用搜索功能时,将这些文件和文件夹排除在外
"search.exclude": {
"**/node_modules": true,
"**/*.log": true,
@@ -158,7 +172,7 @@
"emmet.triggerExpansionOnTab": false,
"errorLens.enabledDiagnosticLevels": ["warning", "error"],
- "errorLens.excludeBySource": ["cSpell", "Grammarly", "eslint"],
+ "errorLens.excludeBySource": ["cSpell", "Grammarly"],
"stylelint.enable": true,
"stylelint.packageManager": "pnpm",
@@ -166,9 +180,10 @@
"stylelint.customSyntax": "postcss-html",
"stylelint.snippet": ["css", "less", "postcss", "scss", "vue"],
- "typescript.inlayHints.enumMemberValues.enabled": true,
- "typescript.preferences.preferTypeOnlyAutoImports": true,
- "typescript.preferences.includePackageJsonAutoImports": "on",
+ "js/ts.tsdk.path": "node_modules/typescript/lib",
+ "js/ts.inlayHints.enumMemberValues.enabled": true,
+ "js/ts.preferences.preferTypeOnlyAutoImports": true,
+ "js/ts.preferences.includePackageJsonAutoImports": "on",
"eslint.validate": [
"javascript",
@@ -195,7 +210,7 @@
"yaml": false
},
- "cssVariables.lookupFiles": ["packages/core/base/design/src/**/*.css"],
+ "cssVariables.lookupFiles": ["packages/@core/base/design/src/**/*.css"],
"i18n-ally.localesPaths": [
"packages/locales/src/langs",
@@ -220,12 +235,9 @@
"*.env": "$(capture).env.*",
"README.md": "README*,CHANGELOG*,LICENSE,CNAME",
"package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json",
- "eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json,lefthook.yml",
- "tailwind.config.mjs": "postcss.*"
+ "oxlint.config.ts": ".eslintignore,.stylelintignore,.commitlintrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json,lefthook.yml,oxfmt.config.*,eslint.config.*"
},
"commentTranslate.hover.enabled": false,
"commentTranslate.multiLineMerge": true,
- "vue.server.hybridMode": true,
- "typescript.tsdk": "node_modules/typescript/lib",
- "oxc.enable": false
+ "vue.server.hybridMode": true
}
diff --git a/apps/vben5/README.ja-JP.md b/apps/vben5/README.ja-JP.md
index 4ce285a74..ca0a9775d 100644
--- a/apps/vben5/README.ja-JP.md
+++ b/apps/vben5/README.ja-JP.md
@@ -114,7 +114,7 @@ pnpm build
## ブラウザサポート
-ローカル開発には `Chrome 80+` ブラウザを推奨します
+Tailwind CSS v4.0 is designed for Safari 16.4+, Chrome 111+, and Firefox 128+
モダンブラウザをサポートし、IEはサポートしません
diff --git a/apps/vben5/README.md b/apps/vben5/README.md
index ce8e89758..e1c9f29cd 100644
--- a/apps/vben5/README.md
+++ b/apps/vben5/README.md
@@ -114,7 +114,7 @@ Reference [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.
## Browser Support
-The `Chrome 80+` browser is recommended for local development
+Tailwind CSS v4.0 is designed for Safari 16.4+, Chrome 111+, and Firefox 128+
Support modern browsers, not IE
diff --git a/apps/vben5/README.zh-CN.md b/apps/vben5/README.zh-CN.md
index d3193ef65..da7ec5fa8 100644
--- a/apps/vben5/README.zh-CN.md
+++ b/apps/vben5/README.zh-CN.md
@@ -114,7 +114,7 @@ pnpm build
## 浏览器支持
-本地开发推荐使用 `Chrome 80+` 浏览器
+Tailwind CSS v4.0 is designed for Safari 16.4+, Chrome 111+, and Firefox 128+
支持现代浏览器,不支持 IE
diff --git a/apps/vben5/apps/app-antd/.env.development b/apps/vben5/apps/app-antd/.env.development
index ef5b909eb..302e7c03e 100644
--- a/apps/vben5/apps/app-antd/.env.development
+++ b/apps/vben5/apps/app-antd/.env.development
@@ -17,6 +17,7 @@ VITE_INJECT_APP_LOADING=true
# 是否仅允许OIDC登录
VITE_GLOB_AUTH_ONLY_OIDC=true
+VITE_GLOB_AUTH_DISABLE_PKCE=true
# 认证服务器
VITE_GLOB_AUTH_AUTHORITY="http://localhost:30000"
# 授权范围
diff --git a/apps/vben5/apps/app-antd/.vite/deps/_metadata.json b/apps/vben5/apps/app-antd/.vite/deps/_metadata.json
index 5f0b346a8..cca964d5e 100644
--- a/apps/vben5/apps/app-antd/.vite/deps/_metadata.json
+++ b/apps/vben5/apps/app-antd/.vite/deps/_metadata.json
@@ -5,4 +5,4 @@
"browserHash": "cfcea6c4",
"optimized": {},
"chunks": {}
-}
\ No newline at end of file
+}
diff --git a/apps/vben5/apps/app-antd/index.html b/apps/vben5/apps/app-antd/index.html
index 33d34a9e2..39be77767 100644
--- a/apps/vben5/apps/app-antd/index.html
+++ b/apps/vben5/apps/app-antd/index.html
@@ -9,8 +9,7 @@
+ content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" />
<%= VITE_APP_TITLE %>
diff --git a/apps/vben5/apps/app-antd/package.json b/apps/vben5/apps/app-antd/package.json
index 8fe700c52..4ae7bbc58 100644
--- a/apps/vben5/apps/app-antd/package.json
+++ b/apps/vben5/apps/app-antd/package.json
@@ -50,6 +50,7 @@
"@abp/ui": "workspace:*",
"@abp/webhooks": "workspace:*",
"@abp/wechat": "workspace:*",
+ "@tanstack/vue-query": "catalog:",
"@vben-core/shadcn-ui": "workspace:*",
"@vben/access": "workspace:*",
"@vben/common-ui": "workspace:*",
diff --git a/apps/vben5/apps/app-antd/postcss.config.mjs b/apps/vben5/apps/app-antd/postcss.config.mjs
deleted file mode 100644
index 3d8070455..000000000
--- a/apps/vben5/apps/app-antd/postcss.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@vben/tailwind-config/postcss';
diff --git a/apps/vben5/apps/app-antd/public/resource/tinymce/langs/en.js b/apps/vben5/apps/app-antd/public/resource/tinymce/langs/en.js
index 27337c394..6389318fd 100644
--- a/apps/vben5/apps/app-antd/public/resource/tinymce/langs/en.js
+++ b/apps/vben5/apps/app-antd/public/resource/tinymce/langs/en.js
@@ -13,20 +13,20 @@ tinymce.addI18n('es', {
Italic: 'Cursiva',
Underline: 'Subrayado',
Strikethrough: 'Tachado',
- Superscript: 'Super\u00edndice',
- Subscript: 'Sub\u00edndice',
+ Superscript: 'Super\u00EDndice',
+ Subscript: 'Sub\u00EDndice',
'Clear formatting': 'Limpiar formato',
'Align left': 'Alinear a la izquierda',
'Align center': 'Alinear al centro',
'Align right': 'Alinear a la derecha',
Justify: 'Justificar',
- 'Bullet list': 'Lista de vi\u00f1etas',
+ 'Bullet list': 'Lista de vi\u00F1etas',
'Numbered list': 'Lista numerada',
- 'Decrease indent': 'Disminuir sangr\u00eda',
- 'Increase indent': 'Incrementar sangr\u00eda',
+ 'Decrease indent': 'Disminuir sangr\u00EDa',
+ 'Increase indent': 'Incrementar sangr\u00EDa',
Close: 'Cerrar',
Formats: 'Formatos',
- "Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": 'Su navegador no es compatible con el acceso directo al portapapeles. Use las teclas Crtl+X\/C\/V de su teclado.',
+ "Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.": 'Su navegador no es compatible con el acceso directo al portapapeles. Use las teclas Crtl+X/C/V de su teclado.',
Headers: 'Encabezados',
'Header 1': 'Encabezado 1',
'Header 2': 'Encabezado 2',
@@ -44,23 +44,23 @@ tinymce.addI18n('es', {
Preformatted: 'Con formato previo',
Div: 'Div',
Pre: 'Pre',
- Code: 'C\u00f3digo',
- Paragraph: 'P\u00e1rrafo',
+ Code: 'C\u00F3digo',
+ Paragraph: 'P\u00E1rrafo',
Blockquote: 'Blockquote',
Inline: 'Alineado',
Blocks: 'Bloques',
- 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.': 'Pegar est\u00e1 ahora en modo de texto plano. El contenido se pegar\u00e1 como texto plano hasta que desactive esta opci\u00f3n.',
+ 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.': 'Pegar est\u00E1 ahora en modo de texto plano. El contenido se pegar\u00E1 como texto plano hasta que desactive esta opci\u00F3n.',
Fonts: 'Fuentes',
- 'Font Sizes': 'Tama\u00f1os de fuente',
+ 'Font Sizes': 'Tama\u00F1os de fuente',
Class: 'Clase',
'Browse for an image': 'Buscar una imagen',
OR: 'OR',
- 'Drop an image here': 'Arrastre una imagen aqu\u00ed',
+ 'Drop an image here': 'Arrastre una imagen aqu\u00ED',
Upload: 'Cargar',
Block: 'Bloque',
Align: 'Alinear',
Default: 'Por defecto',
- Circle: 'C\u00edrculo',
+ Circle: 'C\u00EDrculo',
Disc: 'Disco',
Square: 'Cuadrado',
'Lower Alpha': 'Inferior Alfa',
@@ -71,14 +71,14 @@ tinymce.addI18n('es', {
'Anchor...': 'Anclaje...',
Name: 'Nombre',
Id: 'Id',
- 'Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.': 'Deber\u00eda comenzar por una letra, seguida solo de letras, n\u00fameros, guiones, puntos, dos puntos o guiones bajos.',
- 'You have unsaved changes are you sure you want to navigate away?': 'Tiene cambios sin guardar. \u00bfEst\u00e1 seguro de que quiere salir?',
- 'Restore last draft': 'Restaurar el \u00faltimo borrador',
- 'Special character...': 'Car\u00e1cter especial...',
- 'Source code': 'C\u00f3digo fuente',
- 'Insert\/Edit code sample': 'Insertar\/editar c\u00f3digo de prueba',
+ 'Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.': 'Deber\u00EDa comenzar por una letra, seguida solo de letras, n\u00FAmeros, guiones, puntos, dos puntos o guiones bajos.',
+ 'You have unsaved changes are you sure you want to navigate away?': 'Tiene cambios sin guardar. \u00BFEst\u00E1 seguro de que quiere salir?',
+ 'Restore last draft': 'Restaurar el \u00FAltimo borrador',
+ 'Special character...': 'Car\u00E1cter especial...',
+ 'Source code': 'C\u00F3digo fuente',
+ 'Insert/Edit code sample': 'Insertar/editar c\u00F3digo de prueba',
Language: 'Idioma',
- 'Code sample...': 'Ejemplo de c\u00f3digo...',
+ 'Code sample...': 'Ejemplo de c\u00F3digo...',
'Color Picker': 'Selector de colores',
R: 'R',
G: 'V',
@@ -87,33 +87,33 @@ tinymce.addI18n('es', {
'Right to left': 'De derecha a izquierda',
'Emoticons...': 'Emoticones...',
'Metadata and Document Properties': 'Metadatos y propiedades del documento',
- Title: 'T\u00edtulo',
+ Title: 'T\u00EDtulo',
Keywords: 'Palabras clave',
- Description: 'Descripci\u00f3n',
+ Description: 'Descripci\u00F3n',
Robots: 'Robots',
Author: 'Autor',
- Encoding: 'Codificaci\u00f3n',
+ Encoding: 'Codificaci\u00F3n',
Fullscreen: 'Pantalla completa',
- Action: 'Acci\u00f3n',
+ Action: 'Acci\u00F3n',
Shortcut: 'Atajo',
Help: 'Ayuda',
- Address: 'Direcci\u00f3n',
- 'Focus to menubar': 'Enfocar la barra del men\u00fa',
+ Address: 'Direcci\u00F3n',
+ 'Focus to menubar': 'Enfocar la barra del men\u00FA',
'Focus to toolbar': 'Enfocar la barra de herramientas',
'Focus to element path': 'Enfocar la ruta del elemento',
'Focus to contextual toolbar': 'Enfocar la barra de herramientas contextual',
- 'Insert link (if link plugin activated)': 'Insertar enlace (si el complemento de enlace est\u00e1 activado)',
- 'Save (if save plugin activated)': 'Guardar (si el componente de salvar est\u00e1 activado)',
- 'Find (if searchreplace plugin activated)': 'Buscar (si el complemento buscar-remplazar est\u00e1 activado)',
+ 'Insert link (if link plugin activated)': 'Insertar enlace (si el complemento de enlace est\u00E1 activado)',
+ 'Save (if save plugin activated)': 'Guardar (si el componente de salvar est\u00E1 activado)',
+ 'Find (if searchreplace plugin activated)': 'Buscar (si el complemento buscar-remplazar est\u00E1 activado)',
'Plugins installed ({0}):': 'Plugins instalados ({0}):',
'Premium plugins:': 'Complementos premium:',
- 'Learn more...': 'Aprende m\u00e1s...',
+ 'Learn more...': 'Aprende m\u00E1s...',
'You are using {0}': 'Estas usando {0}',
Plugins: 'Complementos',
'Handy Shortcuts': 'Accesos directos',
- 'Horizontal line': 'L\u00ednea horizontal',
- 'Insert\/edit image': 'Insertar\/editar imagen',
- 'Image description': 'Descripci\u00f3n de la imagen',
+ 'Horizontal line': 'L\u00EDnea horizontal',
+ 'Insert/edit image': 'Insertar/editar imagen',
+ 'Image description': 'Descripci\u00F3n de la imagen',
Source: 'Enlace',
Dimensions: 'Dimensiones',
'Constrain proportions': 'Restringir proporciones',
@@ -125,7 +125,7 @@ tinymce.addI18n('es', {
Border: 'Borde',
'Insert image': 'Insertar imagen',
'Image...': 'Imagen...',
- 'Image list': 'Lista de im\u00e1genes',
+ 'Image list': 'Lista de im\u00E1genes',
'Rotate counterclockwise': 'Girar a la izquierda',
'Rotate clockwise': 'Girar a la derecha',
'Flip vertically': 'Invertir verticalmente',
@@ -136,7 +136,7 @@ tinymce.addI18n('es', {
'Zoom out': 'Alejar',
Crop: 'Recortar',
Resize: 'Redimensionar',
- Orientation: 'Orientaci\u00f3n',
+ Orientation: 'Orientaci\u00F3n',
Brightness: 'Brillo',
Sharpen: 'Forma',
Contrast: 'Contraste',
@@ -144,11 +144,11 @@ tinymce.addI18n('es', {
Gamma: 'Gamma',
Invert: 'Invertir',
Apply: 'Aplicar',
- Back: 'Atr\u00e1s',
- 'Insert date\/time': 'Insertar fecha\/hora',
- 'Date\/time': 'Fecha\/hora',
- 'Insert\/Edit Link': 'Insertar\/editar enlace',
- 'Insert\/edit link': 'Insertar\/editar enlace',
+ Back: 'Atr\u00E1s',
+ 'Insert date/time': 'Insertar fecha/hora',
+ 'Date/time': 'Fecha/hora',
+ 'Insert/Edit Link': 'Insertar/editar enlace',
+ 'Insert/edit link': 'Insertar/editar enlace',
'Text to display': 'Texto para mostrar',
Url: 'URL',
'Open link in...': 'Abrir enlace en...',
@@ -159,20 +159,20 @@ tinymce.addI18n('es', {
Anchors: 'Anclas',
'Link...': 'Enlace...',
'Paste or type a link': 'Pega o introduce un enlace',
- 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?': 'El enlace que has introducido no parece ser una direcci\u00f3n de correo electr\u00f3nico. Quieres a\u00f1adir el prefijo necesario mailto: ?',
- 'The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?': 'El enlace que has introducido no parece ser una enlace externo. Quieres a\u00f1adir el prefijo necesario http:\/\/ ?',
+ 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?': 'El enlace que has introducido no parece ser una direcci\u00F3n de correo electr\u00F3nico. Quieres a\u00F1adir el prefijo necesario mailto: ?',
+ 'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?': 'El enlace que has introducido no parece ser una enlace externo. Quieres a\u00F1adir el prefijo necesario http:// ?',
'Link list': 'Lista de enlaces',
'Insert video': 'Insertar video',
- 'Insert\/edit video': 'Insertar\/editar video',
- 'Insert\/edit media': 'Insertar\/editar medio',
+ 'Insert/edit video': 'Insertar/editar video',
+ 'Insert/edit media': 'Insertar/editar medio',
'Alternative source': 'Enlace alternativo',
'Alternative source URL': 'Origen de URL alternativo',
- 'Media poster (Image URL)': 'P\u00f3ster de medio (URL de imagen)',
- 'Paste your embed code below:': 'Pega tu c\u00f3digo embebido debajo',
+ 'Media poster (Image URL)': 'P\u00F3ster de medio (URL de imagen)',
+ 'Paste your embed code below:': 'Pega tu c\u00F3digo embebido debajo',
Embed: 'Incrustado',
'Media...': 'Medios...',
'Nonbreaking space': 'Espacio fijo',
- 'Page break': 'Salto de p\u00e1gina',
+ 'Page break': 'Salto de p\u00E1gina',
'Paste as text': 'Pegar como texto',
Preview: 'Previsualizar',
'Print...': 'Imprimir...',
@@ -187,11 +187,11 @@ tinymce.addI18n('es', {
'Could not find the specified string.': 'No se encuentra la cadena de texto especificada',
'Match case': 'Coincidencia exacta',
'Find whole words only': 'Solo palabras completas',
- 'Spell check': 'Revisar ortograf\u00eda',
+ 'Spell check': 'Revisar ortograf\u00EDa',
Ignore: 'Ignorar',
'Ignore all': 'Ignorar todos',
Finish: 'Finalizar',
- 'Add to Dictionary': 'A\u00f1adir al Diccionario',
+ 'Add to Dictionary': 'A\u00F1adir al Diccionario',
'Insert table': 'Insertar tabla',
'Table properties': 'Propiedades de la tabla',
'Delete table': 'Eliminar tabla',
@@ -202,15 +202,15 @@ tinymce.addI18n('es', {
'Merge cells': 'Combinar celdas',
'Split cell': 'Dividir celdas',
'Insert row before': 'Insertar fila antes',
- 'Insert row after': 'Insertar fila despu\u00e9s ',
+ 'Insert row after': 'Insertar fila despu\u00E9s ',
'Delete row': 'Eliminar fila',
'Row properties': 'Propiedades de la fila',
'Cut row': 'Cortar fila',
'Copy row': 'Copiar fila',
'Paste row before': 'Pegar la fila antes',
- 'Paste row after': 'Pegar la fila despu\u00e9s',
+ 'Paste row after': 'Pegar la fila despu\u00E9s',
'Insert column before': 'Insertar columna antes',
- 'Insert column after': 'Insertar columna despu\u00e9s',
+ 'Insert column after': 'Insertar columna despu\u00E9s',
'Delete column': 'Eliminar columna',
Cols: 'Columnas',
Rows: 'Filas',
@@ -218,13 +218,13 @@ tinymce.addI18n('es', {
Height: 'Alto',
'Cell spacing': 'Espacio entre celdas',
'Cell padding': 'Relleno de celda',
- 'Show caption': 'Mostrar t\u00edtulo',
+ 'Show caption': 'Mostrar t\u00EDtulo',
Left: 'Izquierda',
Center: 'Centrado',
Right: 'Derecha',
'Cell type': 'Tipo de celda',
- Scope: '\u00c1mbito',
- Alignment: 'Alineaci\u00f3n',
+ Scope: '\u00C1mbito',
+ Alignment: 'Alineaci\u00F3n',
'H Align': 'Alineamiento Horizontal',
'V Align': 'Alineamiento Vertical',
Top: 'Arriba',
@@ -236,7 +236,7 @@ tinymce.addI18n('es', {
'Row type': 'Tipo de fila',
Header: 'Cabecera',
Body: 'Cuerpo',
- Footer: 'Pie de p\u00e1gina',
+ Footer: 'Pie de p\u00E1gina',
'Border color': 'Color del borde',
'Insert template...': 'Insertar plantilla...',
Templates: 'Plantillas',
@@ -253,7 +253,7 @@ tinymce.addI18n('es', {
'Word count': 'Contar palabras',
Count: 'Recuento',
Document: 'Documento',
- Selection: 'Selecci\u00f3n',
+ Selection: 'Selecci\u00F3n',
Words: 'Palabras',
'Words: {0}': 'Palabras: {0}',
'{0} words': '{0} palabras',
@@ -265,15 +265,15 @@ tinymce.addI18n('es', {
Table: 'Tabla',
Tools: 'Herramientas',
'Powered by {0}': 'Desarrollado por {0}',
- 'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help': '\u00c1rea de texto enriquecido. Pulse ALT-F9 para el menu. Pulse ALT-F10 para la barra de herramientas. Pulse ALT-0 para ayuda',
+ 'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help': '\u00C1rea de texto enriquecido. Pulse ALT-F9 para el menu. Pulse ALT-F10 para la barra de herramientas. Pulse ALT-0 para ayuda',
'Image title': 'Titulo de imagen',
'Border width': 'Ancho de borde',
'Border style': 'Estilo de borde',
Error: 'Error',
Warn: 'Advertencia',
- Valid: 'V\u00e1lido',
- 'To open the popup, press Shift+Enter': 'Para abrir el elemento emergente, pulse May\u00fas+Intro',
- 'Rich Text Area. Press ALT-0 for help.': '\u00c1rea de texto enriquecido. Pulse ALT-0 para abrir la ayuda.',
+ Valid: 'V\u00E1lido',
+ 'To open the popup, press Shift+Enter': 'Para abrir el elemento emergente, pulse May\u00FAs+Intro',
+ 'Rich Text Area. Press ALT-0 for help.': '\u00C1rea de texto enriquecido. Pulse ALT-0 para abrir la ayuda.',
'System Font': 'Fuente de sistema',
'Failed to upload image: {0}': 'Fallo al cargar imagen: {0}',
'Failed to load plugin: {0} from url {1}': 'Fallo al cargar complemento: {0} desde URL {1}',
@@ -285,31 +285,31 @@ tinymce.addI18n('es', {
Currency: 'Divisa',
Text: 'Texto',
Quotations: 'Comillas',
- Mathematical: 'S\u00edmbolo matem\u00e1tico',
+ Mathematical: 'S\u00EDmbolo matem\u00E1tico',
'Extended Latin': 'Latino extendido A',
- Symbols: 'S\u00edmbolos',
+ Symbols: 'S\u00EDmbolos',
Arrows: 'Flechas',
'User Defined': 'Definido por el usuario',
- 'dollar sign': 'signo de d\u00f3lar',
+ 'dollar sign': 'signo de d\u00F3lar',
'currency sign': 'signo de divisa',
'euro-currency sign': 'signo de euro',
'colon sign': 'signo de dos puntos',
'cruzeiro sign': 'signo de cruceiro',
- 'french franc sign': 'signo de franco franc\u00e9s',
+ 'french franc sign': 'signo de franco franc\u00E9s',
'lira sign': 'signo de lira',
'mill sign': 'signo de mill',
'naira sign': 'signo de naira',
'peseta sign': 'signo de peseta',
'rupee sign': 'signo de rupia',
'won sign': 'signo de won',
- 'new sheqel sign': 'signo de nuevo s\u00e9quel',
+ 'new sheqel sign': 'signo de nuevo s\u00E9quel',
'dong sign': 'signo de dong',
'kip sign': 'signo de kip',
'tugrik sign': 'signo de tugrik',
'drachma sign': 'signo de dracma',
- 'german penny symbol': 'signo de penique alem\u00e1n',
+ 'german penny symbol': 'signo de penique alem\u00E1n',
'peso sign': 'signo de peso',
- 'guarani sign': 'signo de guaran\u00ed',
+ 'guarani sign': 'signo de guaran\u00ED',
'austral sign': 'signo de austral',
'hryvnia sign': 'signo de grivna',
'cedi sign': 'signo de cedi',
@@ -318,13 +318,13 @@ tinymce.addI18n('es', {
'tenge sign': 'signo de tenge',
'indian rupee sign': 'signo de rupia india',
'turkish lira sign': 'signo de lira turca',
- 'nordic mark sign': 'signo de marco n\u00f3rdico',
+ 'nordic mark sign': 'signo de marco n\u00F3rdico',
'manat sign': 'signo de manat',
'ruble sign': 'signo de rublo',
- 'yen character': 'car\u00e1cter de yen',
- 'yuan character': 'car\u00e1cter de yuan',
- 'yuan character, in hong kong and taiwan': 'car\u00e1cter de yuan en Hong Kong y Taiw\u00e1n',
- 'yen\/yuan character variant one': 'Variante uno de car\u00e1cter de yen\/yuan',
+ 'yen character': 'car\u00E1cter de yen',
+ 'yuan character': 'car\u00E1cter de yuan',
+ 'yuan character, in hong kong and taiwan': 'car\u00E1cter de yuan en Hong Kong y Taiw\u00E1n',
+ 'yen/yuan character variant one': 'Variante uno de car\u00E1cter de yen/yuan',
'Loading emoticons...': 'Cargando emoticonos...',
'Could not load emoticons': 'No se han podido cargar los emoticonos',
People: 'Personas',
@@ -337,19 +337,19 @@ tinymce.addI18n('es', {
Characters: 'Caracteres',
'Characters (no spaces)': 'Caracteres (sin espacios)',
'{0} characters': '{0} caracteres',
- 'Error: Form submit field collision.': 'Error: Colisi\u00f3n de campo al enviar formulario.',
- 'Error: No form element found.': 'Error: No se encuentra ning\u00fan elemento de formulario.',
+ 'Error: Form submit field collision.': 'Error: Colisi\u00F3n de campo al enviar formulario.',
+ 'Error: No form element found.': 'Error: No se encuentra ning\u00FAn elemento de formulario.',
Update: 'Actualizar',
'Color swatch': 'Muestrario de colores',
Turquoise: 'Turquesa',
Green: 'Verde',
Blue: 'Azul',
- Purple: 'P\u00farpura',
+ Purple: 'P\u00FArpura',
'Navy Blue': 'Azul marino',
'Dark Turquoise': 'Turquesa oscuro',
'Dark Green': 'Verde oscuro',
'Medium Blue': 'Azul medio',
- 'Medium Purple': 'P\u00farpura medio',
+ 'Medium Purple': 'P\u00FArpura medio',
'Midnight Blue': 'Azul medio',
Yellow: 'Amarillo',
Orange: 'Naranja',
@@ -371,35 +371,35 @@ tinymce.addI18n('es', {
Black: 'Negro',
White: 'Blanco',
'Switch to or from fullscreen mode': 'Activar o desactivar modo pantalla completa',
- 'Open help dialog': 'Abrir di\u00e1logo de ayuda',
+ 'Open help dialog': 'Abrir di\u00E1logo de ayuda',
history: 'historial',
styles: 'estilos',
formatting: 'formato',
- alignment: 'alineaci\u00f3n',
- indentation: 'sangr\u00eda',
- 'permanent pen': 'bol\u00edgrafo permanente',
+ alignment: 'alineaci\u00F3n',
+ indentation: 'sangr\u00EDa',
+ 'permanent pen': 'bol\u00EDgrafo permanente',
comments: 'comentarios',
'Format Painter': 'Copiar formato',
- 'Insert\/edit iframe': 'Insertar\/editar iframe',
- Capitalization: 'Uso de may\u00fasculas',
- lowercase: 'min\u00fasculas',
- UPPERCASE: 'MAY\u00daSCULAS',
- 'Title Case': 'Tipo T\u00edtulo',
- 'Permanent Pen Properties': 'Propiedades del bol\u00edgrafo permanente',
- 'Permanent pen properties...': 'Propiedades del bol\u00edgrafo permanente...',
+ 'Insert/edit iframe': 'Insertar/editar iframe',
+ Capitalization: 'Uso de may\u00FAsculas',
+ lowercase: 'min\u00FAsculas',
+ UPPERCASE: 'MAY\u00DASCULAS',
+ 'Title Case': 'Tipo T\u00EDtulo',
+ 'Permanent Pen Properties': 'Propiedades del bol\u00EDgrafo permanente',
+ 'Permanent pen properties...': 'Propiedades del bol\u00EDgrafo permanente...',
Font: 'Fuente',
- Size: 'Tama\u00f1o',
- 'More...': 'M\u00e1s...',
+ Size: 'Tama\u00F1o',
+ 'More...': 'M\u00E1s...',
'Spellcheck Language': 'Corrector',
'Select...': 'Seleccionar...',
Preferences: 'Preferencias',
- Yes: 'S\u00ed',
+ Yes: 'S\u00ED',
No: 'No',
- 'Keyboard Navigation': 'Navegaci\u00f3n con el teclado',
- Version: 'Versi\u00f3n',
+ 'Keyboard Navigation': 'Navegaci\u00F3n con el teclado',
+ Version: 'Versi\u00F3n',
Anchor: 'Ancla',
- 'Special character': 'Car\u00e1cter especial',
- 'Code sample': 'Ejemplo de c\u00f3digo',
+ 'Special character': 'Car\u00E1cter especial',
+ 'Code sample': 'Ejemplo de c\u00F3digo',
Color: 'Color',
Emoticons: 'Emoticonos',
'Document properties': 'Propiedades del documento',
@@ -413,7 +413,7 @@ tinymce.addI18n('es', {
Prev: 'Anterior',
'Find and replace': 'Buscar y reemplazar',
'Whole words': 'Palabras completas',
- Spellcheck: 'Corrector ortogr\u00e1fico',
- Caption: 'Subt\u00edtulo',
+ Spellcheck: 'Corrector ortogr\u00E1fico',
+ Caption: 'Subt\u00EDtulo',
'Insert template': 'Insertar plantilla'
})
diff --git a/apps/vben5/apps/app-antd/public/resource/tinymce/langs/zh-CN.js b/apps/vben5/apps/app-antd/public/resource/tinymce/langs/zh-CN.js
index f9d8b5cfd..452395aa5 100644
--- a/apps/vben5/apps/app-antd/public/resource/tinymce/langs/zh-CN.js
+++ b/apps/vben5/apps/app-antd/public/resource/tinymce/langs/zh-CN.js
@@ -1,32 +1,32 @@
tinymce.addI18n('zh_CN',{
-"Redo": "\u91cd\u505a",
-"Undo": "\u64a4\u9500",
-"Cut": "\u526a\u5207",
-"Copy": "\u590d\u5236",
-"Paste": "\u7c98\u8d34",
+"Redo": "\u91CD\u505A",
+"Undo": "\u64A4\u9500",
+"Cut": "\u526A\u5207",
+"Copy": "\u590D\u5236",
+"Paste": "\u7C98\u8D34",
"Select all": "\u5168\u9009",
-"New document": "\u65b0\u6587\u4ef6",
-"Ok": "\u786e\u5b9a",
-"Cancel": "\u53d6\u6d88",
-"Visual aids": "\u7f51\u683c\u7ebf",
-"Bold": "\u7c97\u4f53",
-"Italic": "\u659c\u4f53",
-"Underline": "\u4e0b\u5212\u7ebf",
-"Strikethrough": "\u5220\u9664\u7ebf",
-"Superscript": "\u4e0a\u6807",
-"Subscript": "\u4e0b\u6807",
-"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
-"Align left": "\u5de6\u8fb9\u5bf9\u9f50",
-"Align center": "\u4e2d\u95f4\u5bf9\u9f50",
-"Align right": "\u53f3\u8fb9\u5bf9\u9f50",
-"Justify": "\u4e24\u7aef\u5bf9\u9f50",
-"Bullet list": "\u9879\u76ee\u7b26\u53f7",
-"Numbered list": "\u7f16\u53f7\u5217\u8868",
-"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
-"Increase indent": "\u589e\u52a0\u7f29\u8fdb",
-"Close": "\u5173\u95ed",
-"Formats": "\u683c\u5f0f",
-"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u7b49\u5feb\u6377\u952e\u3002",
+"New document": "\u65B0\u6587\u4EF6",
+"Ok": "\u786E\u5B9A",
+"Cancel": "\u53D6\u6D88",
+"Visual aids": "\u7F51\u683C\u7EBF",
+"Bold": "\u7C97\u4F53",
+"Italic": "\u659C\u4F53",
+"Underline": "\u4E0B\u5212\u7EBF",
+"Strikethrough": "\u5220\u9664\u7EBF",
+"Superscript": "\u4E0A\u6807",
+"Subscript": "\u4E0B\u6807",
+"Clear formatting": "\u6E05\u9664\u683C\u5F0F",
+"Align left": "\u5DE6\u8FB9\u5BF9\u9F50",
+"Align center": "\u4E2D\u95F4\u5BF9\u9F50",
+"Align right": "\u53F3\u8FB9\u5BF9\u9F50",
+"Justify": "\u4E24\u7AEF\u5BF9\u9F50",
+"Bullet list": "\u9879\u76EE\u7B26\u53F7",
+"Numbered list": "\u7F16\u53F7\u5217\u8868",
+"Decrease indent": "\u51CF\u5C11\u7F29\u8FDB",
+"Increase indent": "\u589E\u52A0\u7F29\u8FDB",
+"Close": "\u5173\u95ED",
+"Formats": "\u683C\u5F0F",
+"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.": "\u4F60\u7684\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u6253\u5F00\u526A\u8D34\u677F\uFF0C\u8BF7\u4F7F\u7528Ctrl+X/C/V\u7B49\u5FEB\u6377\u952E\u3002",
"Headers": "\u6807\u9898",
"Header 1": "\u6807\u98981",
"Header 2": "\u6807\u98982",
@@ -41,349 +41,349 @@ tinymce.addI18n('zh_CN',{
"Heading 4": "\u6807\u98984",
"Heading 5": "\u6807\u98985",
"Heading 6": "\u6807\u98986",
-"Preformatted": "\u9884\u5148\u683c\u5f0f\u5316\u7684",
+"Preformatted": "\u9884\u5148\u683C\u5F0F\u5316\u7684",
"Div": "Div",
"Pre": "Pre",
-"Code": "\u4ee3\u7801",
-"Paragraph": "\u6bb5\u843d",
-"Blockquote": "\u5f15\u6587\u533a\u5757",
-"Inline": "\u6587\u672c",
-"Blocks": "\u57fa\u5757",
-"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
-"Fonts": "\u5b57\u4f53",
-"Font Sizes": "\u5b57\u53f7",
-"Class": "\u7c7b\u578b",
-"Browse for an image": "\u6d4f\u89c8\u56fe\u50cf",
+"Code": "\u4EE3\u7801",
+"Paragraph": "\u6BB5\u843D",
+"Blockquote": "\u5F15\u6587\u533A\u5757",
+"Inline": "\u6587\u672C",
+"Blocks": "\u57FA\u5757",
+"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5F53\u524D\u4E3A\u7EAF\u6587\u672C\u7C98\u8D34\u6A21\u5F0F\uFF0C\u518D\u6B21\u70B9\u51FB\u53EF\u4EE5\u56DE\u5230\u666E\u901A\u7C98\u8D34\u6A21\u5F0F\u3002",
+"Fonts": "\u5B57\u4F53",
+"Font Sizes": "\u5B57\u53F7",
+"Class": "\u7C7B\u578B",
+"Browse for an image": "\u6D4F\u89C8\u56FE\u50CF",
"OR": "\u6216",
-"Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64",
-"Upload": "\u4e0a\u4f20",
+"Drop an image here": "\u62D6\u653E\u4E00\u5F20\u56FE\u50CF\u81F3\u6B64",
+"Upload": "\u4E0A\u4F20",
"Block": "\u5757",
-"Align": "\u5bf9\u9f50",
-"Default": "\u9ed8\u8ba4",
-"Circle": "\u7a7a\u5fc3\u5706",
-"Disc": "\u5b9e\u5fc3\u5706",
-"Square": "\u65b9\u5757",
-"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
-"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
-"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
-"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
-"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
-"Anchor...": "\u951a\u70b9...",
-"Name": "\u540d\u79f0",
-"Id": "\u6807\u8bc6\u7b26",
-"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
-"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
-"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
-"Special characters...": "\u7279\u6b8a\u5b57\u7b26...",
-"Source code": "\u6e90\u4ee3\u7801",
-"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
-"Language": "\u8bed\u8a00",
-"Code sample...": "\u793a\u4f8b\u4ee3\u7801...",
+"Align": "\u5BF9\u9F50",
+"Default": "\u9ED8\u8BA4",
+"Circle": "\u7A7A\u5FC3\u5706",
+"Disc": "\u5B9E\u5FC3\u5706",
+"Square": "\u65B9\u5757",
+"Lower Alpha": "\u5C0F\u5199\u82F1\u6587\u5B57\u6BCD",
+"Lower Greek": "\u5C0F\u5199\u5E0C\u814A\u5B57\u6BCD",
+"Lower Roman": "\u5C0F\u5199\u7F57\u9A6C\u5B57\u6BCD",
+"Upper Alpha": "\u5927\u5199\u82F1\u6587\u5B57\u6BCD",
+"Upper Roman": "\u5927\u5199\u7F57\u9A6C\u5B57\u6BCD",
+"Anchor...": "\u951A\u70B9...",
+"Name": "\u540D\u79F0",
+"Id": "\u6807\u8BC6\u7B26",
+"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8BC6\u7B26\u5E94\u8BE5\u4EE5\u5B57\u6BCD\u5F00\u5934\uFF0C\u540E\u8DDF\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u7834\u6298\u53F7\u3001\u70B9\u3001\u5192\u53F7\u6216\u4E0B\u5212\u7EBF\u3002",
+"You have unsaved changes are you sure you want to navigate away?": "\u4F60\u8FD8\u6709\u6587\u6863\u5C1A\u672A\u4FDD\u5B58\uFF0C\u786E\u5B9A\u8981\u79BB\u5F00\uFF1F",
+"Restore last draft": "\u6062\u590D\u4E0A\u6B21\u7684\u8349\u7A3F",
+"Special characters...": "\u7279\u6B8A\u5B57\u7B26...",
+"Source code": "\u6E90\u4EE3\u7801",
+"Insert/Edit code sample": "\u63D2\u5165/\u7F16\u8F91\u4EE3\u7801\u793A\u4F8B",
+"Language": "\u8BED\u8A00",
+"Code sample...": "\u793A\u4F8B\u4EE3\u7801...",
"Color Picker": "\u9009\u8272\u5668",
"R": "R",
"G": "G",
"B": "B",
-"Left to right": "\u4ece\u5de6\u5230\u53f3",
-"Right to left": "\u4ece\u53f3\u5230\u5de6",
-"Emoticons...": "\u8868\u60c5\u7b26\u53f7...",
-"Metadata and Document Properties": "\u5143\u6570\u636e\u548c\u6587\u6863\u5c5e\u6027",
+"Left to right": "\u4ECE\u5DE6\u5230\u53F3",
+"Right to left": "\u4ECE\u53F3\u5230\u5DE6",
+"Emoticons...": "\u8868\u60C5\u7B26\u53F7...",
+"Metadata and Document Properties": "\u5143\u6570\u636E\u548C\u6587\u6863\u5C5E\u6027",
"Title": "\u6807\u9898",
-"Keywords": "\u5173\u952e\u8bcd",
-"Description": "\u63cf\u8ff0",
-"Robots": "\u673a\u5668\u4eba",
-"Author": "\u4f5c\u8005",
-"Encoding": "\u7f16\u7801",
-"Fullscreen": "\u5168\u5c4f",
-"Action": "\u64cd\u4f5c",
-"Shortcut": "\u5feb\u6377\u952e",
-"Help": "\u5e2e\u52a9",
+"Keywords": "\u5173\u952E\u8BCD",
+"Description": "\u63CF\u8FF0",
+"Robots": "\u673A\u5668\u4EBA",
+"Author": "\u4F5C\u8005",
+"Encoding": "\u7F16\u7801",
+"Fullscreen": "\u5168\u5C4F",
+"Action": "\u64CD\u4F5C",
+"Shortcut": "\u5FEB\u6377\u952E",
+"Help": "\u5E2E\u52A9",
"Address": "\u5730\u5740",
-"Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f",
-"Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f",
-"Focus to element path": "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84",
-"Focus to contextual toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355",
-"Insert link (if link plugin activated)": "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
-"Save (if save plugin activated)": "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
-"Find (if searchreplace plugin activated)": "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
-"Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):",
-"Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a",
-"Learn more...": "\u4e86\u89e3\u66f4\u591a...",
-"You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}",
-"Plugins": "\u63d2\u4ef6",
-"Handy Shortcuts": "\u5feb\u6377\u952e",
-"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
-"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247",
-"Image description": "\u56fe\u7247\u63cf\u8ff0",
+"Focus to menubar": "\u79FB\u52A8\u7126\u70B9\u5230\u83DC\u5355\u680F",
+"Focus to toolbar": "\u79FB\u52A8\u7126\u70B9\u5230\u5DE5\u5177\u680F",
+"Focus to element path": "\u79FB\u52A8\u7126\u70B9\u5230\u5143\u7D20\u8DEF\u5F84",
+"Focus to contextual toolbar": "\u79FB\u52A8\u7126\u70B9\u5230\u4E0A\u4E0B\u6587\u83DC\u5355",
+"Insert link (if link plugin activated)": "\u63D2\u5165\u94FE\u63A5 (\u5982\u679C\u94FE\u63A5\u63D2\u4EF6\u5DF2\u6FC0\u6D3B)",
+"Save (if save plugin activated)": "\u4FDD\u5B58(\u5982\u679C\u4FDD\u5B58\u63D2\u4EF6\u5DF2\u6FC0\u6D3B)",
+"Find (if searchreplace plugin activated)": "\u67E5\u627E(\u5982\u679C\u67E5\u627E\u66FF\u6362\u63D2\u4EF6\u5DF2\u6FC0\u6D3B)",
+"Plugins installed ({0}):": "\u5DF2\u5B89\u88C5\u63D2\u4EF6 ({0}):",
+"Premium plugins:": "\u4F18\u79C0\u63D2\u4EF6\uFF1A",
+"Learn more...": "\u4E86\u89E3\u66F4\u591A...",
+"You are using {0}": "\u4F60\u6B63\u5728\u4F7F\u7528 {0}",
+"Plugins": "\u63D2\u4EF6",
+"Handy Shortcuts": "\u5FEB\u6377\u952E",
+"Horizontal line": "\u6C34\u5E73\u5206\u5272\u7EBF",
+"Insert/edit image": "\u63D2\u5165/\u7F16\u8F91\u56FE\u7247",
+"Image description": "\u56FE\u7247\u63CF\u8FF0",
"Source": "\u5730\u5740",
-"Dimensions": "\u5927\u5c0f",
-"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
-"General": "\u666e\u901a",
-"Advanced": "\u9ad8\u7ea7",
-"Style": "\u6837\u5f0f",
-"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
-"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
-"Border": "\u8fb9\u6846",
-"Insert image": "\u63d2\u5165\u56fe\u7247",
-"Image...": "\u56fe\u7247...",
-"Image list": "\u56fe\u7247\u5217\u8868",
-"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
-"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
-"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
-"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
-"Edit image": "\u7f16\u8f91\u56fe\u7247",
-"Image options": "\u56fe\u7247\u9009\u9879",
-"Zoom in": "\u653e\u5927",
-"Zoom out": "\u7f29\u5c0f",
-"Crop": "\u88c1\u526a",
-"Resize": "\u8c03\u6574\u5927\u5c0f",
-"Orientation": "\u65b9\u5411",
-"Brightness": "\u4eae\u5ea6",
+"Dimensions": "\u5927\u5C0F",
+"Constrain proportions": "\u4FDD\u6301\u7EB5\u6A2A\u6BD4",
+"General": "\u666E\u901A",
+"Advanced": "\u9AD8\u7EA7",
+"Style": "\u6837\u5F0F",
+"Vertical space": "\u5782\u76F4\u8FB9\u8DDD",
+"Horizontal space": "\u6C34\u5E73\u8FB9\u8DDD",
+"Border": "\u8FB9\u6846",
+"Insert image": "\u63D2\u5165\u56FE\u7247",
+"Image...": "\u56FE\u7247...",
+"Image list": "\u56FE\u7247\u5217\u8868",
+"Rotate counterclockwise": "\u9006\u65F6\u9488\u65CB\u8F6C",
+"Rotate clockwise": "\u987A\u65F6\u9488\u65CB\u8F6C",
+"Flip vertically": "\u5782\u76F4\u7FFB\u8F6C",
+"Flip horizontally": "\u6C34\u5E73\u7FFB\u8F6C",
+"Edit image": "\u7F16\u8F91\u56FE\u7247",
+"Image options": "\u56FE\u7247\u9009\u9879",
+"Zoom in": "\u653E\u5927",
+"Zoom out": "\u7F29\u5C0F",
+"Crop": "\u88C1\u526A",
+"Resize": "\u8C03\u6574\u5927\u5C0F",
+"Orientation": "\u65B9\u5411",
+"Brightness": "\u4EAE\u5EA6",
"Sharpen": "\u9510\u5316",
-"Contrast": "\u5bf9\u6bd4\u5ea6",
-"Color levels": "\u989c\u8272\u5c42\u6b21",
-"Gamma": "\u4f3d\u9a6c\u503c",
-"Invert": "\u53cd\u8f6c",
-"Apply": "\u5e94\u7528",
-"Back": "\u540e\u9000",
-"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4",
-"Date\/time": "\u65e5\u671f\/\u65f6\u95f4",
-"Insert\/Edit Link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
-"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
-"Text to display": "\u663e\u793a\u6587\u5b57",
+"Contrast": "\u5BF9\u6BD4\u5EA6",
+"Color levels": "\u989C\u8272\u5C42\u6B21",
+"Gamma": "\u4F3D\u9A6C\u503C",
+"Invert": "\u53CD\u8F6C",
+"Apply": "\u5E94\u7528",
+"Back": "\u540E\u9000",
+"Insert date/time": "\u63D2\u5165\u65E5\u671F/\u65F6\u95F4",
+"Date/time": "\u65E5\u671F/\u65F6\u95F4",
+"Insert/Edit Link": "\u63D2\u5165/\u7F16\u8F91\u94FE\u63A5",
+"Insert/edit link": "\u63D2\u5165/\u7F16\u8F91\u94FE\u63A5",
+"Text to display": "\u663E\u793A\u6587\u5B57",
"Url": "\u5730\u5740",
-"Open link in...": "\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...",
-"Current window": "\u5f53\u524d\u7a97\u53e3",
-"None": "\u65e0",
-"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
-"Remove link": "\u5220\u9664\u94fe\u63a5",
-"Anchors": "\u951a\u70b9",
-"Link...": "\u94fe\u63a5...",
-"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
-"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
-"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f",
-"Link list": "\u94fe\u63a5\u5217\u8868",
-"Insert video": "\u63d2\u5165\u89c6\u9891",
-"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891",
-"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53",
-"Alternative source": "\u955c\u50cf",
-"Alternative source URL": "\u66ff\u4ee3\u6765\u6e90\u7f51\u5740",
-"Media poster (Image URL)": "\u5c01\u9762(\u56fe\u7247\u5730\u5740)",
-"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
-"Embed": "\u5185\u5d4c",
-"Media...": "\u591a\u5a92\u4f53...",
-"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
-"Page break": "\u5206\u9875\u7b26",
-"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
-"Preview": "\u9884\u89c8",
+"Open link in...": "\u94FE\u63A5\u6253\u5F00\u4F4D\u7F6E...",
+"Current window": "\u5F53\u524D\u7A97\u53E3",
+"None": "\u65E0",
+"New window": "\u5728\u65B0\u7A97\u53E3\u6253\u5F00",
+"Remove link": "\u5220\u9664\u94FE\u63A5",
+"Anchors": "\u951A\u70B9",
+"Link...": "\u94FE\u63A5...",
+"Paste or type a link": "\u7C98\u8D34\u6216\u8F93\u5165\u94FE\u63A5",
+"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4F60\u6240\u586B\u5199\u7684URL\u5730\u5740\u4E3A\u90AE\u4EF6\u5730\u5740\uFF0C\u9700\u8981\u52A0\u4E0Amailto:\u524D\u7F00\u5417\uFF1F",
+"The URL you entered seems to be an external link. Do you want to add the required http:// prefix?": "\u4F60\u6240\u586B\u5199\u7684URL\u5730\u5740\u5C5E\u4E8E\u5916\u90E8\u94FE\u63A5\uFF0C\u9700\u8981\u52A0\u4E0Ahttp://:\u524D\u7F00\u5417\uFF1F",
+"Link list": "\u94FE\u63A5\u5217\u8868",
+"Insert video": "\u63D2\u5165\u89C6\u9891",
+"Insert/edit video": "\u63D2\u5165/\u7F16\u8F91\u89C6\u9891",
+"Insert/edit media": "\u63D2\u5165/\u7F16\u8F91\u5A92\u4F53",
+"Alternative source": "\u955C\u50CF",
+"Alternative source URL": "\u66FF\u4EE3\u6765\u6E90\u7F51\u5740",
+"Media poster (Image URL)": "\u5C01\u9762(\u56FE\u7247\u5730\u5740)",
+"Paste your embed code below:": "\u5C06\u5185\u5D4C\u4EE3\u7801\u7C98\u8D34\u5728\u4E0B\u9762:",
+"Embed": "\u5185\u5D4C",
+"Media...": "\u591A\u5A92\u4F53...",
+"Nonbreaking space": "\u4E0D\u95F4\u65AD\u7A7A\u683C",
+"Page break": "\u5206\u9875\u7B26",
+"Paste as text": "\u7C98\u8D34\u4E3A\u6587\u672C",
+"Preview": "\u9884\u89C8",
"Print...": "\u6253\u5370...",
-"Save": "\u4fdd\u5b58",
-"Find": "\u67e5\u627e",
-"Replace with": "\u66ff\u6362\u4e3a",
-"Replace": "\u66ff\u6362",
-"Replace all": "\u5168\u90e8\u66ff\u6362",
-"Previous": "\u4e0a\u4e00\u4e2a",
-"Next": "\u4e0b\u4e00\u4e2a",
-"Find and replace...": "\u67e5\u627e\u5e76\u66ff\u6362...",
-"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
-"Match case": "\u533a\u5206\u5927\u5c0f\u5199",
-"Find whole words only": "\u5168\u5b57\u5339\u914d",
-"Spell check": "\u62fc\u5199\u68c0\u67e5",
-"Ignore": "\u5ffd\u7565",
-"Ignore all": "\u5168\u90e8\u5ffd\u7565",
-"Finish": "\u5b8c\u6210",
-"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
-"Insert table": "\u63d2\u5165\u8868\u683c",
-"Table properties": "\u8868\u683c\u5c5e\u6027",
-"Delete table": "\u5220\u9664\u8868\u683c",
-"Cell": "\u5355\u5143\u683c",
-"Row": "\u884c",
+"Save": "\u4FDD\u5B58",
+"Find": "\u67E5\u627E",
+"Replace with": "\u66FF\u6362\u4E3A",
+"Replace": "\u66FF\u6362",
+"Replace all": "\u5168\u90E8\u66FF\u6362",
+"Previous": "\u4E0A\u4E00\u4E2A",
+"Next": "\u4E0B\u4E00\u4E2A",
+"Find and replace...": "\u67E5\u627E\u5E76\u66FF\u6362...",
+"Could not find the specified string.": "\u672A\u627E\u5230\u641C\u7D22\u5185\u5BB9.",
+"Match case": "\u533A\u5206\u5927\u5C0F\u5199",
+"Find whole words only": "\u5168\u5B57\u5339\u914D",
+"Spell check": "\u62FC\u5199\u68C0\u67E5",
+"Ignore": "\u5FFD\u7565",
+"Ignore all": "\u5168\u90E8\u5FFD\u7565",
+"Finish": "\u5B8C\u6210",
+"Add to Dictionary": "\u6DFB\u52A0\u5230\u5B57\u5178",
+"Insert table": "\u63D2\u5165\u8868\u683C",
+"Table properties": "\u8868\u683C\u5C5E\u6027",
+"Delete table": "\u5220\u9664\u8868\u683C",
+"Cell": "\u5355\u5143\u683C",
+"Row": "\u884C",
"Column": "\u5217",
-"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
-"Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
-"Split cell": "\u62c6\u5206\u5355\u5143\u683c",
-"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
-"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
-"Delete row": "\u5220\u9664\u884c",
-"Row properties": "\u884c\u5c5e\u6027",
-"Cut row": "\u526a\u5207\u884c",
-"Copy row": "\u590d\u5236\u884c",
-"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
-"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
-"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
-"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
+"Cell properties": "\u5355\u5143\u683C\u5C5E\u6027",
+"Merge cells": "\u5408\u5E76\u5355\u5143\u683C",
+"Split cell": "\u62C6\u5206\u5355\u5143\u683C",
+"Insert row before": "\u5728\u4E0A\u65B9\u63D2\u5165",
+"Insert row after": "\u5728\u4E0B\u65B9\u63D2\u5165",
+"Delete row": "\u5220\u9664\u884C",
+"Row properties": "\u884C\u5C5E\u6027",
+"Cut row": "\u526A\u5207\u884C",
+"Copy row": "\u590D\u5236\u884C",
+"Paste row before": "\u7C98\u8D34\u5230\u4E0A\u65B9",
+"Paste row after": "\u7C98\u8D34\u5230\u4E0B\u65B9",
+"Insert column before": "\u5728\u5DE6\u4FA7\u63D2\u5165",
+"Insert column after": "\u5728\u53F3\u4FA7\u63D2\u5165",
"Delete column": "\u5220\u9664\u5217",
"Cols": "\u5217",
-"Rows": "\u884c",
-"Width": "\u5bbd",
-"Height": "\u9ad8",
-"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
-"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
-"Show caption": "\u663e\u793a\u6807\u9898",
-"Left": "\u5de6\u5bf9\u9f50",
-"Center": "\u5c45\u4e2d",
-"Right": "\u53f3\u5bf9\u9f50",
-"Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
-"Scope": "\u8303\u56f4",
-"Alignment": "\u5bf9\u9f50\u65b9\u5f0f",
-"H Align": "\u6c34\u5e73\u5bf9\u9f50",
-"V Align": "\u5782\u76f4\u5bf9\u9f50",
-"Top": "\u9876\u90e8\u5bf9\u9f50",
-"Middle": "\u5782\u76f4\u5c45\u4e2d",
-"Bottom": "\u5e95\u90e8\u5bf9\u9f50",
-"Header cell": "\u8868\u5934\u5355\u5143\u683c",
-"Row group": "\u884c\u7ec4",
-"Column group": "\u5217\u7ec4",
-"Row type": "\u884c\u7c7b\u578b",
+"Rows": "\u884C",
+"Width": "\u5BBD",
+"Height": "\u9AD8",
+"Cell spacing": "\u5355\u5143\u683C\u5916\u95F4\u8DDD",
+"Cell padding": "\u5355\u5143\u683C\u5185\u8FB9\u8DDD",
+"Show caption": "\u663E\u793A\u6807\u9898",
+"Left": "\u5DE6\u5BF9\u9F50",
+"Center": "\u5C45\u4E2D",
+"Right": "\u53F3\u5BF9\u9F50",
+"Cell type": "\u5355\u5143\u683C\u7C7B\u578B",
+"Scope": "\u8303\u56F4",
+"Alignment": "\u5BF9\u9F50\u65B9\u5F0F",
+"H Align": "\u6C34\u5E73\u5BF9\u9F50",
+"V Align": "\u5782\u76F4\u5BF9\u9F50",
+"Top": "\u9876\u90E8\u5BF9\u9F50",
+"Middle": "\u5782\u76F4\u5C45\u4E2D",
+"Bottom": "\u5E95\u90E8\u5BF9\u9F50",
+"Header cell": "\u8868\u5934\u5355\u5143\u683C",
+"Row group": "\u884C\u7EC4",
+"Column group": "\u5217\u7EC4",
+"Row type": "\u884C\u7C7B\u578B",
"Header": "\u8868\u5934",
-"Body": "\u8868\u4f53",
-"Footer": "\u8868\u5c3e",
-"Border color": "\u8fb9\u6846\u989c\u8272",
-"Insert template...": "\u63d2\u5165\u6a21\u677f...",
-"Templates": "\u6a21\u677f",
-"Template": "\u6a21\u677f",
-"Text color": "\u6587\u5b57\u989c\u8272",
-"Background color": "\u80cc\u666f\u8272",
-"Custom...": "\u81ea\u5b9a\u4e49...",
-"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
-"No color": "\u65e0",
-"Remove color": "\u79fb\u9664\u989c\u8272",
-"Table of Contents": "\u5185\u5bb9\u5217\u8868",
-"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
-"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
-"Word count": "\u5b57\u6570",
-"Words: {0}": "\u5b57\u6570\uff1a{0}",
-"{0} words": "{0} \u5b57",
-"File": "\u6587\u4ef6",
-"Edit": "\u7f16\u8f91",
-"Insert": "\u63d2\u5165",
-"View": "\u89c6\u56fe",
-"Format": "\u683c\u5f0f",
-"Table": "\u8868\u683c",
-"Tools": "\u5de5\u5177",
-"Powered by {0}": "\u7531{0}\u9a71\u52a8",
-"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9",
-"Image title": "\u56fe\u7247\u6807\u9898",
-"Border width": "\u8fb9\u6846\u5bbd\u5ea6",
-"Border style": "\u8fb9\u6846\u6837\u5f0f",
-"Error": "\u9519\u8bef",
-"Warn": "\u8b66\u544a",
+"Body": "\u8868\u4F53",
+"Footer": "\u8868\u5C3E",
+"Border color": "\u8FB9\u6846\u989C\u8272",
+"Insert template...": "\u63D2\u5165\u6A21\u677F...",
+"Templates": "\u6A21\u677F",
+"Template": "\u6A21\u677F",
+"Text color": "\u6587\u5B57\u989C\u8272",
+"Background color": "\u80CC\u666F\u8272",
+"Custom...": "\u81EA\u5B9A\u4E49...",
+"Custom color": "\u81EA\u5B9A\u4E49\u989C\u8272",
+"No color": "\u65E0",
+"Remove color": "\u79FB\u9664\u989C\u8272",
+"Table of Contents": "\u5185\u5BB9\u5217\u8868",
+"Show blocks": "\u663E\u793A\u533A\u5757\u8FB9\u6846",
+"Show invisible characters": "\u663E\u793A\u4E0D\u53EF\u89C1\u5B57\u7B26",
+"Word count": "\u5B57\u6570",
+"Words: {0}": "\u5B57\u6570\uFF1A{0}",
+"{0} words": "{0} \u5B57",
+"File": "\u6587\u4EF6",
+"Edit": "\u7F16\u8F91",
+"Insert": "\u63D2\u5165",
+"View": "\u89C6\u56FE",
+"Format": "\u683C\u5F0F",
+"Table": "\u8868\u683C",
+"Tools": "\u5DE5\u5177",
+"Powered by {0}": "\u7531{0}\u9A71\u52A8",
+"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7F16\u8F91\u533A\u6309ALT-F9\u6253\u5F00\u83DC\u5355\uFF0C\u6309ALT-F10\u6253\u5F00\u5DE5\u5177\u680F\uFF0C\u6309ALT-0\u67E5\u770B\u5E2E\u52A9",
+"Image title": "\u56FE\u7247\u6807\u9898",
+"Border width": "\u8FB9\u6846\u5BBD\u5EA6",
+"Border style": "\u8FB9\u6846\u6837\u5F0F",
+"Error": "\u9519\u8BEF",
+"Warn": "\u8B66\u544A",
"Valid": "\u6709\u6548",
-"To open the popup, press Shift+Enter": "\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846",
-"Rich Text Area. Press ALT-0 for help.": "\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002",
-"System Font": "\u7cfb\u7edf\u5b57\u4f53",
-"Failed to upload image: {0}": "\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}",
-"Failed to load plugin: {0} from url {1}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}",
-"Failed to load plugin url: {0}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}",
-"Failed to initialize plugin: {0}": "\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}",
-"example": "\u793a\u4f8b",
-"Search": "\u641c\u7d22",
-"All": "\u5168\u90e8",
-"Currency": "\u8d27\u5e01",
-"Text": "\u6587\u5b57",
-"Quotations": "\u5f15\u7528",
-"Mathematical": "\u6570\u5b66",
-"Extended Latin": "\u62c9\u4e01\u8bed\u6269\u5145",
-"Symbols": "\u7b26\u53f7",
-"Arrows": "\u7bad\u5934",
-"User Defined": "\u81ea\u5b9a\u4e49",
-"dollar sign": "\u7f8e\u5143\u7b26\u53f7",
-"currency sign": "\u8d27\u5e01\u7b26\u53f7",
-"euro-currency sign": "\u6b27\u5143\u7b26\u53f7",
-"colon sign": "\u5192\u53f7",
-"cruzeiro sign": "\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7",
-"french franc sign": "\u6cd5\u90ce\u7b26\u53f7",
-"lira sign": "\u91cc\u62c9\u7b26\u53f7",
-"mill sign": "\u5bc6\u5c14\u7b26\u53f7",
-"naira sign": "\u5948\u62c9\u7b26\u53f7",
-"peseta sign": "\u6bd4\u585e\u5854\u7b26\u53f7",
-"rupee sign": "\u5362\u6bd4\u7b26\u53f7",
-"won sign": "\u97e9\u5143\u7b26\u53f7",
-"new sheqel sign": "\u65b0\u8c22\u514b\u5c14\u7b26\u53f7",
-"dong sign": "\u8d8a\u5357\u76fe\u7b26\u53f7",
-"kip sign": "\u8001\u631d\u57fa\u666e\u7b26\u53f7",
-"tugrik sign": "\u56fe\u683c\u91cc\u514b\u7b26\u53f7",
-"drachma sign": "\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7",
-"german penny symbol": "\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7",
-"peso sign": "\u6bd4\u7d22\u7b26\u53f7",
-"guarani sign": "\u74dc\u62c9\u5c3c\u7b26\u53f7",
-"austral sign": "\u6fb3\u5143\u7b26\u53f7",
-"hryvnia sign": "\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7",
-"cedi sign": "\u585e\u5730\u7b26\u53f7",
-"livre tournois sign": "\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7",
-"spesmilo sign": "spesmilo\u7b26\u53f7",
-"tenge sign": "\u575a\u6208\u7b26\u53f7",
-"indian rupee sign": "\u5370\u5ea6\u5362\u6bd4",
-"turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9",
-"nordic mark sign": "\u5317\u6b27\u9a6c\u514b",
-"manat sign": "\u9a6c\u7eb3\u7279\u7b26\u53f7",
-"ruble sign": "\u5362\u5e03\u7b26\u53f7",
-"yen character": "\u65e5\u5143\u5b57\u6837",
-"yuan character": "\u4eba\u6c11\u5e01\u5143\u5b57\u6837",
-"yuan character, in hong kong and taiwan": "\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09",
-"yen\/yuan character variant one": "\u5143\u5b57\u6837\uff08\u5927\u5199\uff09",
-"Loading emoticons...": "\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7...",
-"Could not load emoticons": "\u4e0d\u80fd\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7",
-"People": "\u4eba\u7c7b",
-"Animals and Nature": "\u52a8\u7269\u548c\u81ea\u7136",
-"Food and Drink": "\u98df\u7269\u548c\u996e\u54c1",
-"Activity": "\u6d3b\u52a8",
-"Travel and Places": "\u65c5\u6e38\u548c\u5730\u70b9",
-"Objects": "\u7269\u4ef6",
-"Flags": "\u65d7\u5e1c",
-"Characters": "\u5b57\u7b26",
-"Characters (no spaces)": "\u5b57\u7b26(\u65e0\u7a7a\u683c)",
-"Error: Form submit field collision.": "\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002",
-"Error: No form element found.": "\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002",
-"Update": "\u66f4\u65b0",
-"Color swatch": "\u989c\u8272\u6837\u672c",
-"Turquoise": "\u9752\u7eff\u8272",
-"Green": "\u7eff\u8272",
-"Blue": "\u84dd\u8272",
-"Purple": "\u7d2b\u8272",
-"Navy Blue": "\u6d77\u519b\u84dd",
-"Dark Turquoise": "\u6df1\u84dd\u7eff\u8272",
-"Dark Green": "\u6df1\u7eff\u8272",
-"Medium Blue": "\u4e2d\u84dd\u8272",
-"Medium Purple": "\u4e2d\u7d2b\u8272",
-"Midnight Blue": "\u6df1\u84dd\u8272",
-"Yellow": "\u9ec4\u8272",
-"Orange": "\u6a59\u8272",
-"Red": "\u7ea2\u8272",
-"Light Gray": "\u6d45\u7070\u8272",
+"To open the popup, press Shift+Enter": "\u6309Shitf+Enter\u952E\u6253\u5F00\u5BF9\u8BDD\u6846",
+"Rich Text Area. Press ALT-0 for help.": "\u7F16\u8F91\u533A\u3002\u6309Alt+0\u952E\u6253\u5F00\u5E2E\u52A9\u3002",
+"System Font": "\u7CFB\u7EDF\u5B57\u4F53",
+"Failed to upload image: {0}": "\u56FE\u7247\u4E0A\u4F20\u5931\u8D25: {0}",
+"Failed to load plugin: {0} from url {1}": "\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25: {0} \u6765\u81EA\u94FE\u63A5 {1}",
+"Failed to load plugin url: {0}": "\u63D2\u4EF6\u52A0\u8F7D\u5931\u8D25 \u94FE\u63A5: {0}",
+"Failed to initialize plugin: {0}": "\u63D2\u4EF6\u521D\u59CB\u5316\u5931\u8D25: {0}",
+"example": "\u793A\u4F8B",
+"Search": "\u641C\u7D22",
+"All": "\u5168\u90E8",
+"Currency": "\u8D27\u5E01",
+"Text": "\u6587\u5B57",
+"Quotations": "\u5F15\u7528",
+"Mathematical": "\u6570\u5B66",
+"Extended Latin": "\u62C9\u4E01\u8BED\u6269\u5145",
+"Symbols": "\u7B26\u53F7",
+"Arrows": "\u7BAD\u5934",
+"User Defined": "\u81EA\u5B9A\u4E49",
+"dollar sign": "\u7F8E\u5143\u7B26\u53F7",
+"currency sign": "\u8D27\u5E01\u7B26\u53F7",
+"euro-currency sign": "\u6B27\u5143\u7B26\u53F7",
+"colon sign": "\u5192\u53F7",
+"cruzeiro sign": "\u514B\u9C81\u8D5B\u7F57\u5E01\u7B26\u53F7",
+"french franc sign": "\u6CD5\u90CE\u7B26\u53F7",
+"lira sign": "\u91CC\u62C9\u7B26\u53F7",
+"mill sign": "\u5BC6\u5C14\u7B26\u53F7",
+"naira sign": "\u5948\u62C9\u7B26\u53F7",
+"peseta sign": "\u6BD4\u585E\u5854\u7B26\u53F7",
+"rupee sign": "\u5362\u6BD4\u7B26\u53F7",
+"won sign": "\u97E9\u5143\u7B26\u53F7",
+"new sheqel sign": "\u65B0\u8C22\u514B\u5C14\u7B26\u53F7",
+"dong sign": "\u8D8A\u5357\u76FE\u7B26\u53F7",
+"kip sign": "\u8001\u631D\u57FA\u666E\u7B26\u53F7",
+"tugrik sign": "\u56FE\u683C\u91CC\u514B\u7B26\u53F7",
+"drachma sign": "\u5FB7\u62C9\u514B\u9A6C\u7B26\u53F7",
+"german penny symbol": "\u5FB7\u56FD\u4FBF\u58EB\u7B26\u53F7",
+"peso sign": "\u6BD4\u7D22\u7B26\u53F7",
+"guarani sign": "\u74DC\u62C9\u5C3C\u7B26\u53F7",
+"austral sign": "\u6FB3\u5143\u7B26\u53F7",
+"hryvnia sign": "\u683C\u91CC\u592B\u5C3C\u4E9A\u7B26\u53F7",
+"cedi sign": "\u585E\u5730\u7B26\u53F7",
+"livre tournois sign": "\u91CC\u5F17\u5F17\u5C14\u7B26\u53F7",
+"spesmilo sign": "spesmilo\u7B26\u53F7",
+"tenge sign": "\u575A\u6208\u7B26\u53F7",
+"indian rupee sign": "\u5370\u5EA6\u5362\u6BD4",
+"turkish lira sign": "\u571F\u8033\u5176\u91CC\u62C9",
+"nordic mark sign": "\u5317\u6B27\u9A6C\u514B",
+"manat sign": "\u9A6C\u7EB3\u7279\u7B26\u53F7",
+"ruble sign": "\u5362\u5E03\u7B26\u53F7",
+"yen character": "\u65E5\u5143\u5B57\u6837",
+"yuan character": "\u4EBA\u6C11\u5E01\u5143\u5B57\u6837",
+"yuan character, in hong kong and taiwan": "\u5143\u5B57\u6837\uFF08\u6E2F\u53F0\u5730\u533A\uFF09",
+"yen/yuan character variant one": "\u5143\u5B57\u6837\uFF08\u5927\u5199\uFF09",
+"Loading emoticons...": "\u52A0\u8F7D\u8868\u60C5\u7B26\u53F7...",
+"Could not load emoticons": "\u4E0D\u80FD\u52A0\u8F7D\u8868\u60C5\u7B26\u53F7",
+"People": "\u4EBA\u7C7B",
+"Animals and Nature": "\u52A8\u7269\u548C\u81EA\u7136",
+"Food and Drink": "\u98DF\u7269\u548C\u996E\u54C1",
+"Activity": "\u6D3B\u52A8",
+"Travel and Places": "\u65C5\u6E38\u548C\u5730\u70B9",
+"Objects": "\u7269\u4EF6",
+"Flags": "\u65D7\u5E1C",
+"Characters": "\u5B57\u7B26",
+"Characters (no spaces)": "\u5B57\u7B26(\u65E0\u7A7A\u683C)",
+"Error: Form submit field collision.": "\u9519\u8BEF: \u8868\u5355\u63D0\u4EA4\u5B57\u6BB5\u51B2\u7A81\u3002",
+"Error: No form element found.": "\u9519\u8BEF: \u6CA1\u6709\u8868\u5355\u63A7\u4EF6\u3002",
+"Update": "\u66F4\u65B0",
+"Color swatch": "\u989C\u8272\u6837\u672C",
+"Turquoise": "\u9752\u7EFF\u8272",
+"Green": "\u7EFF\u8272",
+"Blue": "\u84DD\u8272",
+"Purple": "\u7D2B\u8272",
+"Navy Blue": "\u6D77\u519B\u84DD",
+"Dark Turquoise": "\u6DF1\u84DD\u7EFF\u8272",
+"Dark Green": "\u6DF1\u7EFF\u8272",
+"Medium Blue": "\u4E2D\u84DD\u8272",
+"Medium Purple": "\u4E2D\u7D2B\u8272",
+"Midnight Blue": "\u6DF1\u84DD\u8272",
+"Yellow": "\u9EC4\u8272",
+"Orange": "\u6A59\u8272",
+"Red": "\u7EA2\u8272",
+"Light Gray": "\u6D45\u7070\u8272",
"Gray": "\u7070\u8272",
-"Dark Yellow": "\u6697\u9ec4\u8272",
-"Dark Orange": "\u6df1\u6a59\u8272",
-"Dark Red": "\u6df1\u7ea2\u8272",
-"Medium Gray": "\u4e2d\u7070\u8272",
-"Dark Gray": "\u6df1\u7070\u8272",
-"Black": "\u9ed1\u8272",
-"White": "\u767d\u8272",
-"Switch to or from fullscreen mode": "\u5207\u6362\u5168\u5c4f\u6a21\u5f0f",
-"Open help dialog": "\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846",
-"history": "\u5386\u53f2",
-"styles": "\u6837\u5f0f",
-"formatting": "\u683c\u5f0f\u5316",
-"alignment": "\u5bf9\u9f50",
-"indentation": "\u7f29\u8fdb",
-"permanent pen": "\u8bb0\u53f7\u7b14",
-"comments": "\u5907\u6ce8",
-"Anchor": "\u951a\u70b9",
-"Special character": "\u7279\u6b8a\u7b26\u53f7",
-"Code sample": "\u4ee3\u7801\u793a\u4f8b",
-"Color": "\u989c\u8272",
-"Emoticons": "\u8868\u60c5",
-"Document properties": "\u6587\u6863\u5c5e\u6027",
-"Image": "\u56fe\u7247",
-"Insert link": "\u63d2\u5165\u94fe\u63a5",
-"Target": "\u6253\u5f00\u65b9\u5f0f",
-"Link": "\u94fe\u63a5",
-"Poster": "\u5c01\u9762",
-"Media": "\u5a92\u4f53",
+"Dark Yellow": "\u6697\u9EC4\u8272",
+"Dark Orange": "\u6DF1\u6A59\u8272",
+"Dark Red": "\u6DF1\u7EA2\u8272",
+"Medium Gray": "\u4E2D\u7070\u8272",
+"Dark Gray": "\u6DF1\u7070\u8272",
+"Black": "\u9ED1\u8272",
+"White": "\u767D\u8272",
+"Switch to or from fullscreen mode": "\u5207\u6362\u5168\u5C4F\u6A21\u5F0F",
+"Open help dialog": "\u6253\u5F00\u5E2E\u52A9\u5BF9\u8BDD\u6846",
+"history": "\u5386\u53F2",
+"styles": "\u6837\u5F0F",
+"formatting": "\u683C\u5F0F\u5316",
+"alignment": "\u5BF9\u9F50",
+"indentation": "\u7F29\u8FDB",
+"permanent pen": "\u8BB0\u53F7\u7B14",
+"comments": "\u5907\u6CE8",
+"Anchor": "\u951A\u70B9",
+"Special character": "\u7279\u6B8A\u7B26\u53F7",
+"Code sample": "\u4EE3\u7801\u793A\u4F8B",
+"Color": "\u989C\u8272",
+"Emoticons": "\u8868\u60C5",
+"Document properties": "\u6587\u6863\u5C5E\u6027",
+"Image": "\u56FE\u7247",
+"Insert link": "\u63D2\u5165\u94FE\u63A5",
+"Target": "\u6253\u5F00\u65B9\u5F0F",
+"Link": "\u94FE\u63A5",
+"Poster": "\u5C01\u9762",
+"Media": "\u5A92\u4F53",
"Print": "\u6253\u5370",
-"Prev": "\u4e0a\u4e00\u4e2a",
-"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
-"Whole words": "\u5168\u5b57\u5339\u914d",
-"Spellcheck": "\u62fc\u5199\u68c0\u67e5",
+"Prev": "\u4E0A\u4E00\u4E2A",
+"Find and replace": "\u67E5\u627E\u548C\u66FF\u6362",
+"Whole words": "\u5168\u5B57\u5339\u914D",
+"Spellcheck": "\u62FC\u5199\u68C0\u67E5",
"Caption": "\u6807\u9898",
-"Insert template": "\u63d2\u5165\u6a21\u677f"
+"Insert template": "\u63D2\u5165\u6A21\u677F"
});
\ No newline at end of file
diff --git a/apps/vben5/apps/app-antd/src/adapter/component/index.ts b/apps/vben5/apps/app-antd/src/adapter/component/index.ts
index 4ac7e46f3..b3bb2375f 100644
--- a/apps/vben5/apps/app-antd/src/adapter/component/index.ts
+++ b/apps/vben5/apps/app-antd/src/adapter/component/index.ts
@@ -6,14 +6,39 @@
/* eslint-disable vue/one-component-per-file */
import type {
+ AutoCompleteProps,
+ ButtonProps,
+ CascaderProps,
+ CheckboxGroupProps,
+ CheckboxProps,
+ DatePickerProps,
+ DividerProps,
+ InputNumberProps,
+ InputProps,
+ MentionsProps,
+ RadioGroupProps,
+ RadioProps,
+ RateProps,
+ SelectProps,
+ SpaceProps,
+ SwitchProps,
+ TextAreaProps,
+ TimePickerProps,
+ TreeSelectProps,
UploadChangeParam,
UploadFile,
UploadProps,
} from 'ant-design-vue';
+import type { RangePickerProps } from 'ant-design-vue/es/date-picker';
import type { Component, Ref } from 'vue';
-import type { BaseFormComponentType } from '@vben/common-ui';
+import type {
+ ApiComponentSharedProps,
+ BaseFormComponentType,
+ IconPickerProps,
+} from '@vben/common-ui';
+import type { Sortable } from '@vben/hooks';
import type { Recordable } from '@vben/types';
import {
@@ -21,6 +46,9 @@ import {
defineAsyncComponent,
defineComponent,
h,
+ nextTick,
+ onMounted,
+ onUnmounted,
ref,
render,
unref,
@@ -33,6 +61,7 @@ import {
IconPicker,
VCropper,
} from '@vben/common-ui';
+import { useSortable } from '@vben/hooks';
import { IconifyIcon } from '@vben/icons';
import { $t } from '@vben/locales';
import { isEmpty } from '@vben/utils';
@@ -40,7 +69,14 @@ import { isEmpty } from '@vben/utils';
import { FeatureStateCheck, GlobalFeatureStateCheck } from '@abp/features';
import { PermissionStateCheck } from '@abp/permissions';
import { TenantSelect } from '@abp/saas';
-import { message, Modal, notification } from 'ant-design-vue';
+import {
+ Empty,
+ InputSearch,
+ message,
+ Modal,
+ notification,
+ Tree,
+} from 'ant-design-vue';
const ColorPicker = defineAsyncComponent(() =>
import('vue3-colorpicker').then((res) => {
@@ -49,6 +85,16 @@ const ColorPicker = defineAsyncComponent(() =>
}),
);
+type AdapterUploadProps = UploadProps & {
+ aspectRatio?: string;
+ crop?: boolean;
+ draggable?: boolean;
+ handleChange?: (event: UploadChangeParam) => void;
+ maxSize?: number;
+ onDragSort?: (oldIndex: number, newIndex: number) => void;
+ onHandleChange?: (event: UploadChangeParam) => void;
+};
+
const AutoComplete = defineAsyncComponent(
() => import('ant-design-vue/es/auto-complete'),
);
@@ -63,7 +109,6 @@ const DatePicker = defineAsyncComponent(
() => import('ant-design-vue/es/date-picker'),
);
const Divider = defineAsyncComponent(() => import('ant-design-vue/es/divider'));
-const Empty = defineAsyncComponent(() => import('ant-design-vue/es/empty'));
const Input = defineAsyncComponent(() => import('ant-design-vue/es/input'));
const InputNumber = defineAsyncComponent(
() => import('ant-design-vue/es/input-number'),
@@ -71,9 +116,6 @@ const InputNumber = defineAsyncComponent(
const InputPassword = defineAsyncComponent(() =>
import('ant-design-vue/es/input').then((res) => res.InputPassword),
);
-const InputSearch = defineAsyncComponent(() =>
- import('ant-design-vue/es/input').then((res) => res.InputSearch),
-);
const Mentions = defineAsyncComponent(
() => import('ant-design-vue/es/mentions'),
);
@@ -94,7 +136,6 @@ const Textarea = defineAsyncComponent(() =>
const TimePicker = defineAsyncComponent(
() => import('ant-design-vue/es/time-picker'),
);
-const Tree = defineAsyncComponent(() => import('ant-design-vue/es/tree'));
const TreeSelect = defineAsyncComponent(
() => import('ant-design-vue/es/tree-select'),
);
@@ -107,8 +148,8 @@ const PreviewGroup = defineAsyncComponent(() =>
import('ant-design-vue/es/image').then((res) => res.ImagePreviewGroup),
);
-const withDefaultPlaceholder = (
- component: T,
+const withDefaultPlaceholder = (
+ component: Component,
type: 'input' | 'select',
componentProps: Recordable = {},
) => {
@@ -141,260 +182,261 @@ const withDefaultPlaceholder = (
});
};
-const withPreviewUpload = () => {
- // 检查是否为图片文件的辅助函数
- const isImageFile = (file: UploadFile): boolean => {
- const imageExtensions = new Set([
- 'bmp',
- 'gif',
- 'jpeg',
- 'jpg',
- 'png',
- 'svg',
- 'webp',
- ]);
- if (file.url) {
- try {
- const pathname = new URL(file.url, 'http://localhost').pathname;
- const ext = pathname.split('.').pop()?.toLowerCase();
- return ext ? imageExtensions.has(ext) : false;
- } catch {
- const ext = file.url?.split('.').pop()?.toLowerCase();
- return ext ? imageExtensions.has(ext) : false;
- }
- }
- if (!file.type) {
- const ext = file.name?.split('.').pop()?.toLowerCase();
- return ext ? imageExtensions.has(ext) : false;
+const IMAGE_EXTENSIONS = new Set([
+ 'bmp',
+ 'gif',
+ 'jpeg',
+ 'jpg',
+ 'png',
+ 'svg',
+ 'webp',
+]);
+
+/**
+ * 检查是否为图片文件
+ */
+function isImageFile(file: UploadFile): boolean {
+ if (file.url) {
+ try {
+ const pathname = new URL(file.url, 'http://localhost').pathname;
+ const ext = pathname.split('.').pop()?.toLowerCase();
+ return ext ? IMAGE_EXTENSIONS.has(ext) : false;
+ } catch {
+ const ext = file.url?.split('.').pop()?.toLowerCase();
+ return ext ? IMAGE_EXTENSIONS.has(ext) : false;
}
- return file.type.startsWith('image/');
+ }
+ if (!file.type) {
+ const ext = file.name?.split('.').pop()?.toLowerCase();
+ return ext ? IMAGE_EXTENSIONS.has(ext) : false;
+ }
+ return file.type.startsWith('image/');
+}
+
+/**
+ * 创建默认的上传按钮插槽
+ */
+function createDefaultUploadSlots(listType: string, placeholder: string) {
+ if (listType === 'picture-card') {
+ return { default: () => placeholder };
+ }
+ return {
+ default: () =>
+ h(
+ Button,
+ {
+ icon: h(IconifyIcon, {
+ icon: 'ant-design:upload-outlined',
+ class: 'mb-1 size-4',
+ }),
+ },
+ () => placeholder,
+ ),
};
- // 创建默认的上传按钮插槽
- const createDefaultSlotsWithUpload = (
- listType: string,
- placeholder: string,
- ) => {
- switch (listType) {
- case 'picture-card': {
- return {
- default: () => placeholder,
- };
- }
- default: {
- return {
- default: () =>
- h(
- Button,
- {
- icon: h(IconifyIcon, {
- icon: 'ant-design:upload-outlined',
- class: 'mb-1 size-4',
- }),
+}
+
+/**
+ * 获取文件的 Base64
+ */
+function getBase64(file: File): Promise {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsDataURL(file);
+ reader.addEventListener('load', () => resolve(reader.result as string));
+ reader.addEventListener('error', reject);
+ });
+}
+
+/**
+ * 预览图片
+ */
+async function previewImage(
+ file: UploadFile,
+ visible: Ref,
+ fileList: Ref,
+) {
+ // 非图片文件直接打开链接
+ if (!isImageFile(file)) {
+ const url = file.url || file.preview;
+ if (url) {
+ window.open(url, '_blank');
+ } else {
+ message.error($t('ui.formRules.previewWarning'));
+ }
+ return;
+ }
+
+ const [ImageComponent, PreviewGroupComponent] = await Promise.all([
+ Image,
+ PreviewGroup,
+ ]);
+
+ // 过滤图片文件并生成预览
+ const imageFiles = (unref(fileList) || []).filter((f) => isImageFile(f));
+
+ for (const imgFile of imageFiles) {
+ if (!imgFile.url && !imgFile.preview && imgFile.originFileObj) {
+ imgFile.preview = await getBase64(imgFile.originFileObj);
+ }
+ }
+
+ const container = document.createElement('div');
+ document.body.append(container);
+ let isUnmounted = false;
+
+ const currentIndex = imageFiles.findIndex((f) => f.uid === file.uid);
+
+ const PreviewWrapper = {
+ setup() {
+ return () => {
+ if (isUnmounted) return null;
+ return h(
+ PreviewGroupComponent,
+ {
+ class: 'hidden',
+ preview: {
+ visible: visible.value,
+ current: currentIndex,
+ onVisibleChange: (value: boolean) => {
+ visible.value = value;
+ if (!value) {
+ setTimeout(() => {
+ if (!isUnmounted && container) {
+ isUnmounted = true;
+ render(null, container);
+ container.remove();
+ }
+ }, 300);
+ }
},
- () => placeholder,
+ },
+ },
+ () =>
+ imageFiles.map((imgFile) =>
+ h(ImageComponent, {
+ key: imgFile.uid,
+ src: imgFile.url || imgFile.preview,
+ }),
),
- };
- }
- }
+ );
+ };
+ },
};
- // 构建预览图片组
- const previewImage = async (
- file: UploadFile,
- visible: Ref,
- fileList: Ref,
- ) => {
- // 如果当前文件不是图片,直接打开
- if (!isImageFile(file)) {
- if (file.url) {
- window.open(file.url, '_blank');
- } else if (file.preview) {
- window.open(file.preview, '_blank');
- } else {
- message.error($t('ui.formRules.previewWarning'));
- }
- return;
- }
- // 对于图片文件,继续使用预览组
- const [ImageComponent, PreviewGroupComponent] = await Promise.all([
- Image,
- PreviewGroup,
- ]);
-
- const getBase64 = (file: File) => {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.readAsDataURL(file);
- reader.addEventListener('load', () => resolve(reader.result));
- reader.addEventListener('error', (error) => reject(error));
- });
- };
- // 从fileList中过滤出所有图片文件
- const imageFiles = (unref(fileList) || []).filter((element) =>
- isImageFile(element),
- );
-
- // 为所有没有预览地址的图片生成预览
- for (const imgFile of imageFiles) {
- if (!imgFile.url && !imgFile.preview && imgFile.originFileObj) {
- imgFile.preview = (await getBase64(imgFile.originFileObj)) as string;
- }
- }
- const container: HTMLElement | null = document.createElement('div');
+ render(h(PreviewWrapper), container);
+}
+
+/**
+ * 图片裁剪操作
+ */
+function cropImage(file: File, aspectRatio: string | undefined) {
+ return new Promise((resolve, reject) => {
+ const container = document.createElement('div');
document.body.append(container);
- // 用于追踪组件是否已卸载
let isUnmounted = false;
+ let objectUrl: null | string = null;
- const PreviewWrapper = {
+ const open = ref(true);
+ const cropperRef = ref | null>(null);
+
+ const closeModal = () => {
+ open.value = false;
+ setTimeout(() => {
+ if (!isUnmounted && container) {
+ if (objectUrl) {
+ URL.revokeObjectURL(objectUrl);
+ }
+ isUnmounted = true;
+ render(null, container);
+ container.remove();
+ }
+ }, 300);
+ };
+
+ const CropperWrapper = {
setup() {
return () => {
if (isUnmounted) return null;
+ if (!objectUrl) {
+ objectUrl = URL.createObjectURL(file);
+ }
return h(
- PreviewGroupComponent,
+ Modal,
{
- class: 'hidden',
- preview: {
- visible: visible.value,
- // 设置初始显示的图片索引
- current: imageFiles.findIndex((f) => f.uid === file.uid),
- onVisibleChange: (value: boolean) => {
- visible.value = value;
- if (!value) {
- // 延迟清理,确保动画完成
- setTimeout(() => {
- if (!isUnmounted && container) {
- isUnmounted = true;
- render(null, container);
- container.remove();
- }
- }, 300);
+ open: open.value,
+ title: h('div', {}, [
+ $t('ui.crop.title'),
+ h(
+ 'span',
+ {
+ class: `${aspectRatio ? '' : 'hidden'} ml-2 text-sm text-gray-400 font-normal`,
+ },
+ $t('ui.crop.titleTip', [aspectRatio]),
+ ),
+ ]),
+ centered: true,
+ width: 548,
+ keyboard: false,
+ maskClosable: false,
+ closable: false,
+ cancelText: $t('common.cancel'),
+ okText: $t('ui.crop.confirm'),
+ destroyOnClose: true,
+ onOk: async () => {
+ const cropper = cropperRef.value;
+ if (!cropper) {
+ reject(new Error('Cropper not found'));
+ closeModal();
+ return;
+ }
+ try {
+ const dataUrl = await cropper.getCropImage();
+ if (dataUrl) {
+ resolve(dataUrl);
+ } else {
+ reject(new Error($t('ui.crop.errorTip')));
}
- },
+ } catch {
+ reject(new Error($t('ui.crop.errorTip')));
+ } finally {
+ closeModal();
+ }
+ },
+ onCancel() {
+ resolve('');
+ closeModal();
},
},
() =>
- // 渲染所有图片文件
- imageFiles.map((imgFile) =>
- h(ImageComponent, {
- key: imgFile.uid,
- src: imgFile.url || imgFile.preview,
- }),
- ),
+ h(VCropper, {
+ ref: (ref: any) => (cropperRef.value = ref),
+ img: objectUrl as string,
+ aspectRatio,
+ }),
);
};
},
};
- render(h(PreviewWrapper), container);
- };
-
- // 图片裁剪操作
- const cropImage = (file: File, aspectRatio: string | undefined) => {
- return new Promise((resolve, reject) => {
- const container: HTMLElement | null = document.createElement('div');
- document.body.append(container);
-
- // 用于追踪组件是否已卸载
- let isUnmounted = false;
- let objectUrl: null | string = null;
-
- const open = ref(true);
- const cropperRef = ref | null>(null);
-
- const closeModal = () => {
- open.value = false;
- // 延迟清理,确保动画完成
- setTimeout(() => {
- if (!isUnmounted && container) {
- if (objectUrl) {
- URL.revokeObjectURL(objectUrl);
- }
- isUnmounted = true;
- render(null, container);
- container.remove();
- }
- }, 300);
- };
-
- const CropperWrapper = {
- setup() {
- return () => {
- if (isUnmounted) return null;
- if (!objectUrl) {
- objectUrl = URL.createObjectURL(file);
- }
- return h(
- Modal,
- {
- open: open.value,
- title: h('div', {}, [
- $t('ui.crop.title'),
- h(
- 'span',
- {
- class: `${aspectRatio ? '' : 'hidden'} ml-2 text-sm text-gray-400 font-normal`,
- },
- $t('ui.crop.titleTip', [aspectRatio]),
- ),
- ]),
- centered: true,
- width: 548,
- keyboard: false,
- maskClosable: false,
- closable: false,
- cancelText: $t('common.cancel'),
- okText: $t('ui.crop.confirm'),
- destroyOnClose: true,
- onOk: async () => {
- const cropper = cropperRef.value;
- if (!cropper) {
- reject(new Error('Cropper not found'));
- closeModal();
- return;
- }
- try {
- const dataUrl = await cropper.getCropImage();
- resolve(dataUrl);
- } catch {
- reject(new Error($t('ui.crop.errorTip')));
- } finally {
- closeModal();
- }
- },
- onCancel() {
- resolve('');
- closeModal();
- },
- },
- () =>
- h(VCropper, {
- ref: (ref: any) => (cropperRef.value = ref),
- img: objectUrl as string,
- aspectRatio,
- }),
- );
- };
- },
- };
-
- render(h(CropperWrapper), container);
- });
- };
+ render(h(CropperWrapper), container);
+ });
+}
+/**
+ * 带预览功能的上传组件
+ */
+const withPreviewUpload = () => {
return defineComponent({
name: Upload.name,
emits: ['update:modelValue'],
- setup: (
+ setup(
props: any,
{ attrs, slots, emit }: { attrs: any; emit: any; slots: any },
- ) => {
+ ) {
const previewVisible = ref(false);
-
- const placeholder = attrs?.placeholder || $t(`ui.placeholder.upload`);
-
+ const placeholder = attrs?.placeholder || $t('ui.placeholder.upload');
const listType = attrs?.listType || attrs?.['list-type'] || 'text';
-
const fileList = ref(
attrs?.fileList || attrs?.['file-list'] || [],
);
@@ -408,12 +450,14 @@ const withPreviewUpload = () => {
file: UploadFile,
originFileList: Array,
) => {
+ // 文件大小限制
if (maxSize.value && (file.size || 0) / 1024 / 1024 > maxSize.value) {
message.error($t('ui.formRules.sizeLimit', [maxSize.value]));
file.status = 'removed';
return false;
}
- // 多选或者非图片不唤起裁剪框
+
+ // 图片裁剪处理
if (
attrs.crop &&
!attrs.multiple &&
@@ -421,14 +465,11 @@ const withPreviewUpload = () => {
isImageFile(file)
) {
file.status = 'removed';
- // antd Upload组件问题 file参数获取的是UploadFile类型对象无法取到File类型 所以通过originFileList[0]获取
const blob = await cropImage(originFileList[0], aspectRatio.value);
- return new Promise((resolve, reject) => {
- if (!blob) {
- return reject(new Error($t('ui.crop.errorTip')));
- }
- resolve(blob);
- });
+ if (!blob) {
+ throw new Error($t('ui.crop.errorTip'));
+ }
+ return blob;
}
return attrs.beforeUpload?.(file) ?? true;
@@ -436,12 +477,9 @@ const withPreviewUpload = () => {
const handleChange = (event: UploadChangeParam) => {
try {
- // 行内写法 handleChange: (event) => {}
attrs.handleChange?.(event);
- // template写法 @handle-change="(event) => {}"
attrs.onHandleChange?.(event);
} catch (error) {
- // Avoid breaking internal v-model sync on user handler errors
console.error(error);
}
fileList.value = event.fileList.filter(
@@ -458,21 +496,88 @@ const withPreviewUpload = () => {
await previewImage(file, previewVisible, fileList);
};
- const renderUploadButton = (): any => {
- const isDisabled = attrs.disabled;
-
- // 如果禁用,不渲染上传按钮
- if (isDisabled) {
- return null;
- }
-
- // 否则渲染默认上传按钮
+ const renderUploadButton = () => {
+ if (attrs.disabled) return null;
return isEmpty(slots)
- ? createDefaultSlotsWithUpload(listType, placeholder)
+ ? createDefaultUploadSlots(listType, placeholder)
: slots;
};
- // 可以监听到表单API设置的值
+ // 拖拽排序
+ const draggable = computed(
+ () => (attrs.draggable ?? false) && !attrs.disabled,
+ );
+ const uploadId = `upload-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
+ const sortableInstance = ref(null);
+
+ const styleId = `upload-drag-style-${uploadId}`;
+
+ function injectDragStyle() {
+ if (!document.querySelector(`[id="${styleId}"]`)) {
+ const style = document.createElement('style');
+ style.id = styleId;
+ style.textContent = `
+ [data-upload-id="${uploadId}"] .ant-upload-list-item { cursor: move; }
+ [data-upload-id="${uploadId}"] .ant-upload-list-item:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.15); }
+ `;
+ document.head.append(style);
+ }
+ }
+
+ function removeDragStyle() {
+ document.querySelector(`[id="${styleId}"]`)?.remove();
+ }
+
+ async function initSortable(retryCount = 0) {
+ if (!draggable.value) return;
+
+ injectDragStyle();
+ await nextTick();
+ await new Promise((resolve) => setTimeout(resolve, 100));
+
+ const container = document.querySelector(
+ `[data-upload-id="${uploadId}"] .ant-upload-list`,
+ ) as HTMLElement;
+
+ if (!container) {
+ if (retryCount < 5) {
+ setTimeout(() => initSortable(retryCount + 1), 200);
+ }
+ return;
+ }
+
+ const { initializeSortable } = useSortable(container, {
+ animation: 300,
+ delay: 400,
+ delayOnTouchOnly: true,
+ filter:
+ '.ant-upload-select, .ant-upload-list-item-error, .ant-upload-list-item-uploading',
+ onEnd: (evt) => {
+ const { oldIndex, newIndex } = evt;
+ if (
+ oldIndex === undefined ||
+ newIndex === undefined ||
+ oldIndex === newIndex
+ ) {
+ return;
+ }
+
+ const list = [...(fileList.value || [])];
+ const [movedItem] = list.splice(oldIndex, 1);
+ if (movedItem) {
+ list.splice(newIndex, 0, movedItem);
+ fileList.value = list;
+ }
+
+ attrs.onDragSort?.(oldIndex, newIndex);
+ emit('update:modelValue', fileList.value);
+ },
+ });
+
+ sortableInstance.value = await initializeSortable();
+ }
+
+ // 监听表单值变化
watch(
() => attrs.modelValue,
(res) => {
@@ -480,18 +585,28 @@ const withPreviewUpload = () => {
},
);
+ onMounted(initSortable);
+ onUnmounted(() => {
+ sortableInstance.value?.destroy();
+ removeDragStyle();
+ });
+
return () =>
h(
- Upload,
- {
- ...props,
- ...attrs,
- fileList: fileList.value,
- beforeUpload: handleBeforeUpload,
- onChange: handleChange,
- onPreview: handlePreview,
- },
- renderUploadButton(),
+ 'div',
+ { 'data-upload-id': uploadId, class: 'w-full' },
+ h(
+ Upload,
+ {
+ ...props,
+ ...attrs,
+ fileList: fileList.value,
+ beforeUpload: handleBeforeUpload,
+ onChange: handleChange,
+ onPreview: handlePreview,
+ },
+ renderUploadButton() as any,
+ ),
);
},
});
@@ -536,6 +651,39 @@ export type ComponentType =
| 'Upload'
| BaseFormComponentType;
+/**
+ * 与 {@link ComponentType} 中注册的组件名一一对应,便于 Schema 上 `component` + `componentProps` 联动提示
+ */
+export interface ComponentPropsMap {
+ ApiCascader: ApiComponentSharedProps & CascaderProps;
+ ApiSelect: ApiComponentSharedProps & SelectProps;
+ ApiTreeSelect: ApiComponentSharedProps & TreeSelectProps;
+ AutoComplete: AutoCompleteProps;
+ Cascader: CascaderProps;
+ Checkbox: CheckboxProps;
+ CheckboxGroup: CheckboxGroupProps;
+ DatePicker: DatePickerProps;
+ DefaultButton: ButtonProps;
+ Divider: DividerProps;
+ IconPicker: IconPickerProps;
+ Input: InputProps;
+ InputNumber: InputNumberProps;
+ InputPassword: InputProps;
+ Mentions: MentionsProps;
+ PrimaryButton: ButtonProps;
+ Radio: RadioProps;
+ RadioGroup: RadioGroupProps;
+ RangePicker: RangePickerProps;
+ Rate: RateProps;
+ Select: SelectProps;
+ Space: SpaceProps;
+ Switch: SwitchProps;
+ Textarea: TextAreaProps;
+ TimePicker: TimePickerProps;
+ TreeSelect: TreeSelectProps;
+ Upload: AdapterUploadProps;
+}
+
async function initComponentAdapter() {
const components: Partial> = {
// 如果你的组件体积比较大,可以使用异步加载
diff --git a/apps/vben5/apps/app-antd/src/adapter/request/index.ts b/apps/vben5/apps/app-antd/src/adapter/request/index.ts
index 072e67d12..8db1a7434 100644
--- a/apps/vben5/apps/app-antd/src/adapter/request/index.ts
+++ b/apps/vben5/apps/app-antd/src/adapter/request/index.ts
@@ -56,7 +56,7 @@ export function initRequestClient() {
const timezoneStore = useTimezoneStore();
if (accessStore.accessToken) {
- config.headers.Authorization = `${accessStore.accessToken}`;
+ config.headers.Authorization = accessStore.accessToken;
}
config.headers['Accept-Language'] = preferences.app.locale;
config.headers['X-Request-From'] = 'vben';
@@ -76,7 +76,7 @@ export function initRequestClient() {
});
// response数据解构
- requestClient.addResponseInterceptor({
+ requestClient.addResponseInterceptor({
fulfilled: (response) => {
const { data, status } = response;
const { hasWrapResult, getData } = useWrapperResult(response);
diff --git a/apps/vben5/apps/app-antd/src/bootstrap.ts b/apps/vben5/apps/app-antd/src/bootstrap.ts
index f75585871..5dc0bbd3d 100644
--- a/apps/vben5/apps/app-antd/src/bootstrap.ts
+++ b/apps/vben5/apps/app-antd/src/bootstrap.ts
@@ -2,6 +2,7 @@ import { createApp, watchEffect } from 'vue';
import { registerAccessDirective } from '@vben/access';
import { registerLoadingDirective } from '@vben/common-ui';
+import { providePluginsOptions } from '@vben/plugins';
import { preferences } from '@vben/preferences';
import { initStores } from '@vben/stores';
import '@vben/styles';
@@ -12,7 +13,7 @@ import { useTitle } from '@vueuse/core';
import { $t, setupI18n } from '#/locales';
import { initComponentAdapter } from './adapter/component';
-import { initSetupVbenForm } from './adapter/form';
+import { initSetupVbenForm, useVbenForm } from './adapter/form';
import { initRequestClient } from './adapter/request';
import App from './app.vue';
import { router } from './router';
@@ -25,6 +26,11 @@ async function bootstrap(namespace: string) {
// 初始化表单组件
await initSetupVbenForm();
+ // 注入插件全局配置
+ providePluginsOptions({
+ form: { useVbenForm },
+ });
+
// 初始化axios
initRequestClient();
@@ -64,6 +70,10 @@ async function bootstrap(namespace: string) {
// 配置路由及路由守卫
app.use(router);
+ // 配置@tanstack/vue-query
+ const { VueQueryPlugin } = await import('@tanstack/vue-query');
+ app.use(VueQueryPlugin);
+
// 配置Motion插件
const { MotionPlugin } = await import('@vben/plugins/motion');
app.use(MotionPlugin);
diff --git a/apps/vben5/apps/app-antd/src/layouts/auth.vue b/apps/vben5/apps/app-antd/src/layouts/auth.vue
index 18d415bc7..525e2112f 100644
--- a/apps/vben5/apps/app-antd/src/layouts/auth.vue
+++ b/apps/vben5/apps/app-antd/src/layouts/auth.vue
@@ -15,8 +15,7 @@ const logo = computed(() => preferences.logo.source);
:app-name="appName"
:logo="logo"
:page-description="$t('authentication.pageDesc')"
- :page-title="$t('authentication.pageTitle')"
- >
+ :page-title="$t('authentication.pageTitle')">
diff --git a/apps/vben5/apps/app-antd/src/layouts/basic.vue b/apps/vben5/apps/app-antd/src/layouts/basic.vue
index 507ee63d8..f3565effb 100644
--- a/apps/vben5/apps/app-antd/src/layouts/basic.vue
+++ b/apps/vben5/apps/app-antd/src/layouts/basic.vue
@@ -108,13 +108,17 @@ function handleMakeAll() {
}
async function handleLinkUser(token: string) {
- const { currentUser, currentTenant } = abpStore.application!;
const extraQueryParams: Record = {
- LinkUserId: currentUser.id!,
LinkToken: token,
};
- if (currentTenant.id) {
- extraQueryParams.LinkTenantId = currentTenant.id;
+ if (abpStore.application) {
+ const { currentUser, currentTenant } = abpStore.application;
+ if (currentUser.id) {
+ extraQueryParams.LinkUserId = currentUser.id;
+ }
+ if (currentTenant.id) {
+ extraQueryParams.LinkTenantId = currentTenant.id;
+ }
}
// 跳转登录页,交由后端处理
await authStore.oidcLogin({
@@ -173,8 +177,7 @@ watch(
:menus
:tag-text="userInfo?.email"
:text="userInfo?.realName"
- @logout="handleLogout"
- />
+ @logout="handleLogout" />
{{ description }}
@@ -186,14 +189,12 @@ watch(
:dot="showDot"
:notifications="notifications"
@clear="handleNoticeClear"
- @make-all="handleMakeAll"
- />
+ @make-all="handleMakeAll" />
+ :avatar>
diff --git a/apps/vben5/apps/app-antd/src/preferences.ts b/apps/vben5/apps/app-antd/src/preferences.ts
index 1591bd1d8..9b9f94727 100644
--- a/apps/vben5/apps/app-antd/src/preferences.ts
+++ b/apps/vben5/apps/app-antd/src/preferences.ts
@@ -1,4 +1,7 @@
-import { defineOverridesPreferences } from '@vben/preferences';
+import {
+ appCopyrightPreferences,
+ defineOverridesPreferences,
+} from '@vben/preferences';
/**
* @description 项目配置文件
@@ -13,6 +16,7 @@ export const overridesPreferences = defineOverridesPreferences({
enableRefreshToken: true,
name: import.meta.env.VITE_APP_TITLE,
},
+ copyright: appCopyrightPreferences,
logo: {
source: '/resource/img/logo.png',
},
diff --git a/apps/vben5/apps/app-antd/src/store/auth.ts b/apps/vben5/apps/app-antd/src/store/auth.ts
index 06791a955..6ac164de0 100644
--- a/apps/vben5/apps/app-antd/src/store/auth.ts
+++ b/apps/vben5/apps/app-antd/src/store/auth.ts
@@ -58,7 +58,6 @@ export const useAuthStore = defineStore('auth', () => {
accessToken: user.access_token,
tokenType: user.token_type,
refreshToken: user.refresh_token ?? '',
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
expiresIn: user.expires_in!,
});
} catch (error: any) {
@@ -81,7 +80,6 @@ export const useAuthStore = defineStore('auth', () => {
accessToken: user.access_token,
tokenType: user.token_type,
refreshToken: user.refresh_token ?? '',
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
expiresIn: user.expires_in!,
},
onSuccess,
@@ -104,7 +102,6 @@ export const useAuthStore = defineStore('auth', () => {
accessToken: user.access_token,
tokenType: user.token_type,
refreshToken: user.refresh_token ?? '',
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
expiresIn: user.expires_in!,
},
onSuccess,
@@ -132,7 +129,6 @@ export const useAuthStore = defineStore('auth', () => {
accessToken: user.access_token,
tokenType: user.token_type,
refreshToken: user.refresh_token ?? '',
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
expiresIn: user.expires_in!,
},
onSuccess,
@@ -163,7 +159,6 @@ export const useAuthStore = defineStore('auth', () => {
accessToken: user.access_token,
tokenType: user.token_type,
refreshToken: user.refresh_token ?? '',
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
expiresIn: user.expires_in!,
},
onSuccess,
@@ -190,7 +185,6 @@ export const useAuthStore = defineStore('auth', () => {
accessToken: user.access_token,
tokenType: user.token_type,
refreshToken: user.refresh_token ?? '',
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
expiresIn: user.expires_in!,
},
onSuccess,
@@ -228,7 +222,6 @@ export const useAuthStore = defineStore('auth', () => {
}
async function fetchUserInfo() {
- let userInfo: null | (UserInfo & { [key: string]: any }) = null;
let userInfoRes: { [key: string]: any } = {};
const user = await oAuthService.getUser();
if (user) {
@@ -237,7 +230,7 @@ export const useAuthStore = defineStore('auth', () => {
const abpConfig = await getConfigApi({
includeLocalizationResources: false,
});
- userInfo = {
+ const userInfo = {
userId: userInfoRes.sub ?? abpConfig.currentUser.id,
username: userInfoRes.uniqueName ?? abpConfig.currentUser.userName,
realName:
diff --git a/apps/vben5/apps/app-antd/src/views/_core/authentication/code-login.vue b/apps/vben5/apps/app-antd/src/views/_core/authentication/code-login.vue
index 8238ed31d..12ec951d2 100644
--- a/apps/vben5/apps/app-antd/src/views/_core/authentication/code-login.vue
+++ b/apps/vben5/apps/app-antd/src/views/_core/authentication/code-login.vue
@@ -86,6 +86,5 @@ async function handleLogin(values: Recordable) {
ref="codeLogin"
:form-schema="formSchema"
:loading="loading"
- @submit="handleLogin"
- />
+ @submit="handleLogin" />
diff --git a/apps/vben5/apps/app-antd/src/views/_core/authentication/forget-password.vue b/apps/vben5/apps/app-antd/src/views/_core/authentication/forget-password.vue
index 3e676213e..5676a503b 100644
--- a/apps/vben5/apps/app-antd/src/views/_core/authentication/forget-password.vue
+++ b/apps/vben5/apps/app-antd/src/views/_core/authentication/forget-password.vue
@@ -160,6 +160,5 @@ async function handleSubmit(values: Recordable) {
:form-schema="formSchema"
:submit-button-text="$t('AbpAccount.ResetPassword')"
:loading="loading"
- @submit="handleSubmit"
- />
+ @submit="handleSubmit" />
diff --git a/apps/vben5/apps/app-antd/src/views/_core/authentication/login.vue b/apps/vben5/apps/app-antd/src/views/_core/authentication/login.vue
index c414aad1d..de31f751b 100644
--- a/apps/vben5/apps/app-antd/src/views/_core/authentication/login.vue
+++ b/apps/vben5/apps/app-antd/src/views/_core/authentication/login.vue
@@ -162,8 +162,7 @@ onMounted(onInit);
:form-schema="formSchema"
:loading="authStore.loginLoading"
:show-register="isTrue('Abp.Account.IsSelfRegistrationEnabled')"
- @submit="onLogin"
- >
+ @submit="onLogin">
diff --git a/apps/vben5/apps/app-antd/src/views/_core/authentication/qrcode-login.vue b/apps/vben5/apps/app-antd/src/views/_core/authentication/qrcode-login.vue
index db1b41b6d..e114843ad 100644
--- a/apps/vben5/apps/app-antd/src/views/_core/authentication/qrcode-login.vue
+++ b/apps/vben5/apps/app-antd/src/views/_core/authentication/qrcode-login.vue
@@ -19,6 +19,5 @@ async function onConfirm(key: string, tenantId?: string) {
+ :default-avatar="preferences.app.defaultAvatar" />
diff --git a/apps/vben5/apps/app-antd/src/views/_core/authentication/register.vue b/apps/vben5/apps/app-antd/src/views/_core/authentication/register.vue
index b1a5de726..5c3dc93ff 100644
--- a/apps/vben5/apps/app-antd/src/views/_core/authentication/register.vue
+++ b/apps/vben5/apps/app-antd/src/views/_core/authentication/register.vue
@@ -91,6 +91,5 @@ function handleSubmit(value: Recordable) {
+ @submit="handleSubmit" />
diff --git a/apps/vben5/apps/app-antd/src/views/_core/authentication/two-factor-login.vue b/apps/vben5/apps/app-antd/src/views/_core/authentication/two-factor-login.vue
index cf9270deb..0736dbb24 100644
--- a/apps/vben5/apps/app-antd/src/views/_core/authentication/two-factor-login.vue
+++ b/apps/vben5/apps/app-antd/src/views/_core/authentication/two-factor-login.vue
@@ -130,33 +130,28 @@ async function onSendCode(api: () => Promise) {
ref="formRef"
:label-col="{ span: 6 }"
:model="formModel"
- :wrapper-col="{ span: 18 }"
- >
+ :wrapper-col="{ span: 18 }">
+ required>
+ allow-clear />
+ required>
+ type="email" />
@@ -164,8 +159,7 @@ async function onSendCode(api: () => Promise) {
@@ -176,8 +170,7 @@ async function onSendCode(api: () => Promise) {
+ required>
@@ -186,8 +179,7 @@ async function onSendCode(api: () => Promise) {
diff --git a/apps/vben5/apps/app-antd/src/views/account/my-settings/index.vue b/apps/vben5/apps/app-antd/src/views/account/my-settings/index.vue
index f6125b38c..f182f5671 100644
--- a/apps/vben5/apps/app-antd/src/views/account/my-settings/index.vue
+++ b/apps/vben5/apps/app-antd/src/views/account/my-settings/index.vue
@@ -105,8 +105,7 @@ async function onInit() {
+ @on-confirm="onBindComfirm" />
diff --git a/apps/vben5/apps/app-antd/src/views/dashboard/analytics/analytics-visits-sales.vue b/apps/vben5/apps/app-antd/src/views/dashboard/analytics/analytics-visits-sales.vue
index 02f509123..6ff52086f 100644
--- a/apps/vben5/apps/app-antd/src/views/dashboard/analytics/analytics-visits-sales.vue
+++ b/apps/vben5/apps/app-antd/src/views/dashboard/analytics/analytics-visits-sales.vue
@@ -24,7 +24,7 @@ onMounted(() => {
{ name: '定制', value: 310 },
{ name: '技术支持', value: 274 },
{ name: '远程', value: 400 },
- ].sort((a, b) => {
+ ].toSorted((a, b) => {
return a.value - b.value;
}),
name: '商业占比',
diff --git a/apps/vben5/apps/app-antd/src/views/demos/antd/index.vue b/apps/vben5/apps/app-antd/src/views/demos/antd/index.vue
index b3b05cc15..4d286ed5a 100644
--- a/apps/vben5/apps/app-antd/src/views/demos/antd/index.vue
+++ b/apps/vben5/apps/app-antd/src/views/demos/antd/index.vue
@@ -35,8 +35,7 @@ function notify(type: NotificationType) {
+ title="Ant Design Vue组件使用演示">
diff --git a/apps/vben5/apps/app-antd/tailwind.config.mjs b/apps/vben5/apps/app-antd/tailwind.config.mjs
deleted file mode 100644
index f17f556fa..000000000
--- a/apps/vben5/apps/app-antd/tailwind.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@vben/tailwind-config';
diff --git a/apps/vben5/apps/app-antd/tsconfig.json b/apps/vben5/apps/app-antd/tsconfig.json
index 02c287fe6..858a0ec08 100644
--- a/apps/vben5/apps/app-antd/tsconfig.json
+++ b/apps/vben5/apps/app-antd/tsconfig.json
@@ -2,7 +2,6 @@
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web-app.json",
"compilerOptions": {
- "baseUrl": ".",
"paths": {
"#/*": ["./src/*"]
}
diff --git a/apps/vben5/apps/backend-mock/api/auth/codes.ts b/apps/vben5/apps/backend-mock/api/auth/codes.ts
index 7ba012705..e610b3381 100644
--- a/apps/vben5/apps/backend-mock/api/auth/codes.ts
+++ b/apps/vben5/apps/backend-mock/api/auth/codes.ts
@@ -1,5 +1,7 @@
+import { eventHandler } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
-import { unAuthorizedResponse } from '~/utils/response';
+import { MOCK_CODES } from '~/utils/mock-data';
+import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
export default eventHandler((event) => {
const userinfo = verifyAccessToken(event);
diff --git a/apps/vben5/apps/backend-mock/api/auth/login.post.ts b/apps/vben5/apps/backend-mock/api/auth/login.post.ts
index df5737a25..e23942c46 100644
--- a/apps/vben5/apps/backend-mock/api/auth/login.post.ts
+++ b/apps/vben5/apps/backend-mock/api/auth/login.post.ts
@@ -1,9 +1,15 @@
+import { defineEventHandler, readBody, setResponseStatus } from 'h3';
import {
clearRefreshTokenCookie,
setRefreshTokenCookie,
} from '~/utils/cookie-utils';
import { generateAccessToken, generateRefreshToken } from '~/utils/jwt-utils';
-import { forbiddenResponse } from '~/utils/response';
+import { MOCK_USERS } from '~/utils/mock-data';
+import {
+ forbiddenResponse,
+ useResponseError,
+ useResponseSuccess,
+} from '~/utils/response';
export default defineEventHandler(async (event) => {
const { password, username } = await readBody(event);
diff --git a/apps/vben5/apps/backend-mock/api/auth/logout.post.ts b/apps/vben5/apps/backend-mock/api/auth/logout.post.ts
index ac6afe940..74c8d3151 100644
--- a/apps/vben5/apps/backend-mock/api/auth/logout.post.ts
+++ b/apps/vben5/apps/backend-mock/api/auth/logout.post.ts
@@ -1,7 +1,9 @@
+import { defineEventHandler } from 'h3';
import {
clearRefreshTokenCookie,
getRefreshTokenFromCookie,
} from '~/utils/cookie-utils';
+import { useResponseSuccess } from '~/utils/response';
export default defineEventHandler(async (event) => {
const refreshToken = getRefreshTokenFromCookie(event);
diff --git a/apps/vben5/apps/backend-mock/api/auth/refresh.post.ts b/apps/vben5/apps/backend-mock/api/auth/refresh.post.ts
index 7df4d34f5..7d8d3a51e 100644
--- a/apps/vben5/apps/backend-mock/api/auth/refresh.post.ts
+++ b/apps/vben5/apps/backend-mock/api/auth/refresh.post.ts
@@ -1,9 +1,11 @@
+import { defineEventHandler } from 'h3';
import {
clearRefreshTokenCookie,
getRefreshTokenFromCookie,
setRefreshTokenCookie,
} from '~/utils/cookie-utils';
-import { verifyRefreshToken } from '~/utils/jwt-utils';
+import { generateAccessToken, verifyRefreshToken } from '~/utils/jwt-utils';
+import { MOCK_USERS } from '~/utils/mock-data';
import { forbiddenResponse } from '~/utils/response';
export default defineEventHandler(async (event) => {
diff --git a/apps/vben5/apps/backend-mock/api/demo/bigint.ts b/apps/vben5/apps/backend-mock/api/demo/bigint.ts
index 880cc5ea8..00d6c28c5 100644
--- a/apps/vben5/apps/backend-mock/api/demo/bigint.ts
+++ b/apps/vben5/apps/backend-mock/api/demo/bigint.ts
@@ -1,3 +1,7 @@
+import { eventHandler, setHeader } from 'h3';
+import { verifyAccessToken } from '~/utils/jwt-utils';
+import { unAuthorizedResponse } from '~/utils/response';
+
export default eventHandler(async (event) => {
const userinfo = verifyAccessToken(event);
if (!userinfo) {
diff --git a/apps/vben5/apps/backend-mock/api/menu/all.ts b/apps/vben5/apps/backend-mock/api/menu/all.ts
index 580cee4f9..7923f7ca5 100644
--- a/apps/vben5/apps/backend-mock/api/menu/all.ts
+++ b/apps/vben5/apps/backend-mock/api/menu/all.ts
@@ -1,5 +1,7 @@
+import { eventHandler } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
-import { unAuthorizedResponse } from '~/utils/response';
+import { MOCK_MENUS } from '~/utils/mock-data';
+import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
export default eventHandler(async (event) => {
const userinfo = verifyAccessToken(event);
diff --git a/apps/vben5/apps/backend-mock/api/status.ts b/apps/vben5/apps/backend-mock/api/status.ts
index 41773e1d5..43782095d 100644
--- a/apps/vben5/apps/backend-mock/api/status.ts
+++ b/apps/vben5/apps/backend-mock/api/status.ts
@@ -1,3 +1,6 @@
+import { eventHandler, getQuery, setResponseStatus } from 'h3';
+import { useResponseError } from '~/utils/response';
+
export default eventHandler((event) => {
const { status } = getQuery(event);
setResponseStatus(event, Number(status));
diff --git a/apps/vben5/apps/backend-mock/api/system/dept/.post.ts b/apps/vben5/apps/backend-mock/api/system/dept/.post.ts
index c529ea1bd..9a4896afa 100644
--- a/apps/vben5/apps/backend-mock/api/system/dept/.post.ts
+++ b/apps/vben5/apps/backend-mock/api/system/dept/.post.ts
@@ -1,3 +1,4 @@
+import { eventHandler } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
import {
sleep,
diff --git a/apps/vben5/apps/backend-mock/api/system/dept/[id].delete.ts b/apps/vben5/apps/backend-mock/api/system/dept/[id].delete.ts
index e48f051cc..eac0f5846 100644
--- a/apps/vben5/apps/backend-mock/api/system/dept/[id].delete.ts
+++ b/apps/vben5/apps/backend-mock/api/system/dept/[id].delete.ts
@@ -1,3 +1,4 @@
+import { eventHandler } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
import {
sleep,
diff --git a/apps/vben5/apps/backend-mock/api/system/dept/[id].put.ts b/apps/vben5/apps/backend-mock/api/system/dept/[id].put.ts
index aa55c0857..6805e1395 100644
--- a/apps/vben5/apps/backend-mock/api/system/dept/[id].put.ts
+++ b/apps/vben5/apps/backend-mock/api/system/dept/[id].put.ts
@@ -1,3 +1,4 @@
+import { eventHandler } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
import {
sleep,
diff --git a/apps/vben5/apps/backend-mock/api/system/dept/list.ts b/apps/vben5/apps/backend-mock/api/system/dept/list.ts
index ae819b622..a649a0d2f 100644
--- a/apps/vben5/apps/backend-mock/api/system/dept/list.ts
+++ b/apps/vben5/apps/backend-mock/api/system/dept/list.ts
@@ -1,4 +1,5 @@
import { faker } from '@faker-js/faker';
+import { eventHandler } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
diff --git a/apps/vben5/apps/backend-mock/api/system/menu/list.ts b/apps/vben5/apps/backend-mock/api/system/menu/list.ts
index 5328b2fdf..ce96bb14e 100644
--- a/apps/vben5/apps/backend-mock/api/system/menu/list.ts
+++ b/apps/vben5/apps/backend-mock/api/system/menu/list.ts
@@ -1,3 +1,4 @@
+import { eventHandler } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
import { MOCK_MENU_LIST } from '~/utils/mock-data';
import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
diff --git a/apps/vben5/apps/backend-mock/api/system/menu/name-exists.ts b/apps/vben5/apps/backend-mock/api/system/menu/name-exists.ts
index 5599c22b3..7d5551b3b 100644
--- a/apps/vben5/apps/backend-mock/api/system/menu/name-exists.ts
+++ b/apps/vben5/apps/backend-mock/api/system/menu/name-exists.ts
@@ -1,6 +1,7 @@
+import { eventHandler, getQuery } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
import { MOCK_MENU_LIST } from '~/utils/mock-data';
-import { unAuthorizedResponse } from '~/utils/response';
+import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
const namesMap: Record = {};
diff --git a/apps/vben5/apps/backend-mock/api/system/menu/path-exists.ts b/apps/vben5/apps/backend-mock/api/system/menu/path-exists.ts
index 64774f790..f3c3be997 100644
--- a/apps/vben5/apps/backend-mock/api/system/menu/path-exists.ts
+++ b/apps/vben5/apps/backend-mock/api/system/menu/path-exists.ts
@@ -1,6 +1,7 @@
+import { eventHandler, getQuery } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
import { MOCK_MENU_LIST } from '~/utils/mock-data';
-import { unAuthorizedResponse } from '~/utils/response';
+import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
const pathMap: Record = { '/': 0 };
diff --git a/apps/vben5/apps/backend-mock/api/system/role/list.ts b/apps/vben5/apps/backend-mock/api/system/role/list.ts
index 4d5f923e9..bad29a513 100644
--- a/apps/vben5/apps/backend-mock/api/system/role/list.ts
+++ b/apps/vben5/apps/backend-mock/api/system/role/list.ts
@@ -1,4 +1,5 @@
import { faker } from '@faker-js/faker';
+import { eventHandler, getQuery } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
import { getMenuIds, MOCK_MENU_LIST } from '~/utils/mock-data';
import { unAuthorizedResponse, usePageResponseSuccess } from '~/utils/response';
diff --git a/apps/vben5/apps/backend-mock/api/system/user/list.ts b/apps/vben5/apps/backend-mock/api/system/user/list.ts
new file mode 100644
index 000000000..21d44c8ba
--- /dev/null
+++ b/apps/vben5/apps/backend-mock/api/system/user/list.ts
@@ -0,0 +1,85 @@
+import { faker } from '@faker-js/faker';
+import { eventHandler, getQuery } from 'h3';
+import { verifyAccessToken } from '~/utils/jwt-utils';
+import { unAuthorizedResponse, usePageResponseSuccess } from '~/utils/response';
+
+const formatterCN = new Intl.DateTimeFormat('zh-CN', {
+ timeZone: 'Asia/Shanghai',
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+});
+
+function generateMockDataList(count: number) {
+ const dataList = [];
+
+ for (let i = 0; i < count; i++) {
+ const dataItem: Record = {
+ id: faker.string.uuid(),
+ name: faker.commerce.product(),
+ status: faker.helpers.arrayElement([0, 1]),
+ createTime: formatterCN.format(
+ faker.date.between({ from: '2022-01-01', to: '2025-01-01' }),
+ ),
+ deptId: faker.string.uuid(),
+ remark: faker.lorem.sentence(),
+ };
+
+ dataList.push(dataItem);
+ }
+
+ return dataList;
+}
+
+const mockData = generateMockDataList(100);
+
+export default eventHandler(async (event) => {
+ const userinfo = verifyAccessToken(event);
+ if (!userinfo) {
+ return unAuthorizedResponse(event);
+ }
+
+ const {
+ page = 1,
+ pageSize = 20,
+ name,
+ id,
+ remark,
+ startTime,
+ endTime,
+ deptId,
+ status,
+ } = getQuery(event);
+ let listData = structuredClone(mockData);
+ if (name) {
+ listData = listData.filter((item) =>
+ item.name.toLowerCase().includes(String(name).toLowerCase()),
+ );
+ }
+ if (id) {
+ listData = listData.filter((item) =>
+ item.id.toLowerCase().includes(String(id).toLowerCase()),
+ );
+ }
+ if (remark) {
+ listData = listData.filter((item) =>
+ item.remark?.toLowerCase()?.includes(String(remark).toLowerCase()),
+ );
+ }
+ if (startTime) {
+ listData = listData.filter((item) => item.createTime >= startTime);
+ }
+ if (endTime) {
+ listData = listData.filter((item) => item.createTime <= endTime);
+ }
+ if (['0', '1'].includes(status as string)) {
+ listData = listData.filter((item) => item.status === Number(status));
+ }
+ if (deptId) {
+ listData = listData.filter((item) => item.deptId === deptId);
+ }
+ return usePageResponseSuccess(page as string, pageSize as string, listData);
+});
diff --git a/apps/vben5/apps/backend-mock/api/table/list.ts b/apps/vben5/apps/backend-mock/api/table/list.ts
index 3e6f705b8..f81571e3b 100644
--- a/apps/vben5/apps/backend-mock/api/table/list.ts
+++ b/apps/vben5/apps/backend-mock/api/table/list.ts
@@ -1,6 +1,11 @@
import { faker } from '@faker-js/faker';
+import { eventHandler, getQuery } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
-import { unAuthorizedResponse, usePageResponseSuccess } from '~/utils/response';
+import {
+ sleep,
+ unAuthorizedResponse,
+ usePageResponseSuccess,
+} from '~/utils/response';
function generateMockDataList(count: number) {
const dataList = [];
@@ -44,30 +49,69 @@ export default eventHandler(async (event) => {
await sleep(600);
const { page, pageSize, sortBy, sortOrder } = getQuery(event);
+ // 规范化分页参数,处理 string[]
+ const pageRaw = Array.isArray(page) ? page[0] : page;
+ const pageSizeRaw = Array.isArray(pageSize) ? pageSize[0] : pageSize;
+ const pageNumber = Math.max(
+ 1,
+ Number.parseInt(String(pageRaw ?? '1'), 10) || 1,
+ );
+ const pageSizeNumber = Math.min(
+ 100,
+ Math.max(1, Number.parseInt(String(pageSizeRaw ?? '10'), 10) || 10),
+ );
const listData = structuredClone(mockData);
- if (sortBy && Reflect.has(listData[0], sortBy as string)) {
+
+ // 规范化 query 入参,兼容 string[]
+ const sortKeyRaw = Array.isArray(sortBy) ? sortBy[0] : sortBy;
+ const sortOrderRaw = Array.isArray(sortOrder) ? sortOrder[0] : sortOrder;
+ // 检查 sortBy 是否是 listData 元素的合法属性键
+ if (
+ typeof sortKeyRaw === 'string' &&
+ listData[0] &&
+ Object.prototype.hasOwnProperty.call(listData[0], sortKeyRaw)
+ ) {
+ // 定义数组元素的类型
+ type ItemType = (typeof listData)[0];
+ const sortKey = sortKeyRaw as keyof ItemType; // 将 sortBy 断言为合法键
+ const isDesc = sortOrderRaw === 'desc';
listData.sort((a, b) => {
- if (sortOrder === 'asc') {
- if (sortBy === 'price') {
- return (
- Number.parseFloat(a[sortBy as string]) -
- Number.parseFloat(b[sortBy as string])
- );
+ const aValue = a[sortKey] as unknown;
+ const bValue = b[sortKey] as unknown;
+
+ let result: number;
+
+ if (typeof aValue === 'number' && typeof bValue === 'number') {
+ result = aValue - bValue;
+ } else if (aValue instanceof Date && bValue instanceof Date) {
+ result = aValue.getTime() - bValue.getTime();
+ } else if (typeof aValue === 'boolean' && typeof bValue === 'boolean') {
+ if (aValue === bValue) {
+ result = 0;
} else {
- return a[sortBy as string] > b[sortBy as string] ? 1 : -1;
+ result = aValue ? 1 : -1;
}
} else {
- if (sortBy === 'price') {
- return (
- Number.parseFloat(b[sortBy as string]) -
- Number.parseFloat(a[sortBy as string])
- );
- } else {
- return a[sortBy as string] < b[sortBy as string] ? 1 : -1;
- }
+ const aStr = String(aValue);
+ const bStr = String(bValue);
+ const aNum = Number(aStr);
+ const bNum = Number(bStr);
+ result =
+ Number.isFinite(aNum) && Number.isFinite(bNum)
+ ? aNum - bNum
+ : aStr.localeCompare(bStr, undefined, {
+ numeric: true,
+ sensitivity: 'base',
+ });
}
+
+ return isDesc ? -result : result;
});
}
- return usePageResponseSuccess(page as string, pageSize as string, listData);
+ return usePageResponseSuccess(
+ String(pageNumber),
+ String(pageSizeNumber),
+ listData,
+ );
});
diff --git a/apps/vben5/apps/backend-mock/api/test.get.ts b/apps/vben5/apps/backend-mock/api/test.get.ts
index ca4a500be..dc2ceef79 100644
--- a/apps/vben5/apps/backend-mock/api/test.get.ts
+++ b/apps/vben5/apps/backend-mock/api/test.get.ts
@@ -1 +1,3 @@
+import { defineEventHandler } from 'h3';
+
export default defineEventHandler(() => 'Test get handler');
diff --git a/apps/vben5/apps/backend-mock/api/test.post.ts b/apps/vben5/apps/backend-mock/api/test.post.ts
index 698cf211e..0e9e337a8 100644
--- a/apps/vben5/apps/backend-mock/api/test.post.ts
+++ b/apps/vben5/apps/backend-mock/api/test.post.ts
@@ -1 +1,3 @@
+import { defineEventHandler } from 'h3';
+
export default defineEventHandler(() => 'Test post handler');
diff --git a/apps/vben5/apps/backend-mock/api/timezone/getTimezone.ts b/apps/vben5/apps/backend-mock/api/timezone/getTimezone.ts
new file mode 100644
index 000000000..0cbcb6ec0
--- /dev/null
+++ b/apps/vben5/apps/backend-mock/api/timezone/getTimezone.ts
@@ -0,0 +1,12 @@
+import { eventHandler } from 'h3';
+import { verifyAccessToken } from '~/utils/jwt-utils';
+import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
+import { getTimezone } from '~/utils/timezone-utils';
+
+export default eventHandler((event) => {
+ const userinfo = verifyAccessToken(event);
+ if (!userinfo) {
+ return unAuthorizedResponse(event);
+ }
+ return useResponseSuccess(getTimezone());
+});
diff --git a/apps/vben5/apps/backend-mock/api/timezone/getTimezoneOptions.ts b/apps/vben5/apps/backend-mock/api/timezone/getTimezoneOptions.ts
new file mode 100644
index 000000000..6c241864e
--- /dev/null
+++ b/apps/vben5/apps/backend-mock/api/timezone/getTimezoneOptions.ts
@@ -0,0 +1,11 @@
+import { eventHandler } from 'h3';
+import { TIME_ZONE_OPTIONS } from '~/utils/mock-data';
+import { useResponseSuccess } from '~/utils/response';
+
+export default eventHandler(() => {
+ const data = TIME_ZONE_OPTIONS.map((o) => ({
+ label: `${o.timezone} (GMT${o.offset >= 0 ? `+${o.offset}` : o.offset})`,
+ value: o.timezone,
+ }));
+ return useResponseSuccess(data);
+});
diff --git a/apps/vben5/apps/backend-mock/api/timezone/setTimezone.ts b/apps/vben5/apps/backend-mock/api/timezone/setTimezone.ts
new file mode 100644
index 000000000..851e43512
--- /dev/null
+++ b/apps/vben5/apps/backend-mock/api/timezone/setTimezone.ts
@@ -0,0 +1,26 @@
+import { eventHandler, readBody, setResponseStatus } from 'h3';
+import { verifyAccessToken } from '~/utils/jwt-utils';
+import { TIME_ZONE_OPTIONS } from '~/utils/mock-data';
+import {
+ unAuthorizedResponse,
+ useResponseError,
+ useResponseSuccess,
+} from '~/utils/response';
+import { setTimezone } from '~/utils/timezone-utils';
+
+export default eventHandler(async (event) => {
+ const userinfo = verifyAccessToken(event);
+ if (!userinfo) {
+ return unAuthorizedResponse(event);
+ }
+ const body = await readBody<{ timezone?: unknown }>(event);
+ const timezone =
+ typeof body?.timezone === 'string' ? body.timezone : undefined;
+ const allowed = TIME_ZONE_OPTIONS.some((o) => o.timezone === timezone);
+ if (!timezone || !allowed) {
+ setResponseStatus(event, 400);
+ return useResponseError('Bad Request', 'Invalid timezone');
+ }
+ setTimezone(timezone);
+ return useResponseSuccess({});
+});
diff --git a/apps/vben5/apps/backend-mock/api/upload.ts b/apps/vben5/apps/backend-mock/api/upload.ts
index 1bb9e602d..436b63cbf 100644
--- a/apps/vben5/apps/backend-mock/api/upload.ts
+++ b/apps/vben5/apps/backend-mock/api/upload.ts
@@ -1,5 +1,6 @@
+import { eventHandler } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
-import { unAuthorizedResponse } from '~/utils/response';
+import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
export default eventHandler((event) => {
const userinfo = verifyAccessToken(event);
diff --git a/apps/vben5/apps/backend-mock/api/user/info.ts b/apps/vben5/apps/backend-mock/api/user/info.ts
index cfa2346cb..138cb4331 100644
--- a/apps/vben5/apps/backend-mock/api/user/info.ts
+++ b/apps/vben5/apps/backend-mock/api/user/info.ts
@@ -1,5 +1,6 @@
+import { eventHandler } from 'h3';
import { verifyAccessToken } from '~/utils/jwt-utils';
-import { unAuthorizedResponse } from '~/utils/response';
+import { unAuthorizedResponse, useResponseSuccess } from '~/utils/response';
export default eventHandler((event) => {
const userinfo = verifyAccessToken(event);
diff --git a/apps/vben5/apps/backend-mock/middleware/1.api.ts b/apps/vben5/apps/backend-mock/middleware/1.api.ts
index bad9a41a1..339cda4db 100644
--- a/apps/vben5/apps/backend-mock/middleware/1.api.ts
+++ b/apps/vben5/apps/backend-mock/middleware/1.api.ts
@@ -1,3 +1,4 @@
+import { defineEventHandler } from 'h3';
import { forbiddenResponse, sleep } from '~/utils/response';
export default defineEventHandler(async (event) => {
diff --git a/apps/vben5/apps/backend-mock/nitro.config.ts b/apps/vben5/apps/backend-mock/nitro.config.ts
index c0fc13e2e..9cc3acdb2 100644
--- a/apps/vben5/apps/backend-mock/nitro.config.ts
+++ b/apps/vben5/apps/backend-mock/nitro.config.ts
@@ -1,3 +1,5 @@
+import { defineNitroConfig } from 'nitropack/config';
+
import errorHandler from './error';
process.env.COMPATIBILITY_DATE = new Date().toISOString();
diff --git a/apps/vben5/apps/backend-mock/package.json b/apps/vben5/apps/backend-mock/package.json
index cc0b8d533..66961d4da 100644
--- a/apps/vben5/apps/backend-mock/package.json
+++ b/apps/vben5/apps/backend-mock/package.json
@@ -1,6 +1,6 @@
{
"name": "@vben/backend-mock",
- "version": "0.0.1",
+ "version": "5.7.0",
"description": "",
"private": true,
"license": "MIT",
diff --git a/apps/vben5/apps/backend-mock/routes/[...].ts b/apps/vben5/apps/backend-mock/routes/[...].ts
index 99f544b66..5a22563dc 100644
--- a/apps/vben5/apps/backend-mock/routes/[...].ts
+++ b/apps/vben5/apps/backend-mock/routes/[...].ts
@@ -1,3 +1,5 @@
+import { defineEventHandler } from 'h3';
+
export default defineEventHandler(() => {
return `
Hello Vben Admin
diff --git a/apps/vben5/apps/backend-mock/tsconfig.json b/apps/vben5/apps/backend-mock/tsconfig.json
index 43008af1c..4417b73d0 100644
--- a/apps/vben5/apps/backend-mock/tsconfig.json
+++ b/apps/vben5/apps/backend-mock/tsconfig.json
@@ -1,3 +1,11 @@
{
- "extends": "./.nitro/types/tsconfig.json"
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "extends": "@vben/tsconfig/node.json",
+ "compilerOptions": {
+ "paths": {
+ "~/*": ["./*"]
+ }
+ },
+ "include": ["**/*.ts", "**/.*.ts"],
+ "exclude": ["node_modules", "dist", ".nitro"]
}
diff --git a/apps/vben5/apps/backend-mock/utils/cookie-utils.ts b/apps/vben5/apps/backend-mock/utils/cookie-utils.ts
index 78f3aab73..187ce2f00 100644
--- a/apps/vben5/apps/backend-mock/utils/cookie-utils.ts
+++ b/apps/vben5/apps/backend-mock/utils/cookie-utils.ts
@@ -1,5 +1,7 @@
import type { EventHandlerRequest, H3Event } from 'h3';
+import { deleteCookie, getCookie, setCookie } from 'h3';
+
export function clearRefreshTokenCookie(event: H3Event) {
deleteCookie(event, 'jwt', {
httpOnly: true,
diff --git a/apps/vben5/apps/backend-mock/utils/jwt-utils.ts b/apps/vben5/apps/backend-mock/utils/jwt-utils.ts
index 8cfc68436..718583070 100644
--- a/apps/vben5/apps/backend-mock/utils/jwt-utils.ts
+++ b/apps/vben5/apps/backend-mock/utils/jwt-utils.ts
@@ -1,8 +1,11 @@
import type { EventHandlerRequest, H3Event } from 'h3';
+import type { UserInfo } from './mock-data';
+
+import { getHeader } from 'h3';
import jwt from 'jsonwebtoken';
-import { UserInfo } from './mock-data';
+import { MOCK_USERS } from './mock-data';
// TODO: Replace with your own secret key
const ACCESS_TOKEN_SECRET = 'access_token_secret';
@@ -31,12 +34,22 @@ export function verifyAccessToken(
return null;
}
- const token = authHeader.split(' ')[1];
+ const tokenParts = authHeader.split(' ');
+ if (tokenParts.length !== 2) {
+ return null;
+ }
+ const token = tokenParts[1] as string;
try {
- const decoded = jwt.verify(token, ACCESS_TOKEN_SECRET) as UserPayload;
+ const decoded = jwt.verify(
+ token,
+ ACCESS_TOKEN_SECRET,
+ ) as unknown as UserPayload;
const username = decoded.username;
const user = MOCK_USERS.find((item) => item.username === username);
+ if (!user) {
+ return null;
+ }
const { password: _pwd, ...userinfo } = user;
return userinfo;
} catch {
@@ -50,7 +63,12 @@ export function verifyRefreshToken(
try {
const decoded = jwt.verify(token, REFRESH_TOKEN_SECRET) as UserPayload;
const username = decoded.username;
- const user = MOCK_USERS.find((item) => item.username === username);
+ const user = MOCK_USERS.find(
+ (item) => item.username === username,
+ ) as UserInfo;
+ if (!user) {
+ return null;
+ }
const { password: _pwd, ...userinfo } = user;
return userinfo;
} catch {
diff --git a/apps/vben5/apps/backend-mock/utils/mock-data.ts b/apps/vben5/apps/backend-mock/utils/mock-data.ts
index 192f30a00..ee38e8ef9 100644
--- a/apps/vben5/apps/backend-mock/utils/mock-data.ts
+++ b/apps/vben5/apps/backend-mock/utils/mock-data.ts
@@ -7,6 +7,11 @@ export interface UserInfo {
homePath?: string;
}
+export interface TimezoneOption {
+ offset: number;
+ timezone: string;
+}
+
export const MOCK_USERS: UserInfo[] = [
{
id: 0,
@@ -276,7 +281,7 @@ export const MOCK_MENU_LIST = [
children: [
{
id: 20_401,
- pid: 201,
+ pid: 202,
name: 'SystemDeptCreate',
status: 1,
type: 'button',
@@ -285,7 +290,7 @@ export const MOCK_MENU_LIST = [
},
{
id: 20_402,
- pid: 201,
+ pid: 202,
name: 'SystemDeptEdit',
status: 1,
type: 'button',
@@ -294,7 +299,7 @@ export const MOCK_MENU_LIST = [
},
{
id: 20_403,
- pid: 201,
+ pid: 202,
name: 'SystemDeptDelete',
status: 1,
type: 'button',
@@ -388,3 +393,29 @@ export function getMenuIds(menus: any[]) {
});
return ids;
}
+
+/**
+ * 时区选项
+ */
+export const TIME_ZONE_OPTIONS: TimezoneOption[] = [
+ {
+ offset: -5,
+ timezone: 'America/New_York',
+ },
+ {
+ offset: 0,
+ timezone: 'Europe/London',
+ },
+ {
+ offset: 8,
+ timezone: 'Asia/Shanghai',
+ },
+ {
+ offset: 9,
+ timezone: 'Asia/Tokyo',
+ },
+ {
+ offset: 9,
+ timezone: 'Asia/Seoul',
+ },
+];
diff --git a/apps/vben5/apps/backend-mock/utils/response.ts b/apps/vben5/apps/backend-mock/utils/response.ts
index 2a5a908f2..2d4242e98 100644
--- a/apps/vben5/apps/backend-mock/utils/response.ts
+++ b/apps/vben5/apps/backend-mock/utils/response.ts
@@ -1,5 +1,7 @@
import type { EventHandlerRequest, H3Event } from 'h3';
+import { setResponseStatus } from 'h3';
+
export function useResponseSuccess(data: T) {
return {
code: 0,
diff --git a/apps/vben5/apps/backend-mock/utils/timezone-utils.ts b/apps/vben5/apps/backend-mock/utils/timezone-utils.ts
new file mode 100644
index 000000000..da35f920f
--- /dev/null
+++ b/apps/vben5/apps/backend-mock/utils/timezone-utils.ts
@@ -0,0 +1,9 @@
+let mockTimeZone: null | string = null;
+
+export const setTimezone = (timeZone: string) => {
+ mockTimeZone = timeZone;
+};
+
+export const getTimezone = () => {
+ return mockTimeZone;
+};
diff --git a/apps/vben5/apps/web-antd/index.html b/apps/vben5/apps/web-antd/index.html
index 480eb84de..5199ae0f7 100644
--- a/apps/vben5/apps/web-antd/index.html
+++ b/apps/vben5/apps/web-antd/index.html
@@ -9,10 +9,9 @@
+ content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" />
- <%= VITE_APP_TITLE %>
+ %VITE_APP_TITLE%
+ :page-title="$t('authentication.pageTitle')">
diff --git a/apps/vben5/apps/web-antd/src/layouts/basic.vue b/apps/vben5/apps/web-antd/src/layouts/basic.vue
index 1481dc5a8..720dc89f0 100644
--- a/apps/vben5/apps/web-antd/src/layouts/basic.vue
+++ b/apps/vben5/apps/web-antd/src/layouts/basic.vue
@@ -2,18 +2,19 @@
import type { NotificationItem } from '@vben/layouts';
import { computed, ref, watch } from 'vue';
+import { useRouter } from 'vue-router';
import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
import { useWatermark } from '@vben/hooks';
-import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons';
+import { BookOpenText, CircleHelp, SvgGithubIcon } from '@vben/icons';
import {
BasicLayout,
LockScreen,
Notification,
UserDropdown,
} from '@vben/layouts';
-import { preferences } from '@vben/preferences';
+import { preferences, usePreferences } from '@vben/preferences';
import { useAccessStore, useUserStore } from '@vben/stores';
import { openWindow } from '@vben/utils';
@@ -23,6 +24,7 @@ import LoginForm from '#/views/_core/authentication/login.vue';
const notifications = ref([
{
+ id: 1,
avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
date: '3小时前',
isRead: true,
@@ -30,6 +32,7 @@ const notifications = ref([
title: '收到了 14 份新周报',
},
{
+ id: 2,
avatar: 'https://avatar.vercel.sh/1',
date: '刚刚',
isRead: false,
@@ -37,6 +40,7 @@ const notifications = ref([
title: '朱偏右 回复了你',
},
{
+ id: 3,
avatar: 'https://avatar.vercel.sh/1',
date: '2024-01-01',
isRead: false,
@@ -44,23 +48,51 @@ const notifications = ref([
title: '曲丽丽 评论了你',
},
{
+ id: 4,
avatar: 'https://avatar.vercel.sh/satori',
date: '1天前',
isRead: false,
message: '描述信息描述信息描述信息',
title: '代办提醒',
},
+ {
+ id: 5,
+ avatar: 'https://avatar.vercel.sh/satori',
+ date: '1天前',
+ isRead: false,
+ message: '描述信息描述信息描述信息',
+ title: '跳转Workspace示例',
+ link: '/workspace',
+ },
+ {
+ id: 6,
+ avatar: 'https://avatar.vercel.sh/satori',
+ date: '1天前',
+ isRead: false,
+ message: '描述信息描述信息描述信息',
+ title: '跳转外部链接示例',
+ link: 'https://doc.vben.pro',
+ },
]);
+const router = useRouter();
const userStore = useUserStore();
const authStore = useAuthStore();
const accessStore = useAccessStore();
const { destroyWatermark, updateWatermark } = useWatermark();
+const { isDark } = usePreferences();
const showDot = computed(() =>
notifications.value.some((item) => !item.isRead),
);
const menus = computed(() => [
+ {
+ handler: () => {
+ router.push({ name: 'Profile' });
+ },
+ icon: 'lucide:user',
+ text: $t('page.auth.profile'),
+ },
{
handler: () => {
openWindow(VBEN_DOC_URL, {
@@ -76,7 +108,7 @@ const menus = computed(() => [
target: '_blank',
});
},
- icon: MdiGithub,
+ icon: SvgGithubIcon,
text: 'GitHub',
},
{
@@ -102,15 +134,77 @@ function handleNoticeClear() {
notifications.value = [];
}
+function markRead(id: number | string) {
+ const item = notifications.value.find((item) => item.id === id);
+ if (item) {
+ item.isRead = true;
+ }
+}
+
+function remove(id: number | string) {
+ notifications.value = notifications.value.filter((item) => item.id !== id);
+}
+
function handleMakeAll() {
notifications.value.forEach((item) => (item.isRead = true));
}
+
+const viewAll = () => {};
+
+const handleClick = (item: NotificationItem) => {
+ // 如果通知项有链接,点击时跳转
+ if (item.link) {
+ navigateTo(item.link, item.query, item.state);
+ }
+};
+
+function navigateTo(
+ link: string,
+ query?: Record,
+ state?: Record,
+) {
+ if (link.startsWith('http://') || link.startsWith('https://')) {
+ // 外部链接,在新标签页打开
+ window.open(link, '_blank');
+ } else {
+ // 内部路由链接,支持 query 参数和 state
+ router.push({
+ path: link,
+ query: query || {},
+ state,
+ });
+ }
+}
+
watch(
- () => preferences.app.watermark,
- async (enable) => {
+ () => ({
+ enable: preferences.app.watermark,
+ content: preferences.app.watermarkContent,
+ isDark: isDark.value,
+ }),
+ async ({ enable, content, isDark: isDarkValue }) => {
if (enable) {
+ const watermarkColor = isDarkValue
+ ? 'rgba(255, 255, 255, 0.12)'
+ : 'rgba(0, 0, 0, 0.12)';
+
await updateWatermark({
- content: `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
+ advancedStyle: {
+ colorStops: [
+ {
+ color: watermarkColor,
+ offset: 0,
+ },
+ {
+ color: watermarkColor,
+ offset: 1,
+ },
+ ],
+ type: 'linear',
+ },
+ content:
+ content ||
+ `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
});
} else {
destroyWatermark();
@@ -132,21 +226,23 @@ watch(
description="ann.vben@gmail.com"
tag-text="Pro"
@logout="handleLogout"
- />
+ @clear-preferences-and-logout="handleLogout" />
item.id && markRead(item.id)"
+ @remove="(item) => item.id && remove(item.id)"
@make-all="handleMakeAll"
- />
+ @on-click="handleClick"
+ @view-all="viewAll" />
+ :avatar>
diff --git a/apps/vben5/apps/web-antd/src/locales/langs/en-US/demos.json b/apps/vben5/apps/web-antd/src/locales/langs/en-US/demos.json
index 071564349..859718e1a 100644
--- a/apps/vben5/apps/web-antd/src/locales/langs/en-US/demos.json
+++ b/apps/vben5/apps/web-antd/src/locales/langs/en-US/demos.json
@@ -6,7 +6,9 @@
"about": "About",
"document": "Document",
"antdv": "Ant Design Vue Version",
+ "antdv-next": "Antdv Next Version",
"naive-ui": "Naive UI Version",
- "element-plus": "Element Plus Version"
+ "element-plus": "Element Plus Version",
+ "tdesign": "TDesign Vue Version"
}
}
diff --git a/apps/vben5/apps/web-antd/src/locales/langs/en-US/page.json b/apps/vben5/apps/web-antd/src/locales/langs/en-US/page.json
index 618a258c0..39f1641c8 100644
--- a/apps/vben5/apps/web-antd/src/locales/langs/en-US/page.json
+++ b/apps/vben5/apps/web-antd/src/locales/langs/en-US/page.json
@@ -4,7 +4,8 @@
"register": "Register",
"codeLogin": "Code Login",
"qrcodeLogin": "Qr Code Login",
- "forgetPassword": "Forget Password"
+ "forgetPassword": "Forget Password",
+ "profile": "Profile"
},
"dashboard": {
"title": "Dashboard",
diff --git a/apps/vben5/apps/web-antd/src/locales/langs/zh-CN/demos.json b/apps/vben5/apps/web-antd/src/locales/langs/zh-CN/demos.json
index 93ee722f5..29910cdf6 100644
--- a/apps/vben5/apps/web-antd/src/locales/langs/zh-CN/demos.json
+++ b/apps/vben5/apps/web-antd/src/locales/langs/zh-CN/demos.json
@@ -6,7 +6,9 @@
"about": "关于",
"document": "文档",
"antdv": "Ant Design Vue 版本",
+ "antdv-next": "Antdv Next 版本",
"naive-ui": "Naive UI 版本",
- "element-plus": "Element Plus 版本"
+ "element-plus": "Element Plus 版本",
+ "tdesign": "TDesign Vue 版本"
}
}
diff --git a/apps/vben5/apps/web-antd/src/locales/langs/zh-CN/page.json b/apps/vben5/apps/web-antd/src/locales/langs/zh-CN/page.json
index 4cb67081c..2192d1d52 100644
--- a/apps/vben5/apps/web-antd/src/locales/langs/zh-CN/page.json
+++ b/apps/vben5/apps/web-antd/src/locales/langs/zh-CN/page.json
@@ -4,7 +4,8 @@
"register": "注册",
"codeLogin": "验证码登录",
"qrcodeLogin": "二维码登录",
- "forgetPassword": "忘记密码"
+ "forgetPassword": "忘记密码",
+ "profile": "个人中心"
},
"dashboard": {
"title": "概览",
diff --git a/apps/vben5/apps/web-antd/src/main.ts b/apps/vben5/apps/web-antd/src/main.ts
index 5d728a02a..110acbd7c 100644
--- a/apps/vben5/apps/web-antd/src/main.ts
+++ b/apps/vben5/apps/web-antd/src/main.ts
@@ -1,7 +1,7 @@
import { initPreferences } from '@vben/preferences';
import { unmountGlobalLoading } from '@vben/utils';
-import { overridesPreferences } from './preferences';
+import { overridesPreferences, preferencesExtension } from './preferences';
/**
* 应用初始化完成之后再进行页面加载渲染
@@ -15,6 +15,7 @@ async function initApplication() {
// app偏好设置初始化
await initPreferences({
+ extension: preferencesExtension,
namespace,
overrides: overridesPreferences,
});
diff --git a/apps/vben5/apps/web-antd/src/preferences.ts b/apps/vben5/apps/web-antd/src/preferences.ts
index b2e9ace43..d74e734df 100644
--- a/apps/vben5/apps/web-antd/src/preferences.ts
+++ b/apps/vben5/apps/web-antd/src/preferences.ts
@@ -1,4 +1,15 @@
-import { defineOverridesPreferences } from '@vben/preferences';
+import {
+ appCopyrightPreferences,
+ defineOverridesPreferences,
+ definePreferencesExtension,
+} from '@vben/preferences';
+
+interface WebAntdPreferencesExtension {
+ defaultTableSize: number;
+ enableFormFullscreen: boolean;
+ reportTitle: string;
+ tenantMode: 'multi' | 'single';
+}
/**
* @description 项目配置文件
@@ -10,4 +21,54 @@ export const overridesPreferences = defineOverridesPreferences({
app: {
name: import.meta.env.VITE_APP_TITLE,
},
+ copyright: appCopyrightPreferences,
});
+
+export const preferencesExtension =
+ definePreferencesExtension({
+ tabLabel: 'preferences.antd.tabLabel',
+ title: 'preferences.antd.title',
+ fields: [
+ {
+ component: 'switch',
+ defaultValue: true,
+ key: 'enableFormFullscreen',
+ label: 'preferences.antd.fields.enableFormFullscreen.label',
+ tip: 'preferences.antd.fields.enableFormFullscreen.tip',
+ },
+ {
+ component: 'select',
+ defaultValue: 'single',
+ key: 'tenantMode',
+ label: 'preferences.antd.fields.tenantMode.label',
+ options: [
+ {
+ label: 'preferences.antd.fields.tenantMode.options.single.label',
+ value: 'single',
+ },
+ {
+ label: 'preferences.antd.fields.tenantMode.options.multi.label',
+ value: 'multi',
+ },
+ ],
+ },
+ {
+ component: 'number',
+ componentProps: {
+ max: 200,
+ min: 10,
+ step: 10,
+ },
+ defaultValue: 20,
+ key: 'defaultTableSize',
+ label: 'preferences.antd.fields.defaultTableSize.label',
+ },
+ {
+ component: 'input',
+ defaultValue: '',
+ key: 'reportTitle',
+ label: 'preferences.antd.fields.reportTitle.label',
+ placeholder: 'preferences.antd.fields.reportTitle.placeholder',
+ },
+ ],
+ });
diff --git a/apps/vben5/apps/web-antd/src/router/routes/modules/vben.ts b/apps/vben5/apps/web-antd/src/router/routes/modules/vben.ts
index 98acf5821..283d83e53 100644
--- a/apps/vben5/apps/web-antd/src/router/routes/modules/vben.ts
+++ b/apps/vben5/apps/web-antd/src/router/routes/modules/vben.ts
@@ -1,12 +1,15 @@
import type { RouteRecordRaw } from 'vue-router';
import {
+ VBEN_ANTDV_NEXT_PREVIEW_URL,
VBEN_DOC_URL,
VBEN_ELE_PREVIEW_URL,
VBEN_GITHUB_URL,
VBEN_LOGO_URL,
VBEN_NAIVE_PREVIEW_URL,
+ VBEN_TD_PREVIEW_URL,
} from '@vben/constants';
+import { SvgAntdvNextLogoIcon, SvgTDesignIcon } from '@vben/icons';
import { IFrameView } from '#/layouts';
import { $t } from '#/locales';
@@ -42,6 +45,17 @@ const routes: RouteRecordRaw[] = [
title: 'Github',
},
},
+ {
+ name: 'VbenAntdVNext',
+ path: '/vben-admin/antdv-next',
+ component: IFrameView,
+ meta: {
+ badgeType: 'dot',
+ icon: SvgAntdvNextLogoIcon,
+ link: VBEN_ANTDV_NEXT_PREVIEW_URL,
+ title: $t('demos.vben.antdv-next'),
+ },
+ },
{
name: 'VbenNaive',
path: '/vben-admin/naive',
@@ -53,6 +67,17 @@ const routes: RouteRecordRaw[] = [
title: $t('demos.vben.naive-ui'),
},
},
+ {
+ name: 'VbenTDesign',
+ path: '/vben-admin/tdesign',
+ component: IFrameView,
+ meta: {
+ badgeType: 'dot',
+ icon: SvgTDesignIcon,
+ link: VBEN_TD_PREVIEW_URL,
+ title: $t('demos.vben.tdesign'),
+ },
+ },
{
name: 'VbenElementPlus',
path: '/vben-admin/ele',
@@ -76,6 +101,16 @@ const routes: RouteRecordRaw[] = [
order: 9999,
},
},
+ {
+ name: 'Profile',
+ path: '/profile',
+ component: () => import('#/views/_core/profile/index.vue'),
+ meta: {
+ icon: 'lucide:user',
+ hideInMenu: true,
+ title: $t('page.auth.profile'),
+ },
+ },
];
export default routes;
diff --git a/apps/vben5/apps/web-antd/src/store/auth.ts b/apps/vben5/apps/web-antd/src/store/auth.ts
index bd496d1ee..4b93784c8 100644
--- a/apps/vben5/apps/web-antd/src/store/auth.ts
+++ b/apps/vben5/apps/web-antd/src/store/auth.ts
@@ -98,8 +98,7 @@ export const useAuthStore = defineStore('auth', () => {
}
async function fetchUserInfo() {
- let userInfo: null | UserInfo = null;
- userInfo = await getUserInfoApi();
+ const userInfo = await getUserInfoApi();
userStore.setUserInfo(userInfo);
return userInfo;
}
diff --git a/apps/vben5/apps/web-antd/src/views/_core/authentication/code-login.vue b/apps/vben5/apps/web-antd/src/views/_core/authentication/code-login.vue
index acfd1fd78..0f375895e 100644
--- a/apps/vben5/apps/web-antd/src/views/_core/authentication/code-login.vue
+++ b/apps/vben5/apps/web-antd/src/views/_core/authentication/code-login.vue
@@ -55,8 +55,7 @@ const formSchema = computed((): VbenFormSchema[] => {
* @param values 登录表单数据
*/
async function handleLogin(values: Recordable) {
- // eslint-disable-next-line no-console
- console.log(values);
+ void values;
}
@@ -64,6 +63,5 @@ async function handleLogin(values: Recordable) {
+ @submit="handleLogin" />
diff --git a/apps/vben5/apps/web-antd/src/views/_core/authentication/forget-password.vue b/apps/vben5/apps/web-antd/src/views/_core/authentication/forget-password.vue
index fef0d4279..5da4c098e 100644
--- a/apps/vben5/apps/web-antd/src/views/_core/authentication/forget-password.vue
+++ b/apps/vben5/apps/web-antd/src/views/_core/authentication/forget-password.vue
@@ -29,8 +29,7 @@ const formSchema = computed((): VbenFormSchema[] => {
});
function handleSubmit(value: Recordable) {
- // eslint-disable-next-line no-console
- console.log('reset email:', value);
+ void value;
}
@@ -38,6 +37,5 @@ function handleSubmit(value: Recordable) {
+ @submit="handleSubmit" />
diff --git a/apps/vben5/apps/web-antd/src/views/_core/authentication/login.vue b/apps/vben5/apps/web-antd/src/views/_core/authentication/login.vue
index 099e4c8c0..7d9d6f8c0 100644
--- a/apps/vben5/apps/web-antd/src/views/_core/authentication/login.vue
+++ b/apps/vben5/apps/web-antd/src/views/_core/authentication/login.vue
@@ -93,6 +93,5 @@ const formSchema = computed((): VbenFormSchema[] => {
+ @submit="authStore.authLogin" />
diff --git a/apps/vben5/apps/web-antd/src/views/_core/authentication/register.vue b/apps/vben5/apps/web-antd/src/views/_core/authentication/register.vue
index b1a5de726..2b1831352 100644
--- a/apps/vben5/apps/web-antd/src/views/_core/authentication/register.vue
+++ b/apps/vben5/apps/web-antd/src/views/_core/authentication/register.vue
@@ -82,8 +82,7 @@ const formSchema = computed((): VbenFormSchema[] => {
});
function handleSubmit(value: Recordable) {
- // eslint-disable-next-line no-console
- console.log('register submit:', value);
+ void value;
}
@@ -91,6 +90,5 @@ function handleSubmit(value: Recordable) {
+ @submit="handleSubmit" />
diff --git a/apps/vben5/apps/web-antd/src/views/_core/profile/base-setting.vue b/apps/vben5/apps/web-antd/src/views/_core/profile/base-setting.vue
new file mode 100644
index 000000000..aa8a4c260
--- /dev/null
+++ b/apps/vben5/apps/web-antd/src/views/_core/profile/base-setting.vue
@@ -0,0 +1,65 @@
+
+
+
+
diff --git a/apps/vben5/apps/web-antd/src/views/_core/profile/index.vue b/apps/vben5/apps/web-antd/src/views/_core/profile/index.vue
new file mode 100644
index 000000000..ee95282aa
--- /dev/null
+++ b/apps/vben5/apps/web-antd/src/views/_core/profile/index.vue
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/vben5/apps/web-antd/src/views/_core/profile/notification-setting.vue b/apps/vben5/apps/web-antd/src/views/_core/profile/notification-setting.vue
new file mode 100644
index 000000000..324a4b393
--- /dev/null
+++ b/apps/vben5/apps/web-antd/src/views/_core/profile/notification-setting.vue
@@ -0,0 +1,31 @@
+
+
+
+
diff --git a/apps/vben5/apps/web-antd/src/views/_core/profile/password-setting.vue b/apps/vben5/apps/web-antd/src/views/_core/profile/password-setting.vue
new file mode 100644
index 000000000..a71213120
--- /dev/null
+++ b/apps/vben5/apps/web-antd/src/views/_core/profile/password-setting.vue
@@ -0,0 +1,62 @@
+
+
+
+
diff --git a/apps/vben5/apps/web-antd/src/views/_core/profile/security-setting.vue b/apps/vben5/apps/web-antd/src/views/_core/profile/security-setting.vue
new file mode 100644
index 000000000..be30db58e
--- /dev/null
+++ b/apps/vben5/apps/web-antd/src/views/_core/profile/security-setting.vue
@@ -0,0 +1,43 @@
+
+
+
+
diff --git a/apps/vben5/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue b/apps/vben5/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue
index 02f509123..6ff52086f 100644
--- a/apps/vben5/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue
+++ b/apps/vben5/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue
@@ -24,7 +24,7 @@ onMounted(() => {
{ name: '定制', value: 310 },
{ name: '技术支持', value: 274 },
{ name: '远程', value: 400 },
- ].sort((a, b) => {
+ ].toSorted((a, b) => {
return a.value - b.value;
}),
name: '商业占比',
diff --git a/apps/vben5/apps/web-antd/src/views/dashboard/analytics/index.vue b/apps/vben5/apps/web-antd/src/views/dashboard/analytics/index.vue
index 5e3d6d285..e794c99a9 100644
--- a/apps/vben5/apps/web-antd/src/views/dashboard/analytics/index.vue
+++ b/apps/vben5/apps/web-antd/src/views/dashboard/analytics/index.vue
@@ -76,10 +76,10 @@ const chartTabs: TabOption[] = [
-
+
-
+
diff --git a/apps/vben5/apps/web-antd/src/views/dashboard/workspace/index.vue b/apps/vben5/apps/web-antd/src/views/dashboard/workspace/index.vue
index b95d61381..002304aa0 100644
--- a/apps/vben5/apps/web-antd/src/views/dashboard/workspace/index.vue
+++ b/apps/vben5/apps/web-antd/src/views/dashboard/workspace/index.vue
@@ -236,15 +236,14 @@ function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) {
+ :avatar="userStore.userInfo?.avatar || preferences.app.defaultAvatar">
早安, {{ userStore.userInfo?.realName }}, 开始您一天的工作吧!
今日晴,20℃ - 32℃!
-
+
@@ -252,10 +251,9 @@ function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) {
+ @click="navTo" />
diff --git a/apps/vben5/apps/web-antd/src/views/demos/antd/index.vue b/apps/vben5/apps/web-antd/src/views/demos/antd/index.vue
index b3b05cc15..4d286ed5a 100644
--- a/apps/vben5/apps/web-antd/src/views/demos/antd/index.vue
+++ b/apps/vben5/apps/web-antd/src/views/demos/antd/index.vue
@@ -35,8 +35,7 @@ function notify(type: NotificationType) {
+ title="Ant Design Vue组件使用演示">
diff --git a/apps/vben5/apps/web-antd/tailwind.config.mjs b/apps/vben5/apps/web-antd/tailwind.config.mjs
deleted file mode 100644
index f17f556fa..000000000
--- a/apps/vben5/apps/web-antd/tailwind.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@vben/tailwind-config';
diff --git a/apps/vben5/apps/web-antd/tsconfig.json b/apps/vben5/apps/web-antd/tsconfig.json
index 02c287fe6..858a0ec08 100644
--- a/apps/vben5/apps/web-antd/tsconfig.json
+++ b/apps/vben5/apps/web-antd/tsconfig.json
@@ -2,7 +2,6 @@
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web-app.json",
"compilerOptions": {
- "baseUrl": ".",
"paths": {
"#/*": ["./src/*"]
}
diff --git a/apps/vben5/apps/web-antd/tsconfig.node.json b/apps/vben5/apps/web-antd/tsconfig.node.json
index c2f0d86cc..36e9fb5fb 100644
--- a/apps/vben5/apps/web-antd/tsconfig.node.json
+++ b/apps/vben5/apps/web-antd/tsconfig.node.json
@@ -6,5 +6,5 @@
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"noEmit": false
},
- "include": ["vite.config.mts"]
+ "include": ["vite.config.ts"]
}
diff --git a/apps/vben5/apps/web-antd/vite.config.mts b/apps/vben5/apps/web-antd/vite.config.ts
similarity index 100%
rename from apps/vben5/apps/web-antd/vite.config.mts
rename to apps/vben5/apps/web-antd/vite.config.ts
diff --git a/apps/vben5/apps/web-antdv-next/index.html b/apps/vben5/apps/web-antdv-next/index.html
index 480eb84de..5199ae0f7 100644
--- a/apps/vben5/apps/web-antdv-next/index.html
+++ b/apps/vben5/apps/web-antdv-next/index.html
@@ -9,10 +9,9 @@
+ content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" />
- <%= VITE_APP_TITLE %>
+ %VITE_APP_TITLE%
diff --git a/apps/vben5/apps/web-antdv-next/src/layouts/auth.vue b/apps/vben5/apps/web-antdv-next/src/layouts/auth.vue
index 8ba66e85a..3c3162aca 100644
--- a/apps/vben5/apps/web-antdv-next/src/layouts/auth.vue
+++ b/apps/vben5/apps/web-antdv-next/src/layouts/auth.vue
@@ -17,8 +17,7 @@ const logoDark = computed(() => preferences.logo.sourceDark);
:logo="logo"
:logo-dark="logoDark"
:page-description="$t('authentication.pageDesc')"
- :page-title="$t('authentication.pageTitle')"
- >
+ :page-title="$t('authentication.pageTitle')">
diff --git a/apps/vben5/apps/web-antdv-next/src/layouts/basic.vue b/apps/vben5/apps/web-antdv-next/src/layouts/basic.vue
index 2226c68a2..720dc89f0 100644
--- a/apps/vben5/apps/web-antdv-next/src/layouts/basic.vue
+++ b/apps/vben5/apps/web-antdv-next/src/layouts/basic.vue
@@ -14,7 +14,7 @@ import {
Notification,
UserDropdown,
} from '@vben/layouts';
-import { preferences } from '@vben/preferences';
+import { preferences, usePreferences } from '@vben/preferences';
import { useAccessStore, useUserStore } from '@vben/stores';
import { openWindow } from '@vben/utils';
@@ -80,6 +80,7 @@ const userStore = useUserStore();
const authStore = useAuthStore();
const accessStore = useAccessStore();
const { destroyWatermark, updateWatermark } = useWatermark();
+const { isDark } = usePreferences();
const showDot = computed(() =>
notifications.value.some((item) => !item.isRead),
);
@@ -147,14 +148,60 @@ function remove(id: number | string) {
function handleMakeAll() {
notifications.value.forEach((item) => (item.isRead = true));
}
+
+const viewAll = () => {};
+
+const handleClick = (item: NotificationItem) => {
+ // 如果通知项有链接,点击时跳转
+ if (item.link) {
+ navigateTo(item.link, item.query, item.state);
+ }
+};
+
+function navigateTo(
+ link: string,
+ query?: Record,
+ state?: Record,
+) {
+ if (link.startsWith('http://') || link.startsWith('https://')) {
+ // 外部链接,在新标签页打开
+ window.open(link, '_blank');
+ } else {
+ // 内部路由链接,支持 query 参数和 state
+ router.push({
+ path: link,
+ query: query || {},
+ state,
+ });
+ }
+}
+
watch(
() => ({
enable: preferences.app.watermark,
content: preferences.app.watermarkContent,
+ isDark: isDark.value,
}),
- async ({ enable, content }) => {
+ async ({ enable, content, isDark: isDarkValue }) => {
if (enable) {
+ const watermarkColor = isDarkValue
+ ? 'rgba(255, 255, 255, 0.12)'
+ : 'rgba(0, 0, 0, 0.12)';
+
await updateWatermark({
+ advancedStyle: {
+ colorStops: [
+ {
+ color: watermarkColor,
+ offset: 0,
+ },
+ {
+ color: watermarkColor,
+ offset: 1,
+ },
+ ],
+ type: 'linear',
+ },
content:
content ||
`${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
@@ -179,7 +226,7 @@ watch(
description="ann.vben@gmail.com"
tag-text="Pro"
@logout="handleLogout"
- />
+ @clear-preferences-and-logout="handleLogout" />
item.id && markRead(item.id)"
@remove="(item) => item.id && remove(item.id)"
@make-all="handleMakeAll"
- />
+ @on-click="handleClick"
+ @view-all="viewAll" />
+ :avatar>
diff --git a/apps/vben5/apps/web-antdv-next/src/preferences.ts b/apps/vben5/apps/web-antdv-next/src/preferences.ts
index b2e9ace43..303c496e4 100644
--- a/apps/vben5/apps/web-antdv-next/src/preferences.ts
+++ b/apps/vben5/apps/web-antdv-next/src/preferences.ts
@@ -1,4 +1,7 @@
-import { defineOverridesPreferences } from '@vben/preferences';
+import {
+ appCopyrightPreferences,
+ defineOverridesPreferences,
+} from '@vben/preferences';
/**
* @description 项目配置文件
@@ -10,4 +13,5 @@ export const overridesPreferences = defineOverridesPreferences({
app: {
name: import.meta.env.VITE_APP_TITLE,
},
+ copyright: appCopyrightPreferences,
});
diff --git a/apps/vben5/apps/web-antdv-next/src/store/auth.ts b/apps/vben5/apps/web-antdv-next/src/store/auth.ts
index 6f7a3750c..90af35c8d 100644
--- a/apps/vben5/apps/web-antdv-next/src/store/auth.ts
+++ b/apps/vben5/apps/web-antdv-next/src/store/auth.ts
@@ -98,8 +98,7 @@ export const useAuthStore = defineStore('auth', () => {
}
async function fetchUserInfo() {
- let userInfo: null | UserInfo = null;
- userInfo = await getUserInfoApi();
+ const userInfo = await getUserInfoApi();
userStore.setUserInfo(userInfo);
return userInfo;
}
diff --git a/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/code-login.vue b/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/code-login.vue
index acfd1fd78..0f375895e 100644
--- a/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/code-login.vue
+++ b/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/code-login.vue
@@ -55,8 +55,7 @@ const formSchema = computed((): VbenFormSchema[] => {
* @param values 登录表单数据
*/
async function handleLogin(values: Recordable) {
- // eslint-disable-next-line no-console
- console.log(values);
+ void values;
}
@@ -64,6 +63,5 @@ async function handleLogin(values: Recordable) {
+ @submit="handleLogin" />
diff --git a/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/forget-password.vue b/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/forget-password.vue
index fef0d4279..5da4c098e 100644
--- a/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/forget-password.vue
+++ b/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/forget-password.vue
@@ -29,8 +29,7 @@ const formSchema = computed((): VbenFormSchema[] => {
});
function handleSubmit(value: Recordable) {
- // eslint-disable-next-line no-console
- console.log('reset email:', value);
+ void value;
}
@@ -38,6 +37,5 @@ function handleSubmit(value: Recordable) {
+ @submit="handleSubmit" />
diff --git a/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/login.vue b/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/login.vue
index 099e4c8c0..7d9d6f8c0 100644
--- a/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/login.vue
+++ b/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/login.vue
@@ -93,6 +93,5 @@ const formSchema = computed((): VbenFormSchema[] => {
+ @submit="authStore.authLogin" />
diff --git a/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/register.vue b/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/register.vue
index b1a5de726..2b1831352 100644
--- a/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/register.vue
+++ b/apps/vben5/apps/web-antdv-next/src/views/_core/authentication/register.vue
@@ -82,8 +82,7 @@ const formSchema = computed((): VbenFormSchema[] => {
});
function handleSubmit(value: Recordable) {
- // eslint-disable-next-line no-console
- console.log('register submit:', value);
+ void value;
}
@@ -91,6 +90,5 @@ function handleSubmit(value: Recordable) {
+ @submit="handleSubmit" />
diff --git a/apps/vben5/apps/web-antdv-next/src/views/_core/profile/index.vue b/apps/vben5/apps/web-antdv-next/src/views/_core/profile/index.vue
index 8740894e3..ee95282aa 100644
--- a/apps/vben5/apps/web-antdv-next/src/views/_core/profile/index.vue
+++ b/apps/vben5/apps/web-antdv-next/src/views/_core/profile/index.vue
@@ -37,8 +37,7 @@ const tabs = ref([
v-model:model-value="tabsValue"
title="个人中心"
:user-info="userStore.userInfo"
- :tabs="tabs"
- >
+ :tabs="tabs">
diff --git a/apps/vben5/apps/web-antdv-next/src/views/_core/profile/password-setting.vue b/apps/vben5/apps/web-antdv-next/src/views/_core/profile/password-setting.vue
index adb065a23..c68d70a4c 100644
--- a/apps/vben5/apps/web-antdv-next/src/views/_core/profile/password-setting.vue
+++ b/apps/vben5/apps/web-antdv-next/src/views/_core/profile/password-setting.vue
@@ -58,6 +58,5 @@ function handleSubmit() {
+ @submit="handleSubmit" />
diff --git a/apps/vben5/apps/web-antdv-next/src/views/dashboard/analytics/index.vue b/apps/vben5/apps/web-antdv-next/src/views/dashboard/analytics/index.vue
index 5e3d6d285..e794c99a9 100644
--- a/apps/vben5/apps/web-antdv-next/src/views/dashboard/analytics/index.vue
+++ b/apps/vben5/apps/web-antdv-next/src/views/dashboard/analytics/index.vue
@@ -76,10 +76,10 @@ const chartTabs: TabOption[] = [
-
+
-
+
diff --git a/apps/vben5/apps/web-antdv-next/src/views/dashboard/workspace/index.vue b/apps/vben5/apps/web-antdv-next/src/views/dashboard/workspace/index.vue
index b95d61381..f189349ec 100644
--- a/apps/vben5/apps/web-antdv-next/src/views/dashboard/workspace/index.vue
+++ b/apps/vben5/apps/web-antdv-next/src/views/dashboard/workspace/index.vue
@@ -236,8 +236,7 @@ function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) {
+ :avatar="userStore.userInfo?.avatar || preferences.app.defaultAvatar">
早安, {{ userStore.userInfo?.realName }}, 开始您一天的工作吧!
@@ -254,8 +253,7 @@ function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) {
:items="quickNavItems"
class="mt-5 lg:mt-0"
title="快捷导航"
- @click="navTo"
- />
+ @click="navTo" />
diff --git a/apps/vben5/apps/web-antdv-next/src/views/demos/antd/index.vue b/apps/vben5/apps/web-antdv-next/src/views/demos/antd/index.vue
index 6fb1998d6..67d50a723 100644
--- a/apps/vben5/apps/web-antdv-next/src/views/demos/antd/index.vue
+++ b/apps/vben5/apps/web-antdv-next/src/views/demos/antd/index.vue
@@ -35,8 +35,7 @@ function notify(type: NotificationType) {
+ title="Ant Design Vue组件使用演示">
diff --git a/apps/vben5/apps/web-antdv-next/tailwind.config.mjs b/apps/vben5/apps/web-antdv-next/tailwind.config.mjs
deleted file mode 100644
index f17f556fa..000000000
--- a/apps/vben5/apps/web-antdv-next/tailwind.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@vben/tailwind-config';
diff --git a/apps/vben5/apps/web-antdv-next/tsconfig.json b/apps/vben5/apps/web-antdv-next/tsconfig.json
index 02c287fe6..858a0ec08 100644
--- a/apps/vben5/apps/web-antdv-next/tsconfig.json
+++ b/apps/vben5/apps/web-antdv-next/tsconfig.json
@@ -2,7 +2,6 @@
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web-app.json",
"compilerOptions": {
- "baseUrl": ".",
"paths": {
"#/*": ["./src/*"]
}
diff --git a/apps/vben5/apps/web-antdv-next/tsconfig.node.json b/apps/vben5/apps/web-antdv-next/tsconfig.node.json
index c2f0d86cc..36e9fb5fb 100644
--- a/apps/vben5/apps/web-antdv-next/tsconfig.node.json
+++ b/apps/vben5/apps/web-antdv-next/tsconfig.node.json
@@ -6,5 +6,5 @@
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"noEmit": false
},
- "include": ["vite.config.mts"]
+ "include": ["vite.config.ts"]
}
diff --git a/apps/vben5/apps/web-antdv-next/vite.config.mts b/apps/vben5/apps/web-antdv-next/vite.config.ts
similarity index 100%
rename from apps/vben5/apps/web-antdv-next/vite.config.mts
rename to apps/vben5/apps/web-antdv-next/vite.config.ts
diff --git a/apps/vben5/apps/web-ele/index.html b/apps/vben5/apps/web-ele/index.html
index 2b59b8d75..3586891ac 100644
--- a/apps/vben5/apps/web-ele/index.html
+++ b/apps/vben5/apps/web-ele/index.html
@@ -9,10 +9,9 @@
+ content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" />
- <%= VITE_APP_TITLE %>
+ %VITE_APP_TITLE%
+ :page-title="$t('authentication.pageTitle')">
diff --git a/apps/vben5/apps/web-ele/src/layouts/basic.vue b/apps/vben5/apps/web-ele/src/layouts/basic.vue
index 1481dc5a8..720dc89f0 100644
--- a/apps/vben5/apps/web-ele/src/layouts/basic.vue
+++ b/apps/vben5/apps/web-ele/src/layouts/basic.vue
@@ -2,18 +2,19 @@
import type { NotificationItem } from '@vben/layouts';
import { computed, ref, watch } from 'vue';
+import { useRouter } from 'vue-router';
import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
import { useWatermark } from '@vben/hooks';
-import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons';
+import { BookOpenText, CircleHelp, SvgGithubIcon } from '@vben/icons';
import {
BasicLayout,
LockScreen,
Notification,
UserDropdown,
} from '@vben/layouts';
-import { preferences } from '@vben/preferences';
+import { preferences, usePreferences } from '@vben/preferences';
import { useAccessStore, useUserStore } from '@vben/stores';
import { openWindow } from '@vben/utils';
@@ -23,6 +24,7 @@ import LoginForm from '#/views/_core/authentication/login.vue';
const notifications = ref([
{
+ id: 1,
avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
date: '3小时前',
isRead: true,
@@ -30,6 +32,7 @@ const notifications = ref([
title: '收到了 14 份新周报',
},
{
+ id: 2,
avatar: 'https://avatar.vercel.sh/1',
date: '刚刚',
isRead: false,
@@ -37,6 +40,7 @@ const notifications = ref([
title: '朱偏右 回复了你',
},
{
+ id: 3,
avatar: 'https://avatar.vercel.sh/1',
date: '2024-01-01',
isRead: false,
@@ -44,23 +48,51 @@ const notifications = ref([
title: '曲丽丽 评论了你',
},
{
+ id: 4,
avatar: 'https://avatar.vercel.sh/satori',
date: '1天前',
isRead: false,
message: '描述信息描述信息描述信息',
title: '代办提醒',
},
+ {
+ id: 5,
+ avatar: 'https://avatar.vercel.sh/satori',
+ date: '1天前',
+ isRead: false,
+ message: '描述信息描述信息描述信息',
+ title: '跳转Workspace示例',
+ link: '/workspace',
+ },
+ {
+ id: 6,
+ avatar: 'https://avatar.vercel.sh/satori',
+ date: '1天前',
+ isRead: false,
+ message: '描述信息描述信息描述信息',
+ title: '跳转外部链接示例',
+ link: 'https://doc.vben.pro',
+ },
]);
+const router = useRouter();
const userStore = useUserStore();
const authStore = useAuthStore();
const accessStore = useAccessStore();
const { destroyWatermark, updateWatermark } = useWatermark();
+const { isDark } = usePreferences();
const showDot = computed(() =>
notifications.value.some((item) => !item.isRead),
);
const menus = computed(() => [
+ {
+ handler: () => {
+ router.push({ name: 'Profile' });
+ },
+ icon: 'lucide:user',
+ text: $t('page.auth.profile'),
+ },
{
handler: () => {
openWindow(VBEN_DOC_URL, {
@@ -76,7 +108,7 @@ const menus = computed(() => [
target: '_blank',
});
},
- icon: MdiGithub,
+ icon: SvgGithubIcon,
text: 'GitHub',
},
{
@@ -102,15 +134,77 @@ function handleNoticeClear() {
notifications.value = [];
}
+function markRead(id: number | string) {
+ const item = notifications.value.find((item) => item.id === id);
+ if (item) {
+ item.isRead = true;
+ }
+}
+
+function remove(id: number | string) {
+ notifications.value = notifications.value.filter((item) => item.id !== id);
+}
+
function handleMakeAll() {
notifications.value.forEach((item) => (item.isRead = true));
}
+
+const viewAll = () => {};
+
+const handleClick = (item: NotificationItem) => {
+ // 如果通知项有链接,点击时跳转
+ if (item.link) {
+ navigateTo(item.link, item.query, item.state);
+ }
+};
+
+function navigateTo(
+ link: string,
+ query?: Record,
+ state?: Record,
+) {
+ if (link.startsWith('http://') || link.startsWith('https://')) {
+ // 外部链接,在新标签页打开
+ window.open(link, '_blank');
+ } else {
+ // 内部路由链接,支持 query 参数和 state
+ router.push({
+ path: link,
+ query: query || {},
+ state,
+ });
+ }
+}
+
watch(
- () => preferences.app.watermark,
- async (enable) => {
+ () => ({
+ enable: preferences.app.watermark,
+ content: preferences.app.watermarkContent,
+ isDark: isDark.value,
+ }),
+ async ({ enable, content, isDark: isDarkValue }) => {
if (enable) {
+ const watermarkColor = isDarkValue
+ ? 'rgba(255, 255, 255, 0.12)'
+ : 'rgba(0, 0, 0, 0.12)';
+
await updateWatermark({
- content: `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
+ advancedStyle: {
+ colorStops: [
+ {
+ color: watermarkColor,
+ offset: 0,
+ },
+ {
+ color: watermarkColor,
+ offset: 1,
+ },
+ ],
+ type: 'linear',
+ },
+ content:
+ content ||
+ `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
});
} else {
destroyWatermark();
@@ -132,21 +226,23 @@ watch(
description="ann.vben@gmail.com"
tag-text="Pro"
@logout="handleLogout"
- />
+ @clear-preferences-and-logout="handleLogout" />
item.id && markRead(item.id)"
+ @remove="(item) => item.id && remove(item.id)"
@make-all="handleMakeAll"
- />
+ @on-click="handleClick"
+ @view-all="viewAll" />
+ :avatar>
diff --git a/apps/vben5/apps/web-ele/src/locales/langs/en-US/demos.json b/apps/vben5/apps/web-ele/src/locales/langs/en-US/demos.json
index 6eddebb53..e379d22a6 100644
--- a/apps/vben5/apps/web-ele/src/locales/langs/en-US/demos.json
+++ b/apps/vben5/apps/web-ele/src/locales/langs/en-US/demos.json
@@ -7,7 +7,9 @@
"about": "About",
"document": "Document",
"antdv": "Ant Design Vue Version",
+ "antdv-next": "Antdv Next Version",
"naive-ui": "Naive UI Version",
- "element-plus": "Element Plus Version"
+ "element-plus": "Element Plus Version",
+ "tdesign": "TDesign Vue Version"
}
}
diff --git a/apps/vben5/apps/web-ele/src/locales/langs/en-US/page.json b/apps/vben5/apps/web-ele/src/locales/langs/en-US/page.json
index 618a258c0..39f1641c8 100644
--- a/apps/vben5/apps/web-ele/src/locales/langs/en-US/page.json
+++ b/apps/vben5/apps/web-ele/src/locales/langs/en-US/page.json
@@ -4,7 +4,8 @@
"register": "Register",
"codeLogin": "Code Login",
"qrcodeLogin": "Qr Code Login",
- "forgetPassword": "Forget Password"
+ "forgetPassword": "Forget Password",
+ "profile": "Profile"
},
"dashboard": {
"title": "Dashboard",
diff --git a/apps/vben5/apps/web-ele/src/locales/langs/zh-CN/demos.json b/apps/vben5/apps/web-ele/src/locales/langs/zh-CN/demos.json
index ba6d6ccd0..054c447cb 100644
--- a/apps/vben5/apps/web-ele/src/locales/langs/zh-CN/demos.json
+++ b/apps/vben5/apps/web-ele/src/locales/langs/zh-CN/demos.json
@@ -7,7 +7,9 @@
"about": "关于",
"document": "文档",
"antdv": "Ant Design Vue 版本",
+ "antdv-next": "Antdv Next 版本",
"naive-ui": "Naive UI 版本",
- "element-plus": "Element Plus 版本"
+ "element-plus": "Element Plus 版本",
+ "tdesign": "TDesign Vue 版本"
}
}
diff --git a/apps/vben5/apps/web-ele/src/locales/langs/zh-CN/page.json b/apps/vben5/apps/web-ele/src/locales/langs/zh-CN/page.json
index 4cb67081c..2192d1d52 100644
--- a/apps/vben5/apps/web-ele/src/locales/langs/zh-CN/page.json
+++ b/apps/vben5/apps/web-ele/src/locales/langs/zh-CN/page.json
@@ -4,7 +4,8 @@
"register": "注册",
"codeLogin": "验证码登录",
"qrcodeLogin": "二维码登录",
- "forgetPassword": "忘记密码"
+ "forgetPassword": "忘记密码",
+ "profile": "个人中心"
},
"dashboard": {
"title": "概览",
diff --git a/apps/vben5/apps/web-ele/src/preferences.ts b/apps/vben5/apps/web-ele/src/preferences.ts
index b2e9ace43..303c496e4 100644
--- a/apps/vben5/apps/web-ele/src/preferences.ts
+++ b/apps/vben5/apps/web-ele/src/preferences.ts
@@ -1,4 +1,7 @@
-import { defineOverridesPreferences } from '@vben/preferences';
+import {
+ appCopyrightPreferences,
+ defineOverridesPreferences,
+} from '@vben/preferences';
/**
* @description 项目配置文件
@@ -10,4 +13,5 @@ export const overridesPreferences = defineOverridesPreferences({
app: {
name: import.meta.env.VITE_APP_TITLE,
},
+ copyright: appCopyrightPreferences,
});
diff --git a/apps/vben5/apps/web-ele/src/router/routes/modules/vben.ts b/apps/vben5/apps/web-ele/src/router/routes/modules/vben.ts
index 20fbc96c0..1e8176ea0 100644
--- a/apps/vben5/apps/web-ele/src/router/routes/modules/vben.ts
+++ b/apps/vben5/apps/web-ele/src/router/routes/modules/vben.ts
@@ -2,12 +2,18 @@ import type { RouteRecordRaw } from 'vue-router';
import {
VBEN_ANT_PREVIEW_URL,
+ VBEN_ANTDV_NEXT_PREVIEW_URL,
VBEN_DOC_URL,
VBEN_GITHUB_URL,
VBEN_LOGO_URL,
VBEN_NAIVE_PREVIEW_URL,
+ VBEN_TD_PREVIEW_URL,
} from '@vben/constants';
-import { SvgAntdvLogoIcon } from '@vben/icons';
+import {
+ SvgAntdvLogoIcon,
+ SvgAntdvNextLogoIcon,
+ SvgTDesignIcon,
+} from '@vben/icons';
import { IFrameView } from '#/layouts';
import { $t } from '#/locales';
@@ -65,6 +71,28 @@ const routes: RouteRecordRaw[] = [
title: $t('demos.vben.antdv'),
},
},
+ {
+ name: 'VbenAntdVNext',
+ path: '/vben-admin/antdv-next',
+ component: IFrameView,
+ meta: {
+ badgeType: 'dot',
+ icon: SvgAntdvNextLogoIcon,
+ link: VBEN_ANTDV_NEXT_PREVIEW_URL,
+ title: $t('demos.vben.antdv-next'),
+ },
+ },
+ {
+ name: 'VbenTDesign',
+ path: '/vben-admin/tdesign',
+ component: IFrameView,
+ meta: {
+ badgeType: 'dot',
+ icon: SvgTDesignIcon,
+ link: VBEN_TD_PREVIEW_URL,
+ title: $t('demos.vben.tdesign'),
+ },
+ },
],
},
{
@@ -77,6 +105,16 @@ const routes: RouteRecordRaw[] = [
order: 9999,
},
},
+ {
+ name: 'Profile',
+ path: '/profile',
+ component: () => import('#/views/_core/profile/index.vue'),
+ meta: {
+ icon: 'lucide:user',
+ hideInMenu: true,
+ title: $t('page.auth.profile'),
+ },
+ },
];
export default routes;
diff --git a/apps/vben5/apps/web-ele/src/store/auth.ts b/apps/vben5/apps/web-ele/src/store/auth.ts
index 74fadfe24..bb37051ad 100644
--- a/apps/vben5/apps/web-ele/src/store/auth.ts
+++ b/apps/vben5/apps/web-ele/src/store/auth.ts
@@ -99,8 +99,7 @@ export const useAuthStore = defineStore('auth', () => {
}
async function fetchUserInfo() {
- let userInfo: null | UserInfo = null;
- userInfo = await getUserInfoApi();
+ const userInfo = await getUserInfoApi();
userStore.setUserInfo(userInfo);
return userInfo;
}
diff --git a/apps/vben5/apps/web-ele/src/types/element-plus-style-css.d.ts b/apps/vben5/apps/web-ele/src/types/element-plus-style-css.d.ts
new file mode 100644
index 000000000..ba7a76672
--- /dev/null
+++ b/apps/vben5/apps/web-ele/src/types/element-plus-style-css.d.ts
@@ -0,0 +1,4 @@
+declare module 'element-plus/es/components/*/style/css' {
+ const sideEffect: undefined;
+ export default sideEffect;
+}
diff --git a/apps/vben5/apps/web-ele/src/views/_core/authentication/code-login.vue b/apps/vben5/apps/web-ele/src/views/_core/authentication/code-login.vue
index acfd1fd78..5dcb13671 100644
--- a/apps/vben5/apps/web-ele/src/views/_core/authentication/code-login.vue
+++ b/apps/vben5/apps/web-ele/src/views/_core/authentication/code-login.vue
@@ -40,6 +40,10 @@ const formSchema = computed((): VbenFormSchema[] => {
return text;
},
placeholder: $t('authentication.code'),
+ handleSendCode: async () => {
+ console.warn('发送验证码前校验等逻辑');
+ throw new Error('手机号校验失败');
+ },
},
fieldName: 'code',
label: $t('authentication.code'),
@@ -55,8 +59,7 @@ const formSchema = computed((): VbenFormSchema[] => {
* @param values 登录表单数据
*/
async function handleLogin(values: Recordable) {
- // eslint-disable-next-line no-console
- console.log(values);
+ void values;
}
@@ -64,6 +67,5 @@ async function handleLogin(values: Recordable) {
+ @submit="handleLogin" />
diff --git a/apps/vben5/apps/web-ele/src/views/_core/authentication/forget-password.vue b/apps/vben5/apps/web-ele/src/views/_core/authentication/forget-password.vue
index fef0d4279..5da4c098e 100644
--- a/apps/vben5/apps/web-ele/src/views/_core/authentication/forget-password.vue
+++ b/apps/vben5/apps/web-ele/src/views/_core/authentication/forget-password.vue
@@ -29,8 +29,7 @@ const formSchema = computed((): VbenFormSchema[] => {
});
function handleSubmit(value: Recordable) {
- // eslint-disable-next-line no-console
- console.log('reset email:', value);
+ void value;
}
@@ -38,6 +37,5 @@ function handleSubmit(value: Recordable) {
+ @submit="handleSubmit" />
diff --git a/apps/vben5/apps/web-ele/src/views/_core/authentication/login.vue b/apps/vben5/apps/web-ele/src/views/_core/authentication/login.vue
index 099e4c8c0..7d9d6f8c0 100644
--- a/apps/vben5/apps/web-ele/src/views/_core/authentication/login.vue
+++ b/apps/vben5/apps/web-ele/src/views/_core/authentication/login.vue
@@ -93,6 +93,5 @@ const formSchema = computed((): VbenFormSchema[] => {
+ @submit="authStore.authLogin" />
diff --git a/apps/vben5/apps/web-ele/src/views/_core/authentication/register.vue b/apps/vben5/apps/web-ele/src/views/_core/authentication/register.vue
index b1a5de726..2b1831352 100644
--- a/apps/vben5/apps/web-ele/src/views/_core/authentication/register.vue
+++ b/apps/vben5/apps/web-ele/src/views/_core/authentication/register.vue
@@ -82,8 +82,7 @@ const formSchema = computed((): VbenFormSchema[] => {
});
function handleSubmit(value: Recordable) {
- // eslint-disable-next-line no-console
- console.log('register submit:', value);
+ void value;
}
@@ -91,6 +90,5 @@ function handleSubmit(value: Recordable) {
+ @submit="handleSubmit" />
diff --git a/apps/vben5/apps/web-ele/src/views/_core/profile/base-setting.vue b/apps/vben5/apps/web-ele/src/views/_core/profile/base-setting.vue
new file mode 100644
index 000000000..aa8a4c260
--- /dev/null
+++ b/apps/vben5/apps/web-ele/src/views/_core/profile/base-setting.vue
@@ -0,0 +1,65 @@
+
+
+
+
diff --git a/apps/vben5/apps/web-ele/src/views/_core/profile/index.vue b/apps/vben5/apps/web-ele/src/views/_core/profile/index.vue
new file mode 100644
index 000000000..ee95282aa
--- /dev/null
+++ b/apps/vben5/apps/web-ele/src/views/_core/profile/index.vue
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/vben5/apps/web-ele/src/views/_core/profile/notification-setting.vue b/apps/vben5/apps/web-ele/src/views/_core/profile/notification-setting.vue
new file mode 100644
index 000000000..324a4b393
--- /dev/null
+++ b/apps/vben5/apps/web-ele/src/views/_core/profile/notification-setting.vue
@@ -0,0 +1,31 @@
+
+
+
+
diff --git a/apps/vben5/apps/web-ele/src/views/_core/profile/password-setting.vue b/apps/vben5/apps/web-ele/src/views/_core/profile/password-setting.vue
new file mode 100644
index 000000000..daa3fb69f
--- /dev/null
+++ b/apps/vben5/apps/web-ele/src/views/_core/profile/password-setting.vue
@@ -0,0 +1,62 @@
+
+
+
+
diff --git a/apps/vben5/apps/web-ele/src/views/_core/profile/security-setting.vue b/apps/vben5/apps/web-ele/src/views/_core/profile/security-setting.vue
new file mode 100644
index 000000000..be30db58e
--- /dev/null
+++ b/apps/vben5/apps/web-ele/src/views/_core/profile/security-setting.vue
@@ -0,0 +1,43 @@
+
+
+
+
diff --git a/apps/vben5/apps/web-ele/src/views/dashboard/analytics/analytics-visits-sales.vue b/apps/vben5/apps/web-ele/src/views/dashboard/analytics/analytics-visits-sales.vue
index 02f509123..6ff52086f 100644
--- a/apps/vben5/apps/web-ele/src/views/dashboard/analytics/analytics-visits-sales.vue
+++ b/apps/vben5/apps/web-ele/src/views/dashboard/analytics/analytics-visits-sales.vue
@@ -24,7 +24,7 @@ onMounted(() => {
{ name: '定制', value: 310 },
{ name: '技术支持', value: 274 },
{ name: '远程', value: 400 },
- ].sort((a, b) => {
+ ].toSorted((a, b) => {
return a.value - b.value;
}),
name: '商业占比',
diff --git a/apps/vben5/apps/web-ele/src/views/dashboard/analytics/index.vue b/apps/vben5/apps/web-ele/src/views/dashboard/analytics/index.vue
index 5e3d6d285..e794c99a9 100644
--- a/apps/vben5/apps/web-ele/src/views/dashboard/analytics/index.vue
+++ b/apps/vben5/apps/web-ele/src/views/dashboard/analytics/index.vue
@@ -76,10 +76,10 @@ const chartTabs: TabOption[] = [
-
+
-
+
diff --git a/apps/vben5/apps/web-ele/src/views/dashboard/workspace/index.vue b/apps/vben5/apps/web-ele/src/views/dashboard/workspace/index.vue
index b95d61381..f189349ec 100644
--- a/apps/vben5/apps/web-ele/src/views/dashboard/workspace/index.vue
+++ b/apps/vben5/apps/web-ele/src/views/dashboard/workspace/index.vue
@@ -236,8 +236,7 @@ function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) {
+ :avatar="userStore.userInfo?.avatar || preferences.app.defaultAvatar">
早安, {{ userStore.userInfo?.realName }}, 开始您一天的工作吧!
@@ -254,8 +253,7 @@ function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) {
:items="quickNavItems"
class="mt-5 lg:mt-0"
title="快捷导航"
- @click="navTo"
- />
+ @click="navTo" />
diff --git a/apps/vben5/apps/web-ele/src/views/demos/element/index.vue b/apps/vben5/apps/web-ele/src/views/demos/element/index.vue
index 0a7012d63..3927d5c0e 100644
--- a/apps/vben5/apps/web-ele/src/views/demos/element/index.vue
+++ b/apps/vben5/apps/web-ele/src/views/demos/element/index.vue
@@ -59,8 +59,7 @@ const segmentedOptions = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
+ title="Element Plus组件使用演示">
按钮
@@ -97,14 +96,11 @@ const segmentedOptions = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
+ size="large" />
V-Loading
-
- 一些演示的内容
-
+ 一些演示的内容
diff --git a/apps/vben5/apps/web-ele/src/views/demos/form/basic.vue b/apps/vben5/apps/web-ele/src/views/demos/form/basic.vue
index 0ecab5864..66ecda2f6 100644
--- a/apps/vben5/apps/web-ele/src/views/demos/form/basic.vue
+++ b/apps/vben5/apps/web-ele/src/views/demos/form/basic.vue
@@ -173,9 +173,8 @@ function setFormValues() {
-
+ title="表单演示">
+
diff --git a/apps/vben5/apps/web-ele/tailwind.config.mjs b/apps/vben5/apps/web-ele/tailwind.config.mjs
deleted file mode 100644
index f17f556fa..000000000
--- a/apps/vben5/apps/web-ele/tailwind.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@vben/tailwind-config';
diff --git a/apps/vben5/apps/web-ele/tsconfig.json b/apps/vben5/apps/web-ele/tsconfig.json
index 02c287fe6..858a0ec08 100644
--- a/apps/vben5/apps/web-ele/tsconfig.json
+++ b/apps/vben5/apps/web-ele/tsconfig.json
@@ -2,7 +2,6 @@
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web-app.json",
"compilerOptions": {
- "baseUrl": ".",
"paths": {
"#/*": ["./src/*"]
}
diff --git a/apps/vben5/apps/web-ele/tsconfig.node.json b/apps/vben5/apps/web-ele/tsconfig.node.json
index c2f0d86cc..36e9fb5fb 100644
--- a/apps/vben5/apps/web-ele/tsconfig.node.json
+++ b/apps/vben5/apps/web-ele/tsconfig.node.json
@@ -6,5 +6,5 @@
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"noEmit": false
},
- "include": ["vite.config.mts"]
+ "include": ["vite.config.ts"]
}
diff --git a/apps/vben5/apps/web-ele/vite.config.mts b/apps/vben5/apps/web-ele/vite.config.ts
similarity index 100%
rename from apps/vben5/apps/web-ele/vite.config.mts
rename to apps/vben5/apps/web-ele/vite.config.ts
diff --git a/apps/vben5/apps/web-naive/index.html b/apps/vben5/apps/web-naive/index.html
index 7ea638418..43320df32 100644
--- a/apps/vben5/apps/web-naive/index.html
+++ b/apps/vben5/apps/web-naive/index.html
@@ -9,10 +9,9 @@
+ content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" />
- <%= VITE_APP_TITLE %>
+ %VITE_APP_TITLE%
+ :page-title="$t('authentication.pageTitle')">
diff --git a/apps/vben5/apps/web-naive/src/layouts/basic.vue b/apps/vben5/apps/web-naive/src/layouts/basic.vue
index 691893841..720dc89f0 100644
--- a/apps/vben5/apps/web-naive/src/layouts/basic.vue
+++ b/apps/vben5/apps/web-naive/src/layouts/basic.vue
@@ -2,18 +2,19 @@
import type { NotificationItem } from '@vben/layouts';
import { computed, ref, watch } from 'vue';
+import { useRouter } from 'vue-router';
import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
import { VBEN_DOC_URL, VBEN_GITHUB_URL } from '@vben/constants';
import { useWatermark } from '@vben/hooks';
-import { BookOpenText, CircleHelp, MdiGithub } from '@vben/icons';
+import { BookOpenText, CircleHelp, SvgGithubIcon } from '@vben/icons';
import {
BasicLayout,
LockScreen,
Notification,
UserDropdown,
} from '@vben/layouts';
-import { preferences } from '@vben/preferences';
+import { preferences, usePreferences } from '@vben/preferences';
import { useAccessStore, useUserStore } from '@vben/stores';
import { openWindow } from '@vben/utils';
@@ -23,6 +24,7 @@ import LoginForm from '#/views/_core/authentication/login.vue';
const notifications = ref([
{
+ id: 1,
avatar: 'https://avatar.vercel.sh/vercel.svg?text=VB',
date: '3小时前',
isRead: true,
@@ -30,6 +32,7 @@ const notifications = ref([
title: '收到了 14 份新周报',
},
{
+ id: 2,
avatar: 'https://avatar.vercel.sh/1',
date: '刚刚',
isRead: false,
@@ -37,6 +40,7 @@ const notifications = ref([
title: '朱偏右 回复了你',
},
{
+ id: 3,
avatar: 'https://avatar.vercel.sh/1',
date: '2024-01-01',
isRead: false,
@@ -44,23 +48,51 @@ const notifications = ref([
title: '曲丽丽 评论了你',
},
{
+ id: 4,
avatar: 'https://avatar.vercel.sh/satori',
date: '1天前',
isRead: false,
message: '描述信息描述信息描述信息',
title: '代办提醒',
},
+ {
+ id: 5,
+ avatar: 'https://avatar.vercel.sh/satori',
+ date: '1天前',
+ isRead: false,
+ message: '描述信息描述信息描述信息',
+ title: '跳转Workspace示例',
+ link: '/workspace',
+ },
+ {
+ id: 6,
+ avatar: 'https://avatar.vercel.sh/satori',
+ date: '1天前',
+ isRead: false,
+ message: '描述信息描述信息描述信息',
+ title: '跳转外部链接示例',
+ link: 'https://doc.vben.pro',
+ },
]);
+const router = useRouter();
const userStore = useUserStore();
const authStore = useAuthStore();
const accessStore = useAccessStore();
const { destroyWatermark, updateWatermark } = useWatermark();
+const { isDark } = usePreferences();
const showDot = computed(() =>
notifications.value.some((item) => !item.isRead),
);
const menus = computed(() => [
+ {
+ handler: () => {
+ router.push({ name: 'Profile' });
+ },
+ icon: 'lucide:user',
+ text: $t('page.auth.profile'),
+ },
{
handler: () => {
openWindow(VBEN_DOC_URL, {
@@ -76,7 +108,7 @@ const menus = computed(() => [
target: '_blank',
});
},
- icon: MdiGithub,
+ icon: SvgGithubIcon,
text: 'GitHub',
},
{
@@ -102,16 +134,77 @@ function handleNoticeClear() {
notifications.value = [];
}
+function markRead(id: number | string) {
+ const item = notifications.value.find((item) => item.id === id);
+ if (item) {
+ item.isRead = true;
+ }
+}
+
+function remove(id: number | string) {
+ notifications.value = notifications.value.filter((item) => item.id !== id);
+}
+
function handleMakeAll() {
notifications.value.forEach((item) => (item.isRead = true));
}
+const viewAll = () => {};
+
+const handleClick = (item: NotificationItem) => {
+ // 如果通知项有链接,点击时跳转
+ if (item.link) {
+ navigateTo(item.link, item.query, item.state);
+ }
+};
+
+function navigateTo(
+ link: string,
+ query?: Record,
+ state?: Record,
+) {
+ if (link.startsWith('http://') || link.startsWith('https://')) {
+ // 外部链接,在新标签页打开
+ window.open(link, '_blank');
+ } else {
+ // 内部路由链接,支持 query 参数和 state
+ router.push({
+ path: link,
+ query: query || {},
+ state,
+ });
+ }
+}
+
watch(
- () => preferences.app.watermark,
- async (enable) => {
+ () => ({
+ enable: preferences.app.watermark,
+ content: preferences.app.watermarkContent,
+ isDark: isDark.value,
+ }),
+ async ({ enable, content, isDark: isDarkValue }) => {
if (enable) {
+ const watermarkColor = isDarkValue
+ ? 'rgba(255, 255, 255, 0.12)'
+ : 'rgba(0, 0, 0, 0.12)';
+
await updateWatermark({
- content: `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
+ advancedStyle: {
+ colorStops: [
+ {
+ color: watermarkColor,
+ offset: 0,
+ },
+ {
+ color: watermarkColor,
+ offset: 1,
+ },
+ ],
+ type: 'linear',
+ },
+ content:
+ content ||
+ `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
});
} else {
destroyWatermark();
@@ -133,21 +226,23 @@ watch(
description="ann.vben@gmail.com"
tag-text="Pro"
@logout="handleLogout"
- />
+ @clear-preferences-and-logout="handleLogout" />
item.id && markRead(item.id)"
+ @remove="(item) => item.id && remove(item.id)"
@make-all="handleMakeAll"
- />
+ @on-click="handleClick"
+ @view-all="viewAll" />
+ :avatar>
diff --git a/apps/vben5/apps/web-naive/src/locales/langs/en-US/demos.json b/apps/vben5/apps/web-naive/src/locales/langs/en-US/demos.json
index 839fc2e68..b02a97de3 100644
--- a/apps/vben5/apps/web-naive/src/locales/langs/en-US/demos.json
+++ b/apps/vben5/apps/web-naive/src/locales/langs/en-US/demos.json
@@ -3,12 +3,15 @@
"naive": "Naive UI",
"table": "Table",
"form": "Form",
+ "arrayForm": "Array Editor",
"vben": {
"title": "Project",
"about": "About",
"document": "Document",
"antdv": "Ant Design Vue Version",
+ "antdv-next": "Antdv Next Version",
"naive-ui": "Naive UI Version",
- "element-plus": "Element Plus Version"
+ "element-plus": "Element Plus Version",
+ "tdesign": "TDesign Vue Version"
}
}
diff --git a/apps/vben5/apps/web-naive/src/locales/langs/en-US/page.json b/apps/vben5/apps/web-naive/src/locales/langs/en-US/page.json
index 618a258c0..39f1641c8 100644
--- a/apps/vben5/apps/web-naive/src/locales/langs/en-US/page.json
+++ b/apps/vben5/apps/web-naive/src/locales/langs/en-US/page.json
@@ -4,7 +4,8 @@
"register": "Register",
"codeLogin": "Code Login",
"qrcodeLogin": "Qr Code Login",
- "forgetPassword": "Forget Password"
+ "forgetPassword": "Forget Password",
+ "profile": "Profile"
},
"dashboard": {
"title": "Dashboard",
diff --git a/apps/vben5/apps/web-naive/src/locales/langs/zh-CN/demos.json b/apps/vben5/apps/web-naive/src/locales/langs/zh-CN/demos.json
index e0d7e616d..b35cb934f 100644
--- a/apps/vben5/apps/web-naive/src/locales/langs/zh-CN/demos.json
+++ b/apps/vben5/apps/web-naive/src/locales/langs/zh-CN/demos.json
@@ -3,12 +3,15 @@
"naive": "Naive UI",
"table": "Table",
"form": "表单",
+ "arrayForm": "数组编辑器",
"vben": {
"title": "项目",
"about": "关于",
"document": "文档",
"antdv": "Ant Design Vue 版本",
+ "antdv-next": "Antdv Next 版本",
"naive-ui": "Naive UI 版本",
- "element-plus": "Element Plus 版本"
+ "element-plus": "Element Plus 版本",
+ "tdesign": "TDesign Vue 版本"
}
}
diff --git a/apps/vben5/apps/web-naive/src/locales/langs/zh-CN/page.json b/apps/vben5/apps/web-naive/src/locales/langs/zh-CN/page.json
index 4cb67081c..2192d1d52 100644
--- a/apps/vben5/apps/web-naive/src/locales/langs/zh-CN/page.json
+++ b/apps/vben5/apps/web-naive/src/locales/langs/zh-CN/page.json
@@ -4,7 +4,8 @@
"register": "注册",
"codeLogin": "验证码登录",
"qrcodeLogin": "二维码登录",
- "forgetPassword": "忘记密码"
+ "forgetPassword": "忘记密码",
+ "profile": "个人中心"
},
"dashboard": {
"title": "概览",
diff --git a/apps/vben5/apps/web-naive/src/preferences.ts b/apps/vben5/apps/web-naive/src/preferences.ts
index b2e9ace43..303c496e4 100644
--- a/apps/vben5/apps/web-naive/src/preferences.ts
+++ b/apps/vben5/apps/web-naive/src/preferences.ts
@@ -1,4 +1,7 @@
-import { defineOverridesPreferences } from '@vben/preferences';
+import {
+ appCopyrightPreferences,
+ defineOverridesPreferences,
+} from '@vben/preferences';
/**
* @description 项目配置文件
@@ -10,4 +13,5 @@ export const overridesPreferences = defineOverridesPreferences({
app: {
name: import.meta.env.VITE_APP_TITLE,
},
+ copyright: appCopyrightPreferences,
});
diff --git a/apps/vben5/apps/web-naive/src/router/routes/modules/demos.ts b/apps/vben5/apps/web-naive/src/router/routes/modules/demos.ts
index 5e49ffa01..9a4ffeff6 100644
--- a/apps/vben5/apps/web-naive/src/router/routes/modules/demos.ts
+++ b/apps/vben5/apps/web-naive/src/router/routes/modules/demos.ts
@@ -37,6 +37,14 @@ const routes: RouteRecordRaw[] = [
path: '/demos/form',
component: () => import('#/views/demos/form/basic.vue'),
},
+ {
+ meta: {
+ title: $t('demos.arrayForm'),
+ },
+ name: 'ArrayForm',
+ path: '/demos/array-form',
+ component: () => import('#/views/demos/naive/array-form/index.vue'),
+ },
],
},
];
diff --git a/apps/vben5/apps/web-naive/src/router/routes/modules/vben.ts b/apps/vben5/apps/web-naive/src/router/routes/modules/vben.ts
index 169de855b..888ff4cb4 100644
--- a/apps/vben5/apps/web-naive/src/router/routes/modules/vben.ts
+++ b/apps/vben5/apps/web-naive/src/router/routes/modules/vben.ts
@@ -2,12 +2,18 @@ import type { RouteRecordRaw } from 'vue-router';
import {
VBEN_ANT_PREVIEW_URL,
+ VBEN_ANTDV_NEXT_PREVIEW_URL,
VBEN_DOC_URL,
VBEN_ELE_PREVIEW_URL,
VBEN_GITHUB_URL,
VBEN_LOGO_URL,
+ VBEN_TD_PREVIEW_URL,
} from '@vben/constants';
-import { SvgAntdvLogoIcon } from '@vben/icons';
+import {
+ SvgAntdvLogoIcon,
+ SvgAntdvNextLogoIcon,
+ SvgTDesignIcon,
+} from '@vben/icons';
import { IFrameView } from '#/layouts';
import { $t } from '#/locales';
@@ -54,6 +60,28 @@ const routes: RouteRecordRaw[] = [
title: $t('demos.vben.antdv'),
},
},
+ {
+ name: 'VbenAntdVNext',
+ path: '/vben-admin/antdv-next',
+ component: IFrameView,
+ meta: {
+ badgeType: 'dot',
+ icon: SvgAntdvNextLogoIcon,
+ link: VBEN_ANTDV_NEXT_PREVIEW_URL,
+ title: $t('demos.vben.antdv-next'),
+ },
+ },
+ {
+ name: 'VbenTDesign',
+ path: '/vben-admin/tdesign',
+ component: IFrameView,
+ meta: {
+ badgeType: 'dot',
+ icon: SvgTDesignIcon,
+ link: VBEN_TD_PREVIEW_URL,
+ title: $t('demos.vben.tdesign'),
+ },
+ },
{
name: 'VbenElementPlus',
path: '/vben-admin/ele',
@@ -77,6 +105,16 @@ const routes: RouteRecordRaw[] = [
order: 9999,
},
},
+ {
+ name: 'Profile',
+ path: '/profile',
+ component: () => import('#/views/_core/profile/index.vue'),
+ meta: {
+ icon: 'lucide:user',
+ hideInMenu: true,
+ title: $t('page.auth.profile'),
+ },
+ },
];
export default routes;
diff --git a/apps/vben5/apps/web-naive/src/store/auth.ts b/apps/vben5/apps/web-naive/src/store/auth.ts
index 0ff050b3b..5e5cf655b 100644
--- a/apps/vben5/apps/web-naive/src/store/auth.ts
+++ b/apps/vben5/apps/web-naive/src/store/auth.ts
@@ -99,8 +99,7 @@ export const useAuthStore = defineStore('auth', () => {
}
async function fetchUserInfo() {
- let userInfo: null | UserInfo = null;
- userInfo = await getUserInfoApi();
+ const userInfo = await getUserInfoApi();
userStore.setUserInfo(userInfo);
return userInfo;
}
diff --git a/apps/vben5/apps/web-naive/src/views/_core/authentication/code-login.vue b/apps/vben5/apps/web-naive/src/views/_core/authentication/code-login.vue
index acfd1fd78..0f375895e 100644
--- a/apps/vben5/apps/web-naive/src/views/_core/authentication/code-login.vue
+++ b/apps/vben5/apps/web-naive/src/views/_core/authentication/code-login.vue
@@ -55,8 +55,7 @@ const formSchema = computed((): VbenFormSchema[] => {
* @param values 登录表单数据
*/
async function handleLogin(values: Recordable) {
- // eslint-disable-next-line no-console
- console.log(values);
+ void values;
}
@@ -64,6 +63,5 @@ async function handleLogin(values: Recordable) {
+ @submit="handleLogin" />
diff --git a/apps/vben5/apps/web-naive/src/views/_core/authentication/forget-password.vue b/apps/vben5/apps/web-naive/src/views/_core/authentication/forget-password.vue
index fef0d4279..5da4c098e 100644
--- a/apps/vben5/apps/web-naive/src/views/_core/authentication/forget-password.vue
+++ b/apps/vben5/apps/web-naive/src/views/_core/authentication/forget-password.vue
@@ -29,8 +29,7 @@ const formSchema = computed((): VbenFormSchema[] => {
});
function handleSubmit(value: Recordable) {
- // eslint-disable-next-line no-console
- console.log('reset email:', value);
+ void value;
}
@@ -38,6 +37,5 @@ function handleSubmit(value: Recordable) {
+ @submit="handleSubmit" />
diff --git a/apps/vben5/apps/web-naive/src/views/_core/authentication/login.vue b/apps/vben5/apps/web-naive/src/views/_core/authentication/login.vue
index 099e4c8c0..7d9d6f8c0 100644
--- a/apps/vben5/apps/web-naive/src/views/_core/authentication/login.vue
+++ b/apps/vben5/apps/web-naive/src/views/_core/authentication/login.vue
@@ -93,6 +93,5 @@ const formSchema = computed((): VbenFormSchema[] => {
+ @submit="authStore.authLogin" />
diff --git a/apps/vben5/apps/web-naive/src/views/_core/authentication/register.vue b/apps/vben5/apps/web-naive/src/views/_core/authentication/register.vue
index daf89c447..03329e1da 100644
--- a/apps/vben5/apps/web-naive/src/views/_core/authentication/register.vue
+++ b/apps/vben5/apps/web-naive/src/views/_core/authentication/register.vue
@@ -82,8 +82,7 @@ const formSchema = computed((): VbenFormSchema[] => {
});
function handleSubmit(value: Recordable) {
- // eslint-disable-next-line no-console
- console.log('register submit:', value);
+ void value;
}
@@ -91,6 +90,5 @@ function handleSubmit(value: Recordable) {
+ @submit="handleSubmit" />
diff --git a/apps/vben5/apps/web-naive/src/views/_core/profile/base-setting.vue b/apps/vben5/apps/web-naive/src/views/_core/profile/base-setting.vue
new file mode 100644
index 000000000..aa8a4c260
--- /dev/null
+++ b/apps/vben5/apps/web-naive/src/views/_core/profile/base-setting.vue
@@ -0,0 +1,65 @@
+
+
+
+
diff --git a/apps/vben5/apps/web-naive/src/views/_core/profile/index.vue b/apps/vben5/apps/web-naive/src/views/_core/profile/index.vue
new file mode 100644
index 000000000..ee95282aa
--- /dev/null
+++ b/apps/vben5/apps/web-naive/src/views/_core/profile/index.vue
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/vben5/apps/web-naive/src/views/_core/profile/notification-setting.vue b/apps/vben5/apps/web-naive/src/views/_core/profile/notification-setting.vue
new file mode 100644
index 000000000..324a4b393
--- /dev/null
+++ b/apps/vben5/apps/web-naive/src/views/_core/profile/notification-setting.vue
@@ -0,0 +1,31 @@
+
+
+
+
diff --git a/apps/vben5/apps/web-naive/src/views/_core/profile/password-setting.vue b/apps/vben5/apps/web-naive/src/views/_core/profile/password-setting.vue
new file mode 100644
index 000000000..fa0c4982a
--- /dev/null
+++ b/apps/vben5/apps/web-naive/src/views/_core/profile/password-setting.vue
@@ -0,0 +1,62 @@
+
+
+
+
diff --git a/apps/vben5/apps/web-naive/src/views/_core/profile/security-setting.vue b/apps/vben5/apps/web-naive/src/views/_core/profile/security-setting.vue
new file mode 100644
index 000000000..be30db58e
--- /dev/null
+++ b/apps/vben5/apps/web-naive/src/views/_core/profile/security-setting.vue
@@ -0,0 +1,43 @@
+
+
+
+
diff --git a/apps/vben5/apps/web-naive/src/views/dashboard/analytics/analytics-visits-sales.vue b/apps/vben5/apps/web-naive/src/views/dashboard/analytics/analytics-visits-sales.vue
index 02f509123..6ff52086f 100644
--- a/apps/vben5/apps/web-naive/src/views/dashboard/analytics/analytics-visits-sales.vue
+++ b/apps/vben5/apps/web-naive/src/views/dashboard/analytics/analytics-visits-sales.vue
@@ -24,7 +24,7 @@ onMounted(() => {
{ name: '定制', value: 310 },
{ name: '技术支持', value: 274 },
{ name: '远程', value: 400 },
- ].sort((a, b) => {
+ ].toSorted((a, b) => {
return a.value - b.value;
}),
name: '商业占比',
diff --git a/apps/vben5/apps/web-naive/src/views/dashboard/analytics/index.vue b/apps/vben5/apps/web-naive/src/views/dashboard/analytics/index.vue
index 5e3d6d285..e794c99a9 100644
--- a/apps/vben5/apps/web-naive/src/views/dashboard/analytics/index.vue
+++ b/apps/vben5/apps/web-naive/src/views/dashboard/analytics/index.vue
@@ -76,10 +76,10 @@ const chartTabs: TabOption[] = [
-
+
-
+
diff --git a/apps/vben5/apps/web-naive/src/views/dashboard/workspace/index.vue b/apps/vben5/apps/web-naive/src/views/dashboard/workspace/index.vue
index b95d61381..f189349ec 100644
--- a/apps/vben5/apps/web-naive/src/views/dashboard/workspace/index.vue
+++ b/apps/vben5/apps/web-naive/src/views/dashboard/workspace/index.vue
@@ -236,8 +236,7 @@ function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) {
+ :avatar="userStore.userInfo?.avatar || preferences.app.defaultAvatar">
早安, {{ userStore.userInfo?.realName }}, 开始您一天的工作吧!
@@ -254,8 +253,7 @@ function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) {
:items="quickNavItems"
class="mt-5 lg:mt-0"
title="快捷导航"
- @click="navTo"
- />
+ @click="navTo" />
diff --git a/apps/vben5/apps/web-naive/src/views/demos/form/basic.vue b/apps/vben5/apps/web-naive/src/views/demos/form/basic.vue
index fe26624cc..7019338d7 100644
--- a/apps/vben5/apps/web-naive/src/views/demos/form/basic.vue
+++ b/apps/vben5/apps/web-naive/src/views/demos/form/basic.vue
@@ -1,12 +1,15 @@
-
+ title="表单演示">
+
设置表单值
+
+ 打开弹窗
+
+
diff --git a/apps/vben5/apps/web-naive/src/views/demos/form/modal.vue b/apps/vben5/apps/web-naive/src/views/demos/form/modal.vue
new file mode 100644
index 000000000..52e23542d
--- /dev/null
+++ b/apps/vben5/apps/web-naive/src/views/demos/form/modal.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
diff --git a/apps/vben5/apps/web-naive/src/views/demos/naive/array-form/index.vue b/apps/vben5/apps/web-naive/src/views/demos/naive/array-form/index.vue
new file mode 100644
index 000000000..05b5fd5e7
--- /dev/null
+++ b/apps/vben5/apps/web-naive/src/views/demos/naive/array-form/index.vue
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+ 设置表单值
+ 获取表单值
+
+ 提交校验
+
+
+
+
+
+
diff --git a/apps/vben5/apps/web-naive/src/views/demos/table/index.vue b/apps/vben5/apps/web-naive/src/views/demos/table/index.vue
index ddc958bc3..59a0d5837 100644
--- a/apps/vben5/apps/web-naive/src/views/demos/table/index.vue
+++ b/apps/vben5/apps/web-naive/src/views/demos/table/index.vue
@@ -29,8 +29,7 @@ const data = [
+ title="NDataTable">
diff --git a/apps/vben5/apps/web-naive/tailwind.config.mjs b/apps/vben5/apps/web-naive/tailwind.config.mjs
deleted file mode 100644
index f17f556fa..000000000
--- a/apps/vben5/apps/web-naive/tailwind.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@vben/tailwind-config';
diff --git a/apps/vben5/apps/web-naive/tsconfig.json b/apps/vben5/apps/web-naive/tsconfig.json
index 02c287fe6..858a0ec08 100644
--- a/apps/vben5/apps/web-naive/tsconfig.json
+++ b/apps/vben5/apps/web-naive/tsconfig.json
@@ -2,7 +2,6 @@
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web-app.json",
"compilerOptions": {
- "baseUrl": ".",
"paths": {
"#/*": ["./src/*"]
}
diff --git a/apps/vben5/apps/web-naive/tsconfig.node.json b/apps/vben5/apps/web-naive/tsconfig.node.json
index c2f0d86cc..36e9fb5fb 100644
--- a/apps/vben5/apps/web-naive/tsconfig.node.json
+++ b/apps/vben5/apps/web-naive/tsconfig.node.json
@@ -6,5 +6,5 @@
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"noEmit": false
},
- "include": ["vite.config.mts"]
+ "include": ["vite.config.ts"]
}
diff --git a/apps/vben5/apps/web-naive/vite.config.mts b/apps/vben5/apps/web-naive/vite.config.ts
similarity index 100%
rename from apps/vben5/apps/web-naive/vite.config.mts
rename to apps/vben5/apps/web-naive/vite.config.ts
diff --git a/apps/vben5/apps/web-tdesign/index.html b/apps/vben5/apps/web-tdesign/index.html
index 480eb84de..5199ae0f7 100644
--- a/apps/vben5/apps/web-tdesign/index.html
+++ b/apps/vben5/apps/web-tdesign/index.html
@@ -9,10 +9,9 @@
+ content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" />
- <%= VITE_APP_TITLE %>
+ %VITE_APP_TITLE%
@@ -64,6 +63,5 @@ async function handleLogin(values: Recordable) {
+ @submit="handleLogin" />
diff --git a/apps/vben5/apps/web-tdesign/src/views/_core/authentication/forget-password.vue b/apps/vben5/apps/web-tdesign/src/views/_core/authentication/forget-password.vue
index fef0d4279..5da4c098e 100644
--- a/apps/vben5/apps/web-tdesign/src/views/_core/authentication/forget-password.vue
+++ b/apps/vben5/apps/web-tdesign/src/views/_core/authentication/forget-password.vue
@@ -29,8 +29,7 @@ const formSchema = computed((): VbenFormSchema[] => {
});
function handleSubmit(value: Recordable) {
- // eslint-disable-next-line no-console
- console.log('reset email:', value);
+ void value;
}
@@ -38,6 +37,5 @@ function handleSubmit(value: Recordable) {
+ @submit="handleSubmit" />
diff --git a/apps/vben5/apps/web-tdesign/src/views/_core/authentication/login.vue b/apps/vben5/apps/web-tdesign/src/views/_core/authentication/login.vue
index 89af0c270..402ea22b2 100644
--- a/apps/vben5/apps/web-tdesign/src/views/_core/authentication/login.vue
+++ b/apps/vben5/apps/web-tdesign/src/views/_core/authentication/login.vue
@@ -94,6 +94,5 @@ const formSchema = computed((): VbenFormSchema[] => {
+ @submit="authStore.authLogin" />
diff --git a/apps/vben5/apps/web-tdesign/src/views/_core/authentication/register.vue b/apps/vben5/apps/web-tdesign/src/views/_core/authentication/register.vue
index 1a80ff51a..6a959e560 100644
--- a/apps/vben5/apps/web-tdesign/src/views/_core/authentication/register.vue
+++ b/apps/vben5/apps/web-tdesign/src/views/_core/authentication/register.vue
@@ -82,8 +82,7 @@ const formSchema = computed((): VbenFormSchema[] => {
});
async function handleSubmit(value: Recordable) {
- // eslint-disable-next-line no-console
- console.log('register submit:', value);
+ void value;
}
@@ -91,6 +90,5 @@ async function handleSubmit(value: Recordable) {
+ @submit="handleSubmit" />
diff --git a/apps/vben5/apps/web-tdesign/src/views/_core/profile/index.vue b/apps/vben5/apps/web-tdesign/src/views/_core/profile/index.vue
index 8740894e3..ee95282aa 100644
--- a/apps/vben5/apps/web-tdesign/src/views/_core/profile/index.vue
+++ b/apps/vben5/apps/web-tdesign/src/views/_core/profile/index.vue
@@ -37,8 +37,7 @@ const tabs = ref([
v-model:model-value="tabsValue"
title="个人中心"
:user-info="userStore.userInfo"
- :tabs="tabs"
- >
+ :tabs="tabs">
diff --git a/apps/vben5/apps/web-tdesign/src/views/_core/profile/password-setting.vue b/apps/vben5/apps/web-tdesign/src/views/_core/profile/password-setting.vue
index 1db119450..9f01f5bd7 100644
--- a/apps/vben5/apps/web-tdesign/src/views/_core/profile/password-setting.vue
+++ b/apps/vben5/apps/web-tdesign/src/views/_core/profile/password-setting.vue
@@ -58,6 +58,5 @@ function handleSubmit() {
+ @submit="handleSubmit" />
diff --git a/apps/vben5/apps/web-tdesign/src/views/dashboard/analytics/index.vue b/apps/vben5/apps/web-tdesign/src/views/dashboard/analytics/index.vue
index 5e3d6d285..e794c99a9 100644
--- a/apps/vben5/apps/web-tdesign/src/views/dashboard/analytics/index.vue
+++ b/apps/vben5/apps/web-tdesign/src/views/dashboard/analytics/index.vue
@@ -76,10 +76,10 @@ const chartTabs: TabOption[] = [
-
+
-
+
diff --git a/apps/vben5/apps/web-tdesign/src/views/dashboard/workspace/index.vue b/apps/vben5/apps/web-tdesign/src/views/dashboard/workspace/index.vue
index b95d61381..f189349ec 100644
--- a/apps/vben5/apps/web-tdesign/src/views/dashboard/workspace/index.vue
+++ b/apps/vben5/apps/web-tdesign/src/views/dashboard/workspace/index.vue
@@ -236,8 +236,7 @@ function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) {
+ :avatar="userStore.userInfo?.avatar || preferences.app.defaultAvatar">
早安, {{ userStore.userInfo?.realName }}, 开始您一天的工作吧!
@@ -254,8 +253,7 @@ function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) {
:items="quickNavItems"
class="mt-5 lg:mt-0"
title="快捷导航"
- @click="navTo"
- />
+ @click="navTo" />
diff --git a/apps/vben5/apps/web-tdesign/src/views/demos/tdesign/index.vue b/apps/vben5/apps/web-tdesign/src/views/demos/tdesign/index.vue
index 27299dea5..fbf170de8 100644
--- a/apps/vben5/apps/web-tdesign/src/views/demos/tdesign/index.vue
+++ b/apps/vben5/apps/web-tdesign/src/views/demos/tdesign/index.vue
@@ -36,9 +36,8 @@ function notify(type: NotificationType) {
-
+ title="TDesign Vue组件使用演示">
+
@@ -46,7 +45,7 @@ function notify(type: NotificationType) {
-
+
@@ -55,7 +54,7 @@ function notify(type: NotificationType) {
-
+
diff --git a/apps/vben5/apps/web-tdesign/tailwind.config.mjs b/apps/vben5/apps/web-tdesign/tailwind.config.mjs
deleted file mode 100644
index f17f556fa..000000000
--- a/apps/vben5/apps/web-tdesign/tailwind.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@vben/tailwind-config';
diff --git a/apps/vben5/apps/web-tdesign/tsconfig.json b/apps/vben5/apps/web-tdesign/tsconfig.json
index 02c287fe6..858a0ec08 100644
--- a/apps/vben5/apps/web-tdesign/tsconfig.json
+++ b/apps/vben5/apps/web-tdesign/tsconfig.json
@@ -2,7 +2,6 @@
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@vben/tsconfig/web-app.json",
"compilerOptions": {
- "baseUrl": ".",
"paths": {
"#/*": ["./src/*"]
}
diff --git a/apps/vben5/apps/web-tdesign/tsconfig.node.json b/apps/vben5/apps/web-tdesign/tsconfig.node.json
index c2f0d86cc..36e9fb5fb 100644
--- a/apps/vben5/apps/web-tdesign/tsconfig.node.json
+++ b/apps/vben5/apps/web-tdesign/tsconfig.node.json
@@ -6,5 +6,5 @@
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"noEmit": false
},
- "include": ["vite.config.mts"]
+ "include": ["vite.config.ts"]
}
diff --git a/apps/vben5/apps/web-tdesign/vite.config.mts b/apps/vben5/apps/web-tdesign/vite.config.ts
similarity index 100%
rename from apps/vben5/apps/web-tdesign/vite.config.mts
rename to apps/vben5/apps/web-tdesign/vite.config.ts
diff --git a/apps/vben5/cspell.json b/apps/vben5/cspell.json
index 0c05c512d..982392e14 100644
--- a/apps/vben5/cspell.json
+++ b/apps/vben5/cspell.json
@@ -12,10 +12,13 @@
"axios",
"brotli",
"cascader",
+ "chatcmpl",
"clsx",
+ "dedup",
"defu",
"demi",
"dotenv",
+ "echart",
"echarts",
"ependencies",
"esbuild",
@@ -24,6 +27,7 @@
"execa",
"iconify",
"iconoir",
+ "indexeddb",
"intlify",
"isequal",
"jspm",
@@ -33,6 +37,7 @@
"minw",
"mkdist",
"mockjs",
+ "myapp",
"naiveui",
"napi",
"nocheck",
@@ -41,6 +46,9 @@
"noreferrer",
"nprogress",
"nuxt",
+ "organisation",
+ "oxfmt",
+ "oxlint",
"pinia",
"prefixs",
"publint",
@@ -51,8 +59,12 @@
"sonner",
"sortablejs",
"styl",
+ "tabler",
"taze",
"tdesign",
+ "tsdown",
+ "tsgolint",
+ "turborepo",
"ui-kit",
"uicons",
"unplugin",
diff --git a/apps/vben5/docs/.vitepress/build/dayjs-loader.mjs b/apps/vben5/docs/.vitepress/build/dayjs-loader.mjs
new file mode 100644
index 000000000..623c394ff
--- /dev/null
+++ b/apps/vben5/docs/.vitepress/build/dayjs-loader.mjs
@@ -0,0 +1,2 @@
+import { register } from 'node:module';
+register('./dayjs-resolve-hook.mjs', import.meta.url);
diff --git a/apps/vben5/docs/.vitepress/build/dayjs-resolve-hook.mjs b/apps/vben5/docs/.vitepress/build/dayjs-resolve-hook.mjs
new file mode 100644
index 000000000..f8989044a
--- /dev/null
+++ b/apps/vben5/docs/.vitepress/build/dayjs-resolve-hook.mjs
@@ -0,0 +1,10 @@
+const DAYJS_SUBPATH_RE = /^dayjs\/(plugin|locale)\/([^./]+)$/;
+
+/** @type {import('node:module').ResolveHook} */
+export async function resolve(specifier, context, nextResolve) {
+ const match = specifier.match(DAYJS_SUBPATH_RE);
+ if (match) {
+ return nextResolve(`${specifier}.js`, context);
+ }
+ return nextResolve(specifier, context);
+}
diff --git a/apps/vben5/docs/.vitepress/components/demo-preview.vue b/apps/vben5/docs/.vitepress/components/demo-preview.vue
index 983a95062..b5b0c7ab6 100644
--- a/apps/vben5/docs/.vitepress/components/demo-preview.vue
+++ b/apps/vben5/docs/.vitepress/components/demo-preview.vue
@@ -21,13 +21,12 @@ const parsedFiles = computed(() => {
+ class="not-prose relative w-full overflow-x-auto rounded-t-lg px-4 py-6">
-
+
ERROR:
The preview directory does not exist. Please check the 'dir'
diff --git a/apps/vben5/docs/.vitepress/components/preview-group.vue b/apps/vben5/docs/.vitepress/components/preview-group.vue
index e08e921aa..f22381d1c 100644
--- a/apps/vben5/docs/.vitepress/components/preview-group.vue
+++ b/apps/vben5/docs/.vitepress/components/preview-group.vue
@@ -5,7 +5,7 @@ import { computed, ref, useSlots } from 'vue';
import { VbenTooltip } from '@vben-core/shadcn-ui';
-import { Code } from 'lucide-vue-next';
+import { Code } from '@lucide/vue';
import {
TabsContent,
TabsIndicator,
@@ -49,24 +49,21 @@ const toggleOpen = () => {
+ @update:model-value="open = true">
-
+ class="absolute bottom-0 left-0 h-[2px] w-(--reka-tabs-indicator-size) translate-x-(--reka-tabs-indicator-position) rounded-full transition-[width,transform] duration-300">
+
+ class="border-box px-4 py-3 text-foreground data-[state=active]:text-(--vp-c-indigo-1)"
+ tabindex="-1">
{{ tab.label }}
@@ -76,14 +73,12 @@ const toggleOpen = () => {
:class="{
'py-2': !open,
}"
- class="flex items-center"
- >
+ class="flex items-center">
+ @click="toggleOpen" />
{{ open ? 'Collapse code' : 'Expand code' }}
@@ -92,15 +87,13 @@ const toggleOpen = () => {
diff --git a/apps/vben5/docs/src/demos/vben-modal/auto-height/modal.vue b/apps/vben5/docs/src/demos/vben-modal/auto-height/modal.vue
index e270df1ae..b3b041164 100644
--- a/apps/vben5/docs/src/demos/vben-modal/auto-height/modal.vue
+++ b/apps/vben5/docs/src/demos/vben-modal/auto-height/modal.vue
@@ -32,8 +32,7 @@ function handleUpdate(len: number) {
+ class="flex-center h-55 w-full bg-muted even:bg-heavy">
{{ item }}
diff --git a/apps/vben5/docs/src/demos/vben-modal/basic/index.vue b/apps/vben5/docs/src/demos/vben-modal/basic/index.vue
index 9f8997085..99f5e0804 100644
--- a/apps/vben5/docs/src/demos/vben-modal/basic/index.vue
+++ b/apps/vben5/docs/src/demos/vben-modal/basic/index.vue
@@ -6,6 +6,6 @@ const [Modal, modalApi] = useVbenModal();
modalApi.open()">Open
- modal content
+ modal content
diff --git a/apps/vben5/docs/src/demos/vben-table-action/basic/index.vue b/apps/vben5/docs/src/demos/vben-table-action/basic/index.vue
new file mode 100644
index 000000000..b88cd4a19
--- /dev/null
+++ b/apps/vben5/docs/src/demos/vben-table-action/basic/index.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+ 最近点击:{{ last }}
+
+
+
diff --git a/apps/vben5/docs/src/demos/vben-table-action/dropdown/index.vue b/apps/vben5/docs/src/demos/vben-table-action/dropdown/index.vue
new file mode 100644
index 000000000..2d5261e5d
--- /dev/null
+++ b/apps/vben5/docs/src/demos/vben-table-action/dropdown/index.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+ 最近点击:{{ last }}
+
+
+
diff --git a/apps/vben5/docs/src/demos/vben-table-action/permission/index.vue b/apps/vben5/docs/src/demos/vben-table-action/permission/index.vue
new file mode 100644
index 000000000..28dde3963
--- /dev/null
+++ b/apps/vben5/docs/src/demos/vben-table-action/permission/index.vue
@@ -0,0 +1,27 @@
+
+
+
+
diff --git a/apps/vben5/docs/src/demos/vben-table-action/popconfirm/index.vue b/apps/vben5/docs/src/demos/vben-table-action/popconfirm/index.vue
new file mode 100644
index 000000000..513430ced
--- /dev/null
+++ b/apps/vben5/docs/src/demos/vben-table-action/popconfirm/index.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+ 最近操作:{{ last }}
+
+
+
diff --git a/apps/vben5/docs/src/demos/vben-table-action/tooltip/index.vue b/apps/vben5/docs/src/demos/vben-table-action/tooltip/index.vue
new file mode 100644
index 000000000..2bd75e28b
--- /dev/null
+++ b/apps/vben5/docs/src/demos/vben-table-action/tooltip/index.vue
@@ -0,0 +1,17 @@
+
+
+
+
diff --git a/apps/vben5/docs/src/demos/vben-tiptap/basic/index.vue b/apps/vben5/docs/src/demos/vben-tiptap/basic/index.vue
new file mode 100644
index 000000000..fc7579a5c
--- /dev/null
+++ b/apps/vben5/docs/src/demos/vben-tiptap/basic/index.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
当前内容:
+
+ {{ content }}
+
+
+
+
diff --git a/apps/vben5/docs/src/demos/vben-tiptap/image-upload/index.vue b/apps/vben5/docs/src/demos/vben-tiptap/image-upload/index.vue
new file mode 100644
index 000000000..e72d02ba7
--- /dev/null
+++ b/apps/vben5/docs/src/demos/vben-tiptap/image-upload/index.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
提示:
+
+ - 点击工具栏图片按钮可选择本地上传或 URL 插入
+ - 拖拽图片到编辑器区域可直接上传
+ - 粘贴图片也会触发上传
+ - 上传过程中会显示进度条
+
+
+
+
diff --git a/apps/vben5/docs/src/demos/vben-vxe-table/basic/index.vue b/apps/vben5/docs/src/demos/vben-vxe-table/basic/index.vue
index 4b6b5a632..5480f9945 100644
--- a/apps/vben5/docs/src/demos/vben-vxe-table/basic/index.vue
+++ b/apps/vben5/docs/src/demos/vben-vxe-table/basic/index.vue
@@ -1,7 +1,7 @@
-
+
@@ -169,8 +170,7 @@ async function onChange() {
block
type="link"
v-access:code="[ResourcesPermissions.Update]"
- @click="onUpdate(row)"
- >
+ @click="onUpdate(row)">
{{ $t('AbpUi.Edit') }}
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
diff --git a/apps/vben5/packages/@abp/localization/src/components/texts/LocalizationTextModal.vue b/apps/vben5/packages/@abp/localization/src/components/texts/LocalizationTextModal.vue
index a7b5cffcf..37690ad9c 100644
--- a/apps/vben5/packages/@abp/localization/src/components/texts/LocalizationTextModal.vue
+++ b/apps/vben5/packages/@abp/localization/src/components/texts/LocalizationTextModal.vue
@@ -133,53 +133,44 @@ async function onSubmit() {
ref="form"
:label-col="{ span: 6 }"
:model="formModel"
- :wrapper-col="{ span: 18 }"
- >
+ :wrapper-col="{ span: 18 }">
+ required>
+ required>
+ :field-names="{ label: 'displayName', value: 'name' }" />
+ required>
+ autocomplete="off" />
+ required>
+ autocomplete="off" />
diff --git a/apps/vben5/packages/@abp/localization/src/components/texts/LocalizationTextTable.vue b/apps/vben5/packages/@abp/localization/src/components/texts/LocalizationTextTable.vue
index 07b9acf2e..e1283bcd6 100644
--- a/apps/vben5/packages/@abp/localization/src/components/texts/LocalizationTextTable.vue
+++ b/apps/vben5/packages/@abp/localization/src/components/texts/LocalizationTextTable.vue
@@ -265,14 +265,16 @@ function onDelete(row: TextDifferenceDto) {
async function onChange(data: TextDto) {
const input = await gridApi.formApi.getValues();
onGet(input);
- // 资源变化刷新本地多语言缓存
- const cultureName =
- abpStore.application!.localization.currentCulture.cultureName;
- if (data.cultureName === cultureName) {
- const localization = await getLocalizationApi({
- cultureName,
- });
- abpStore.setLocalization(localization);
+ if (abpStore.localization?.currentCulture) {
+ // 资源变化刷新本地多语言缓存
+ const cultureName =
+ abpStore.application!.localization.currentCulture.cultureName;
+ if (data.cultureName === cultureName) {
+ const localization = await getLocalizationApi({
+ cultureName,
+ });
+ abpStore.setLocalization(localization);
+ }
}
}
@@ -291,8 +293,7 @@ onMounted(onInit);
:value="modelValue"
:field-names="{ label: 'displayName', value: 'cultureName' }"
@change="(value) => onFieldChange('cultureName', value?.toString())"
- @search="onSearchLanguages"
- />
+ @search="onSearchLanguages" />
+ @click="onCreate">
{{ $t('LocalizationManagement.Text:AddNew') }}
@@ -348,8 +345,7 @@ onMounted(onInit);
block
type="link"
v-access:code="[TextsPermissions.Update]"
- @click="onUpdate(row)"
- >
+ @click="onUpdate(row)">
{{ $t('AbpUi.Edit') }}
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
@@ -368,8 +363,7 @@ onMounted(onInit);
+ @change="onChange" />
diff --git a/apps/vben5/packages/@abp/notifications/src/components/definitions/groups/NotificationGroupDefinitionModal.vue b/apps/vben5/packages/@abp/notifications/src/components/definitions/groups/NotificationGroupDefinitionModal.vue
index 40929c16c..adb96b2ff 100644
--- a/apps/vben5/packages/@abp/notifications/src/components/definitions/groups/NotificationGroupDefinitionModal.vue
+++ b/apps/vben5/packages/@abp/notifications/src/components/definitions/groups/NotificationGroupDefinitionModal.vue
@@ -74,7 +74,9 @@ const [Modal, modalApi] = useVbenModal({
try {
modalApi.setState({ loading: true });
const { name } = modalApi.getData();
- name && (await onGet(name));
+ if (name) {
+ await onGet(name);
+ }
} finally {
modalApi.setState({ loading: false });
}
@@ -97,6 +99,7 @@ function onPropChange(prop: PropertyInfo) {
}
function onPropDelete(prop: PropertyInfo) {
formModel.value.extraProperties ??= {};
+ // oxlint-disable-next-line typescript/no-dynamic-delete
delete formModel.value.extraProperties[prop.key];
}
@@ -107,50 +110,41 @@ function onPropDelete(prop: PropertyInfo) {
ref="form"
:label-col="{ span: 6 }"
:model="formModel"
- :wrapper-col="{ span: 18 }"
- >
+ :wrapper-col="{ span: 18 }">
+ required>
+ autocomplete="off" />
+ required>
+ :disabled="formModel.isStatic" />
+ name="description">
+ :disabled="formModel.isStatic" />
+ :extra="$t('Notifications.Description:AllowSubscriptionToClients')">
+ v-model:checked="formModel.allowSubscriptionToClients">
{{ $t('Notifications.DisplayName:AllowSubscriptionToClients') }}
@@ -161,8 +155,7 @@ function onPropDelete(prop: PropertyInfo) {
:data="formModel.extraProperties"
:disabled="formModel.isStatic"
@change="onPropChange"
- @delete="onPropDelete"
- />
+ @delete="onPropDelete" />
diff --git a/apps/vben5/packages/@abp/notifications/src/components/definitions/groups/NotificationGroupDefinitionTable.vue b/apps/vben5/packages/@abp/notifications/src/components/definitions/groups/NotificationGroupDefinitionTable.vue
index b16553a2e..7246a21df 100644
--- a/apps/vben5/packages/@abp/notifications/src/components/definitions/groups/NotificationGroupDefinitionTable.vue
+++ b/apps/vben5/packages/@abp/notifications/src/components/definitions/groups/NotificationGroupDefinitionTable.vue
@@ -213,8 +213,7 @@ onMounted(onGet);
:icon="h(PlusOutlined)"
type="primary"
v-access:code="[GroupDefinitionsPermissions.Create]"
- @click="onCreate"
- >
+ @click="onCreate">
{{ $t('Notifications.GroupDefinitions:AddNew') }}
@@ -225,8 +224,7 @@ onMounted(onGet);
block
type="link"
v-access:code="[GroupDefinitionsPermissions.Update]"
- @click="onUpdate(row)"
- >
+ @click="onUpdate(row)">
{{ $t('AbpUi.Edit') }}
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
@@ -248,8 +245,7 @@ onMounted(onGet);
hasAccessByCodes([NotificationDefinitionsPermissions.Create])
"
key="definitions"
- :icon="h(DefinitionIcon)"
- >
+ :icon="h(DefinitionIcon)">
{{ $t('Notifications.NotificationDefinitions:AddNew') }}
diff --git a/apps/vben5/packages/@abp/notifications/src/components/definitions/notifications/NotificationDefinitionModal.vue b/apps/vben5/packages/@abp/notifications/src/components/definitions/notifications/NotificationDefinitionModal.vue
index 0f3411654..3a8f8df1e 100644
--- a/apps/vben5/packages/@abp/notifications/src/components/definitions/notifications/NotificationDefinitionModal.vue
+++ b/apps/vben5/packages/@abp/notifications/src/components/definitions/notifications/NotificationDefinitionModal.vue
@@ -115,7 +115,9 @@ const [Modal, modalApi] = useVbenModal({
const { groupName, name } =
modalApi.getData();
await onInit(groupName);
- name && (await onGet(name));
+ if (name) {
+ await onGet(name);
+ }
} finally {
modalApi.setState({ loading: false });
}
@@ -149,8 +151,8 @@ async function onInit(groupName?: string) {
displayName: Lr(displayName.resourceName, displayName.name),
};
});
- if (getGroupsRes.items.length === 1) {
- formModel.value.groupName = getGroupsRes.items[0]!.name;
+ if (getGroupsRes.items.length === 1 && getGroupsRes.items[0]) {
+ formModel.value.groupName = getGroupsRes.items[0].name;
}
}
function onPropChange(prop: PropertyInfo) {
@@ -159,6 +161,7 @@ function onPropChange(prop: PropertyInfo) {
}
function onPropDelete(prop: PropertyInfo) {
formModel.value.extraProperties ??= {};
+ // oxlint-disable-next-line typescript/no-dynamic-delete
delete formModel.value.extraProperties[prop.key];
}
@@ -169,129 +172,108 @@ function onPropDelete(prop: PropertyInfo) {
ref="form"
:label-col="{ span: 6 }"
:model="formModel"
- :wrapper-col="{ span: 18 }"
- >
+ :wrapper-col="{ span: 18 }">
+ required>
+ :field-names="{ label: 'displayName', value: 'name' }" />
+ required>
+ autocomplete="off" />
+ required>
+ :disabled="formModel.isStatic" />
+ :label="$t('Notifications.DisplayName:Description')">
+ v-model:value="formModel.description" />
+ :extra="$t('Notifications.Description:AllowSubscriptionToClients')">
+ v-model:checked="formModel.allowSubscriptionToClients">
{{ $t('Notifications.DisplayName:AllowSubscriptionToClients') }}
+ :extra="$t('Notifications.Description:NotificationType')">
+ :options="notificationTypeOptions" />
+ :extra="$t('Notifications.Description:NotificationLifetime')">
+ :options="notificationLifetimeOptions" />
+ :extra="$t('Notifications.Description:ContentType')">
+ :options="notificationContentTypeOptions" />
+ :extra="$t('Notifications.Description:Providers')">
+ :field-names="{ label: 'name', value: 'name' }" />
+ :extra="$t('Notifications.Description:Template')">
+ :field-names="{ label: 'name', value: 'name' }" />
@@ -300,8 +282,7 @@ function onPropDelete(prop: PropertyInfo) {
:data="formModel.extraProperties"
:disabled="formModel.isStatic"
@change="onPropChange"
- @delete="onPropDelete"
- />
+ @delete="onPropDelete" />
diff --git a/apps/vben5/packages/@abp/notifications/src/components/definitions/notifications/NotificationDefinitionTable.vue b/apps/vben5/packages/@abp/notifications/src/components/definitions/notifications/NotificationDefinitionTable.vue
index 396ad1bb0..ac0caf624 100644
--- a/apps/vben5/packages/@abp/notifications/src/components/definitions/notifications/NotificationDefinitionTable.vue
+++ b/apps/vben5/packages/@abp/notifications/src/components/definitions/notifications/NotificationDefinitionTable.vue
@@ -365,8 +365,7 @@ onMounted(onGet);
:icon="h(PlusOutlined)"
type="primary"
v-access:code="[NotificationDefinitionsPermissions.Create]"
- @click="onCreate"
- >
+ @click="onCreate">
{{ $t('Notifications.NotificationDefinitions:AddNew') }}
@@ -378,14 +377,12 @@ onMounted(onGet);
trigger: 'row',
rowField: 'name',
childrenField: 'children',
- }"
- >
+ }">
+ class="text-green-500" />
@@ -403,8 +400,7 @@ onMounted(onGet);
block
type="link"
v-access:code="[NotificationDefinitionsPermissions.Update]"
- @click="onUpdate(definition)"
- >
+ @click="onUpdate(definition)">
{{ $t('AbpUi.Edit') }}
+ @click="onDelete(definition)">
{{ $t('AbpUi.Delete') }}
diff --git a/apps/vben5/packages/@abp/notifications/src/components/definitions/notifications/NotificationSendModal.vue b/apps/vben5/packages/@abp/notifications/src/components/definitions/notifications/NotificationSendModal.vue
index 601bdb892..d470a2ad7 100644
--- a/apps/vben5/packages/@abp/notifications/src/components/definitions/notifications/NotificationSendModal.vue
+++ b/apps/vben5/packages/@abp/notifications/src/components/definitions/notifications/NotificationSendModal.vue
@@ -119,8 +119,7 @@ async function onSubmit() {
ref="form"
:label-col="{ span: 6 }"
:model="formModel"
- :wrapper-col="{ span: 18 }"
- >
+ :wrapper-col="{ span: 18 }">
@@ -130,42 +129,36 @@ async function onSubmit() {
v-if="!notification?.template"
name="title"
:label="$t('Notifications.Notifications:Title')"
- required
- >
+ required>
+ required>
+ :toolbar="getMarkdownToolbar" />
+ :label="$t('Notifications.Notifications:Description')">
+ :label="$t('Notifications.Notifications:Severity')">
+ :options="notificationSeverityOptions" />
diff --git a/apps/vben5/packages/@abp/notifications/src/components/my-notifilers/MyNotificationModal.vue b/apps/vben5/packages/@abp/notifications/src/components/my-notifilers/MyNotificationModal.vue
index dc4f42a20..33c32630c 100644
--- a/apps/vben5/packages/@abp/notifications/src/components/my-notifilers/MyNotificationModal.vue
+++ b/apps/vben5/packages/@abp/notifications/src/components/my-notifilers/MyNotificationModal.vue
@@ -23,8 +23,7 @@ const [Modal, modalApi] = useVbenModal({
+ :value="notification?.message as string" />
diff --git a/apps/vben5/packages/@abp/notifications/src/components/my-notifilers/MyNotificationTable.vue b/apps/vben5/packages/@abp/notifications/src/components/my-notifilers/MyNotificationTable.vue
index 4a35f4522..e3f68c0fe 100644
--- a/apps/vben5/packages/@abp/notifications/src/components/my-notifilers/MyNotificationTable.vue
+++ b/apps/vben5/packages/@abp/notifications/src/components/my-notifilers/MyNotificationTable.vue
@@ -211,17 +211,17 @@ const [NotificationModal, modalApi] = useVbenModal({
async function onClickRead(row: Notification) {
modalApi.setData(row);
modalApi.open();
- await _onRead([row.id], NotificationReadState.Read);
+ await onRead([row.id], NotificationReadState.Read);
}
/** 行标记阅读状态 */
async function onRowRead(row: Notification, info: MenuInfo) {
switch (info.key) {
case 'read': {
- return await _onRead([row.id], NotificationReadState.Read);
+ return await onRead([row.id], NotificationReadState.Read);
}
case 'un-read': {
- return await _onRead([row.id], NotificationReadState.UnRead);
+ return await onRead([row.id], NotificationReadState.UnRead);
}
}
}
@@ -230,18 +230,18 @@ async function onRowRead(row: Notification, info: MenuInfo) {
async function onBulkRead(info: MenuInfo) {
switch (info.key) {
case 'read': {
- await _onRead(selectedKeys.value, NotificationReadState.Read);
+ await onRead(selectedKeys.value, NotificationReadState.Read);
break;
}
case 'un-read': {
- await _onRead(selectedKeys.value, NotificationReadState.UnRead);
+ await onRead(selectedKeys.value, NotificationReadState.UnRead);
break;
}
}
selectedKeys.value = [];
}
-async function _onRead(idList: string[], state: NotificationReadState) {
+async function onRead(idList: string[], state: NotificationReadState) {
try {
gridApi.setLoading(true);
await markReadStateApi({
@@ -305,8 +305,7 @@ const onDelete = (row: Notification) => {
+ color="#00DD00" />
{{ row.title }}
@@ -320,8 +319,7 @@ const onDelete = (row: Notification) => {
:icon="h(DeleteOutlined)"
danger
type="link"
- @click="onDelete(row)"
- >
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
diff --git a/apps/vben5/packages/@abp/notifications/src/components/send-records/NotificationSendRecordTable.vue b/apps/vben5/packages/@abp/notifications/src/components/send-records/NotificationSendRecordTable.vue
index 275f0b0fe..2a472f30d 100644
--- a/apps/vben5/packages/@abp/notifications/src/components/send-records/NotificationSendRecordTable.vue
+++ b/apps/vben5/packages/@abp/notifications/src/components/send-records/NotificationSendRecordTable.vue
@@ -335,8 +335,7 @@ async function onSend(row: NotificationSendRecordDto) {
:icon="h(DeleteOutlined)"
danger
type="link"
- @click="onDelete(row)"
- >
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
diff --git a/apps/vben5/packages/@abp/notifications/src/types/groups.ts b/apps/vben5/packages/@abp/notifications/src/types/groups.ts
index a75894faf..e716c87fb 100644
--- a/apps/vben5/packages/@abp/notifications/src/types/groups.ts
+++ b/apps/vben5/packages/@abp/notifications/src/types/groups.ts
@@ -12,15 +12,13 @@ interface NotificationGroupDefinitionGetListInput {
filter?: string;
}
-interface NotificationGroupDefinitionCreateOrUpdateDto
- extends IHasExtraProperties {
+interface NotificationGroupDefinitionCreateOrUpdateDto extends IHasExtraProperties {
allowSubscriptionToClients: boolean;
description?: string;
displayName: string;
}
-interface NotificationGroupDefinitionCreateDto
- extends NotificationGroupDefinitionCreateOrUpdateDto {
+interface NotificationGroupDefinitionCreateDto extends NotificationGroupDefinitionCreateOrUpdateDto {
name: string;
}
diff --git a/apps/vben5/packages/@abp/notifications/src/types/notifications.ts b/apps/vben5/packages/@abp/notifications/src/types/notifications.ts
index 710fb18ad..62f1d2b93 100644
--- a/apps/vben5/packages/@abp/notifications/src/types/notifications.ts
+++ b/apps/vben5/packages/@abp/notifications/src/types/notifications.ts
@@ -46,7 +46,7 @@ interface NotificationSendInput {
/** 当前文化(模板渲染时需要) */
culture?: string;
/** 通知数据 */
- data: Dictionary;
+ data: Dictionary;
/** 通知(模板通知时为模板名称)名称 */
name: string;
/** 紧急程度 */
diff --git a/apps/vben5/packages/@abp/openiddict/src/components/applications/ApplicationModal.vue b/apps/vben5/packages/@abp/openiddict/src/components/applications/ApplicationModal.vue
index 57cea20b8..dc931fc6e 100644
--- a/apps/vben5/packages/@abp/openiddict/src/components/applications/ApplicationModal.vue
+++ b/apps/vben5/packages/@abp/openiddict/src/components/applications/ApplicationModal.vue
@@ -290,85 +290,71 @@ function onUriDelete(uri: string) {
ref="form"
:label-col="{ span: 6 }"
:model="formModel"
- :wrapper-col="{ span: 18 }"
- >
+ :wrapper-col="{ span: 18 }">
+ required>
+ :options="applicationTypes" />
+ required>
+ name="clientType">
+ :options="clientTypes" />
+ name="clientSecret">
+ autocomplete="off" />
+ name="clientUri">
+ name="logoUri">
+ name="consentType">
+ default-value="explicit" />
+ name="displayName">
+ @delete="onDisplayNameDelete" />
@@ -395,76 +381,65 @@ function onUriDelete(uri: string) {
:title="uriComponentState.title"
:uris="uriComponentState.uris"
@change="onUriChange"
- @delete="onUriDelete"
- />
+ @delete="onUriDelete" />
+ :extra="$t('AbpOpenIddict.Description:AccessTokenLifetime')">
+ v-model:value="formModel.settings.tokenLifetime.accessToken" />
+ :extra="$t('AbpOpenIddict.Description:AuthorizationCodeLifetime')">
+ v-model:value="
+ formModel.settings.tokenLifetime.authorizationCode
+ " />
+ :extra="$t('AbpOpenIddict.Description:IdentityTokenLifetime')">
+ v-model:value="formModel.settings.tokenLifetime.identityToken" />
+ :extra="$t('AbpOpenIddict.Description:RefreshTokenLifetime')">
+ v-model:value="formModel.settings.tokenLifetime.refreshToken" />
+ :extra="$t('AbpOpenIddict.Description:DeviceCodeLifetime')">
+ v-model:value="formModel.settings.tokenLifetime.deviceCode" />
+ :extra="$t('AbpOpenIddict.Description:UserCodeLifetime')">
+ v-model:value="formModel.settings.tokenLifetime.userCode" />
@@ -481,18 +456,15 @@ function onUriDelete(uri: string) {
$t('AbpOpenIddict.Assigned'),
$t('AbpOpenIddict.Available'),
]"
- class="tree-transfer"
- />
+ class="tree-transfer" />
+ :name="['requirements', 'features', 'requirePkce']">
+ v-model:checked="formModel.requirements.features.requirePkce">
{{ $t('AbpOpenIddict.Requirements:PKCE') }}
@@ -500,37 +472,31 @@ function onUriDelete(uri: string) {
:label="$t('AbpOpenIddict.DisplayName:Endpoints')"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 20 }"
- name="endpoints"
- >
+ name="endpoints">
+ mode="tags" />
+ name="grantTypes">
+ mode="tags" />
+ name="responseTypes">
+ mode="tags" />
@@ -538,8 +504,7 @@ function onUriDelete(uri: string) {
+ @delete="onPropDelete" />
diff --git a/apps/vben5/packages/@abp/openiddict/src/components/applications/ApplicationTable.vue b/apps/vben5/packages/@abp/openiddict/src/components/applications/ApplicationTable.vue
index 8860b6d61..cb22a275f 100644
--- a/apps/vben5/packages/@abp/openiddict/src/components/applications/ApplicationTable.vue
+++ b/apps/vben5/packages/@abp/openiddict/src/components/applications/ApplicationTable.vue
@@ -241,8 +241,7 @@ const onMenuClick = (row: OpenIddictApplicationDto, info: MenuInfo) => {
:icon="h(PlusOutlined)"
type="primary"
v-access:code="[ApplicationsPermissions.Create]"
- @click="onCreate"
- >
+ @click="onCreate">
{{ $t('AbpOpenIddict.Applications:AddNew') }}
@@ -266,8 +265,7 @@ const onMenuClick = (row: OpenIddictApplicationDto, info: MenuInfo) => {
block
type="link"
v-access:code="[ApplicationsPermissions.Update]"
- @click="onUpdate(row)"
- >
+ @click="onUpdate(row)">
{{ $t('AbpUi.Edit') }}
@@ -278,8 +276,7 @@ const onMenuClick = (row: OpenIddictApplicationDto, info: MenuInfo) => {
danger
type="link"
v-access:code="[ApplicationsPermissions.Delete]"
- @click="onDelete(row)"
- >
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
@@ -293,8 +290,7 @@ const onMenuClick = (row: OpenIddictApplicationDto, info: MenuInfo) => {
hasAccessByCodes([ApplicationsPermissions.ManageSecret])
"
key="secret"
- :icon="h(SecretIcon)"
- >
+ :icon="h(SecretIcon)">
{{ $t('AbpOpenIddict.GenerateSecret') }}
diff --git a/apps/vben5/packages/@abp/openiddict/src/components/authorizations/AuthorizationModal.vue b/apps/vben5/packages/@abp/openiddict/src/components/authorizations/AuthorizationModal.vue
index 987bea8cd..4d610f39f 100644
--- a/apps/vben5/packages/@abp/openiddict/src/components/authorizations/AuthorizationModal.vue
+++ b/apps/vben5/packages/@abp/openiddict/src/components/authorizations/AuthorizationModal.vue
@@ -144,8 +144,7 @@ async function onGet(id: string) {
v-for="scope in modelValue"
:key="scope"
:title="scope"
- :value="scope"
- />
+ :value="scope" />
diff --git a/apps/vben5/packages/@abp/openiddict/src/components/authorizations/AuthorizationTable.vue b/apps/vben5/packages/@abp/openiddict/src/components/authorizations/AuthorizationTable.vue
index fb2364db5..ce4792485 100644
--- a/apps/vben5/packages/@abp/openiddict/src/components/authorizations/AuthorizationTable.vue
+++ b/apps/vben5/packages/@abp/openiddict/src/components/authorizations/AuthorizationTable.vue
@@ -243,8 +243,7 @@ onMounted(onSearchClient);
class="w-full"
show-search
@change="onChangeClient"
- @search="onSearchClient"
- />
+ @search="onSearchClient" />
@@ -265,8 +264,7 @@ onMounted(onSearchClient);
:icon="h(EditOutlined)"
block
type="link"
- @click="onUpdate(row)"
- >
+ @click="onUpdate(row)">
{{ $t('AbpUi.Edit') }}
@@ -277,8 +275,7 @@ onMounted(onSearchClient);
danger
type="link"
v-access:code="[AuthorizationsPermissions.Delete]"
- @click="onDelete(row)"
- >
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
diff --git a/apps/vben5/packages/@abp/openiddict/src/components/display-names/DisplayNameTable.vue b/apps/vben5/packages/@abp/openiddict/src/components/display-names/DisplayNameTable.vue
index 2ac62bac1..1d7098c9c 100644
--- a/apps/vben5/packages/@abp/openiddict/src/components/display-names/DisplayNameTable.vue
+++ b/apps/vben5/packages/@abp/openiddict/src/components/display-names/DisplayNameTable.vue
@@ -18,8 +18,7 @@ defineOptions({
const props = defineProps
();
const emits = defineEmits<{
- (event: 'change', data: DisplayNameInfo): void;
- (event: 'delete', data: DisplayNameInfo): void;
+ (event: 'change' | 'delete', data: DisplayNameInfo): void;
}>();
const getDataResource = computed((): DisplayNameInfo[] => {
@@ -77,8 +76,7 @@ function onChange(prop: DisplayNameInfo) {
+ :toolbar-config="{ slots: { tools: 'toolbar_tools' } }">
{{ $t('AbpOpenIddict.DisplayName:AddNew') }}
@@ -88,8 +86,7 @@ function onChange(prop: DisplayNameInfo) {
+ @confirm="onDelete(row)">
{{ $t('AbpUi.Delete') }}
diff --git a/apps/vben5/packages/@abp/openiddict/src/components/properties/PropertyTable.vue b/apps/vben5/packages/@abp/openiddict/src/components/properties/PropertyTable.vue
index cd535e0fc..e3f019d9d 100644
--- a/apps/vben5/packages/@abp/openiddict/src/components/properties/PropertyTable.vue
+++ b/apps/vben5/packages/@abp/openiddict/src/components/properties/PropertyTable.vue
@@ -18,8 +18,7 @@ defineOptions({
const props = defineProps();
const emits = defineEmits<{
- (event: 'change', data: PropertyInfo): void;
- (event: 'delete', data: PropertyInfo): void;
+ (event: 'change' | 'delete', data: PropertyInfo): void;
}>();
const getDataResource = computed((): PropertyInfo[] => {
@@ -75,8 +74,7 @@ function onChange(prop: PropertyInfo) {
+ :toolbar-config="{ slots: { tools: 'toolbar_tools' } }">
{{ $t('AbpOpenIddict.Propertites:New') }}
@@ -86,8 +84,7 @@ function onChange(prop: PropertyInfo) {
+ @confirm="onDelete(row)">
{{ $t('AbpUi.Delete') }}
diff --git a/apps/vben5/packages/@abp/openiddict/src/components/scopes/ScopeModal.vue b/apps/vben5/packages/@abp/openiddict/src/components/scopes/ScopeModal.vue
index dc2892d42..2056ba40a 100644
--- a/apps/vben5/packages/@abp/openiddict/src/components/scopes/ScopeModal.vue
+++ b/apps/vben5/packages/@abp/openiddict/src/components/scopes/ScopeModal.vue
@@ -143,16 +143,14 @@ function onPropDelete(prop: PropertyInfo) {
ref="form"
:label-col="{ span: 6 }"
:model="formModel"
- :wrapper-col="{ span: 18 }"
- >
+ :wrapper-col="{ span: 18 }">
+ required>
@@ -160,29 +158,25 @@ function onPropDelete(prop: PropertyInfo) {
+ name="displayName">
+ @delete="onDisplayNameDelete" />
+ name="description">
+ @delete="onDescriptionDelete" />
@@ -198,16 +192,14 @@ function onPropDelete(prop: PropertyInfo) {
$t('AbpOpenIddict.Assigned'),
$t('AbpOpenIddict.Available'),
]"
- class="tree-transfer"
- />
+ class="tree-transfer" />
+ @delete="onPropDelete" />
diff --git a/apps/vben5/packages/@abp/openiddict/src/components/scopes/ScopeTable.vue b/apps/vben5/packages/@abp/openiddict/src/components/scopes/ScopeTable.vue
index 1cdc7da32..5944ecb58 100644
--- a/apps/vben5/packages/@abp/openiddict/src/components/scopes/ScopeTable.vue
+++ b/apps/vben5/packages/@abp/openiddict/src/components/scopes/ScopeTable.vue
@@ -190,8 +190,7 @@ const onMenuClick = (row: OpenIddictScopeDto, info: MenuInfo) => {
:icon="h(PlusOutlined)"
type="primary"
v-access:code="[ScopesPermissions.Create]"
- @click="onCreate"
- >
+ @click="onCreate">
{{ $t('AbpOpenIddict.Scopes:AddNew') }}
@@ -214,8 +213,7 @@ const onMenuClick = (row: OpenIddictScopeDto, info: MenuInfo) => {
block
type="link"
v-access:code="[ScopesPermissions.Update]"
- @click="onUpdate(row)"
- >
+ @click="onUpdate(row)">
{{ $t('AbpUi.Edit') }}
{
danger
type="link"
v-access:code="[ScopesPermissions.Delete]"
- @click="onDelete(row)"
- >
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
@@ -234,8 +231,7 @@ const onMenuClick = (row: OpenIddictScopeDto, info: MenuInfo) => {
diff --git a/apps/vben5/packages/@abp/openiddict/src/components/tokens/TokenTable.vue b/apps/vben5/packages/@abp/openiddict/src/components/tokens/TokenTable.vue
index 11aafb492..11900a9e0 100644
--- a/apps/vben5/packages/@abp/openiddict/src/components/tokens/TokenTable.vue
+++ b/apps/vben5/packages/@abp/openiddict/src/components/tokens/TokenTable.vue
@@ -268,8 +268,7 @@ onMounted(onSearchClient);
class="w-full"
show-search
@change="onChangeClient"
- @search="onSearchClient"
- />
+ @search="onSearchClient" />
@@ -290,8 +289,7 @@ onMounted(onSearchClient);
:icon="h(EditOutlined)"
block
type="link"
- @click="onUpdate(row)"
- >
+ @click="onUpdate(row)">
{{ $t('AbpUi.Edit') }}
@@ -302,8 +300,7 @@ onMounted(onSearchClient);
danger
type="link"
v-access:code="[TokensPermissions.Delete]"
- @click="onDelete(row)"
- >
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
diff --git a/apps/vben5/packages/@abp/openiddict/src/components/uris/UriTable.vue b/apps/vben5/packages/@abp/openiddict/src/components/uris/UriTable.vue
index 956dea577..2b541e947 100644
--- a/apps/vben5/packages/@abp/openiddict/src/components/uris/UriTable.vue
+++ b/apps/vben5/packages/@abp/openiddict/src/components/uris/UriTable.vue
@@ -77,8 +77,7 @@ function onChange(uri: string) {
+ :toolbar-config="toolbarConfig">
{{ title }}
@@ -91,8 +90,7 @@ function onChange(uri: string) {
+ @confirm="onDelete(row)">
{{ $t('AbpUi.Delete') }}
diff --git a/apps/vben5/packages/@abp/openiddict/src/types/applications.ts b/apps/vben5/packages/@abp/openiddict/src/types/applications.ts
index 74c2fd70d..8c3f89683 100644
--- a/apps/vben5/packages/@abp/openiddict/src/types/applications.ts
+++ b/apps/vben5/packages/@abp/openiddict/src/types/applications.ts
@@ -26,8 +26,7 @@ interface OpenIddictApplicationSettingsDto {
tokenLifetime: OpenIddictApplicationTokenLifetimesDto;
}
-interface OpenIddictApplicationGetListInput
- extends PagedAndSortedResultRequestDto {
+interface OpenIddictApplicationGetListInput extends PagedAndSortedResultRequestDto {
filter?: string;
}
diff --git a/apps/vben5/packages/@abp/openiddict/src/types/authorizations.ts b/apps/vben5/packages/@abp/openiddict/src/types/authorizations.ts
index eedc7712d..7907a0ba3 100644
--- a/apps/vben5/packages/@abp/openiddict/src/types/authorizations.ts
+++ b/apps/vben5/packages/@abp/openiddict/src/types/authorizations.ts
@@ -4,8 +4,7 @@ import type {
PagedAndSortedResultRequestDto,
} from '@abp/core';
-interface OpenIddictAuthorizationDto
- extends ExtensibleAuditedEntityDto {
+interface OpenIddictAuthorizationDto extends ExtensibleAuditedEntityDto {
applicationId?: string;
creationDate?: string;
properties?: Dictionary;
@@ -15,8 +14,7 @@ interface OpenIddictAuthorizationDto
type?: string;
}
-interface OpenIddictAuthorizationGetListInput
- extends PagedAndSortedResultRequestDto {
+interface OpenIddictAuthorizationGetListInput extends PagedAndSortedResultRequestDto {
beginCreationTime?: string;
clientId?: string;
endCreationTime?: string;
diff --git a/apps/vben5/packages/@abp/oss/src/components/containers/ContainerTable.vue b/apps/vben5/packages/@abp/oss/src/components/containers/ContainerTable.vue
index ca6db4fcf..a3b5d6ae1 100644
--- a/apps/vben5/packages/@abp/oss/src/components/containers/ContainerTable.vue
+++ b/apps/vben5/packages/@abp/oss/src/components/containers/ContainerTable.vue
@@ -188,8 +188,7 @@ function onDelete(row: OssContainerDto) {
danger
block
type="link"
- @click="onDelete(row)"
- >
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
diff --git a/apps/vben5/packages/@abp/oss/src/components/objects/FileList.vue b/apps/vben5/packages/@abp/oss/src/components/objects/FileList.vue
index 34af7586c..a02d6ac8a 100644
--- a/apps/vben5/packages/@abp/oss/src/components/objects/FileList.vue
+++ b/apps/vben5/packages/@abp/oss/src/components/objects/FileList.vue
@@ -260,8 +260,7 @@ watch(
v-if="props.path"
:icon="h(UploadOutlined)"
type="primary"
- @click="onUpload"
- >
+ @click="onUpload">
{{ $t('AbpOssManagement.Objects:UploadFile') }}
@@ -274,8 +273,7 @@ watch(
:icon="h(DownloadOutlined)"
block
type="link"
- @click="onDownload(row)"
- >
+ @click="onDownload(row)">
{{ $t('AbpOssManagement.Objects:Download') }}
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
diff --git a/apps/vben5/packages/@abp/oss/src/components/objects/FileUploadModal.vue b/apps/vben5/packages/@abp/oss/src/components/objects/FileUploadModal.vue
index 69b38a1f8..3760d88de 100644
--- a/apps/vben5/packages/@abp/oss/src/components/objects/FileUploadModal.vue
+++ b/apps/vben5/packages/@abp/oss/src/components/objects/FileUploadModal.vue
@@ -195,8 +195,7 @@ watch(
@file-error="onUploadError"
@file-progress="onUploadProgress"
@file-success="onUploadSuccess"
- @files-submitted="onFileSubmitted"
- >
+ @files-submitted="onFileSubmitted">
@@ -212,13 +211,11 @@ watch(
+ :title="$t('AbpOssManagement.DisplayName:Name')" />
+ width="100">
{{ formatSize(row.size) }}
@@ -226,8 +223,7 @@ watch(
+ width="180">
{{ $t('AbpOssManagement.Upload:Completed') }}
@@ -249,8 +245,7 @@ watch(
fixed="right"
field="action"
:title="$t('AbpUi.Actions')"
- width="100"
- >
+ width="100">
@@ -258,21 +253,18 @@ watch(
v-if="row.paused || row.error"
:icon="h(CaretRightOutlined)"
@click="onResume(row)"
- type="link"
- />
+ type="link" />
+ type="link" />
+ danger />
diff --git a/apps/vben5/packages/@abp/oss/src/components/objects/FolderTree.vue b/apps/vben5/packages/@abp/oss/src/components/objects/FolderTree.vue
index 54ce10f7a..4946e8418 100644
--- a/apps/vben5/packages/@abp/oss/src/components/objects/FolderTree.vue
+++ b/apps/vben5/packages/@abp/oss/src/components/objects/FolderTree.vue
@@ -15,8 +15,7 @@ import { Button, Card, DirectoryTree, Empty, Select } from 'ant-design-vue';
import { useContainesApi } from '../../api';
const emits = defineEmits<{
- (event: 'bucketChange', data: string): void;
- (event: 'folderChange', data: string): void;
+ (event: 'bucketChange' | 'folderChange', data: string): void;
}>();
interface Folder {
@@ -160,8 +159,7 @@ onMounted(onInit);
:options="containers"
:field-names="{ label: 'name', value: 'name' }"
v-model:value="bucket"
- @change="(e) => onBucketChange(e!.toString())"
- />
+ @change="(e) => onBucketChange(e!.toString())" />
{{ $t('AbpOssManagement.Objects:CreateFolder') }}
@@ -175,8 +173,7 @@ onMounted(onInit);
:tree-data="folders"
:load-data="onLoadChildFolders"
@select="onFolderChange"
- @expand="onFolderExpand"
- />
+ @expand="onFolderExpand" />
diff --git a/apps/vben5/packages/@abp/oss/src/components/objects/ObjectPage.vue b/apps/vben5/packages/@abp/oss/src/components/objects/ObjectPage.vue
index 26a2f0734..36e92e1c7 100644
--- a/apps/vben5/packages/@abp/oss/src/components/objects/ObjectPage.vue
+++ b/apps/vben5/packages/@abp/oss/src/components/objects/ObjectPage.vue
@@ -22,8 +22,7 @@ function onFolderChange(val: string) {
+ @folder-change="onFolderChange" />
diff --git a/apps/vben5/packages/@abp/permissions/src/components/definitions/groups/PermissionGroupDefinitionModal.vue b/apps/vben5/packages/@abp/permissions/src/components/definitions/groups/PermissionGroupDefinitionModal.vue
index d5044eaa6..a741c67a6 100644
--- a/apps/vben5/packages/@abp/permissions/src/components/definitions/groups/PermissionGroupDefinitionModal.vue
+++ b/apps/vben5/packages/@abp/permissions/src/components/definitions/groups/PermissionGroupDefinitionModal.vue
@@ -73,7 +73,9 @@ const [Modal, modalApi] = useVbenModal({
try {
modalApi.setState({ loading: true });
const { name } = modalApi.getData
();
- name && (await onGet(name));
+ if (name) {
+ await onGet(name);
+ }
} finally {
modalApi.setState({ loading: false });
}
@@ -96,6 +98,7 @@ function onPropChange(prop: PropertyInfo) {
}
function onPropDelete(prop: PropertyInfo) {
formModel.value.extraProperties ??= {};
+ // oxlint-disable-next-line typescript/no-dynamic-delete
delete formModel.value.extraProperties[prop.key];
}
@@ -106,31 +109,26 @@ function onPropDelete(prop: PropertyInfo) {
ref="form"
:label-col="{ span: 6 }"
:model="formModel"
- :wrapper-col="{ span: 18 }"
- >
+ :wrapper-col="{ span: 18 }">
+ required>
+ autocomplete="off" />
+ required>
+ :disabled="formModel.isStatic" />
@@ -139,8 +137,7 @@ function onPropDelete(prop: PropertyInfo) {
:data="formModel.extraProperties"
:disabled="formModel.isStatic"
@change="onPropChange"
- @delete="onPropDelete"
- />
+ @delete="onPropDelete" />
diff --git a/apps/vben5/packages/@abp/permissions/src/components/definitions/groups/PermissionGroupDefinitionTable.vue b/apps/vben5/packages/@abp/permissions/src/components/definitions/groups/PermissionGroupDefinitionTable.vue
index c3a1690b1..e73fa54ac 100644
--- a/apps/vben5/packages/@abp/permissions/src/components/definitions/groups/PermissionGroupDefinitionTable.vue
+++ b/apps/vben5/packages/@abp/permissions/src/components/definitions/groups/PermissionGroupDefinitionTable.vue
@@ -214,8 +214,7 @@ onMounted(onGet);
:icon="h(PlusOutlined)"
type="primary"
v-access:code="[GroupDefinitionsPermissions.Create]"
- @click="onCreate"
- >
+ @click="onCreate">
{{ $t('AbpPermissionManagement.GroupDefinitions:AddNew') }}
@@ -227,8 +226,7 @@ onMounted(onGet);
block
type="link"
v-access:code="[GroupDefinitionsPermissions.Update]"
- @click="onUpdate(row)"
- >
+ @click="onUpdate(row)">
{{ $t('AbpUi.Edit') }}
@@ -240,8 +238,7 @@ onMounted(onGet);
danger
type="link"
v-access:code="[GroupDefinitionsPermissions.Delete]"
- @click="onDelete(row)"
- >
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
@@ -256,8 +253,7 @@ onMounted(onGet);
])
"
key="permissions"
- :icon="h(PermissionsOutlined)"
- >
+ :icon="h(PermissionsOutlined)">
{{
$t('AbpPermissionManagement.PermissionDefinitions:AddNew')
}}
diff --git a/apps/vben5/packages/@abp/permissions/src/components/definitions/permissions/PermissionDefinitionModal.vue b/apps/vben5/packages/@abp/permissions/src/components/definitions/permissions/PermissionDefinitionModal.vue
index ce8793210..794e6baf0 100644
--- a/apps/vben5/packages/@abp/permissions/src/components/definitions/permissions/PermissionDefinitionModal.vue
+++ b/apps/vben5/packages/@abp/permissions/src/components/definitions/permissions/PermissionDefinitionModal.vue
@@ -122,7 +122,9 @@ const [Modal, modalApi] = useVbenModal({
try {
modalApi.setState({ loading: true });
const { groupName, name } = modalApi.getData();
- name && (await onGet(name));
+ if (name) {
+ await onGet(name);
+ }
await Promise.all([onInitGroups(groupName), onInitProviders()]);
} finally {
modalApi.setState({ loading: false });
@@ -184,6 +186,7 @@ function onPropChange(prop: PropertyInfo) {
}
function onPropDelete(prop: PropertyInfo) {
formModel.value.extraProperties ??= {};
+ // oxlint-disable-next-line typescript/no-dynamic-delete
delete formModel.value.extraProperties[prop.key];
}
@@ -194,27 +197,23 @@ function onPropDelete(prop: PropertyInfo) {
ref="form"
:label-col="{ span: 6 }"
:model="formModel"
- :wrapper-col="{ span: 18 }"
- >
+ :wrapper-col="{ span: 18 }">
+ name="isEnabled">
+ :disabled="formModel.isStatic">
{{ $t('AbpPermissionManagement.DisplayName:IsEnabled') }}
+ required>
+ name="parentName">
+ :tree-data="availablePermissions" />
+ required>
+ autocomplete="off" />
+ required>
+ :disabled="formModel.isStatic" />
+ name="multiTenancySide">
+ :options="multiTenancySideOptions" />
+ name="providers">
+ mode="multiple" />
+ :tab="$t('AbpPermissionManagement.StateCheckers')">
+ :wrapper-col="{ span: 24 }">
+ :state="permissionState" />
@@ -316,8 +301,7 @@ function onPropDelete(prop: PropertyInfo) {
:data="formModel.extraProperties"
:disabled="formModel.isStatic"
@change="onPropChange"
- @delete="onPropDelete"
- />
+ @delete="onPropDelete" />
diff --git a/apps/vben5/packages/@abp/permissions/src/components/definitions/permissions/PermissionDefinitionTable.vue b/apps/vben5/packages/@abp/permissions/src/components/definitions/permissions/PermissionDefinitionTable.vue
index 8b4316a37..57e8c0c40 100644
--- a/apps/vben5/packages/@abp/permissions/src/components/definitions/permissions/PermissionDefinitionTable.vue
+++ b/apps/vben5/packages/@abp/permissions/src/components/definitions/permissions/PermissionDefinitionTable.vue
@@ -288,8 +288,7 @@ onMounted(onGet);
:icon="h(PlusOutlined)"
type="primary"
v-access:code="[GroupDefinitionsPermissions.Create]"
- @click="onCreate"
- >
+ @click="onCreate">
{{ $t('AbpPermissionManagement.PermissionDefinitions:AddNew') }}
@@ -301,8 +300,7 @@ onMounted(onGet);
trigger: 'row',
rowField: 'name',
childrenField: 'children',
- }"
- >
+ }">
{{ multiTenancySidesMap[permission.multiTenancySide] }}
@@ -322,8 +320,7 @@ onMounted(onGet);
block
type="link"
v-access:code="[GroupDefinitionsPermissions.Update]"
- @click="onUpdate(permission)"
- >
+ @click="onUpdate(permission)">
{{ $t('AbpUi.Edit') }}
+ @click="onDelete(permission)">
{{ $t('AbpUi.Delete') }}
diff --git a/apps/vben5/packages/@abp/permissions/src/components/permissions/PermissionGrantedModal.vue b/apps/vben5/packages/@abp/permissions/src/components/permissions/PermissionGrantedModal.vue
index f5defeb2a..e66444179 100644
--- a/apps/vben5/packages/@abp/permissions/src/components/permissions/PermissionGrantedModal.vue
+++ b/apps/vben5/packages/@abp/permissions/src/components/permissions/PermissionGrantedModal.vue
@@ -317,14 +317,12 @@ async function onDeleteGrand(grantedInfo: PermissionProvider) {
+ :title="$t('AbpPermissionManagement.AllPermissions')">
+ placeholder="搜索权限" />
+ @select="onSelectNode">
{{
@@ -376,8 +373,7 @@ async function onDeleteGrand(grantedInfo: PermissionProvider) {
:options="permissionProviders"
:field-names="{ label: 'name', value: 'value' }"
v-model:value="providerName"
- @change="onProviderChange"
- />
+ @change="onProviderChange" />
@@ -390,8 +386,7 @@ async function onDeleteGrand(grantedInfo: PermissionProvider) {
permissionInfo!.displayName ?? permissionInfo!.name,
])
"
- @confirm="onDeleteGrand(row)"
- >
+ @confirm="onDeleteGrand(row)">
{{ $t('AbpPermissionManagement.RejectPermission') }}
diff --git a/apps/vben5/packages/@abp/permissions/src/components/permissions/PermissionModal.vue b/apps/vben5/packages/@abp/permissions/src/components/permissions/PermissionModal.vue
index 30f95862e..a91c48237 100644
--- a/apps/vben5/packages/@abp/permissions/src/components/permissions/PermissionModal.vue
+++ b/apps/vben5/packages/@abp/permissions/src/components/permissions/PermissionModal.vue
@@ -237,7 +237,9 @@ function checkChildrenGrant(current: PermissionTree, isGranted: boolean) {
if (!isGranted) {
const childKeys: string[] = [];
children.forEach((node) => {
- !isGranted && (node.isGranted = false);
+ if (!isGranted) {
+ node.isGranted = false;
+ }
childKeys.push(node.name);
});
checkedNodeKeys.value = checkedNodeKeys.value.filter(
@@ -272,7 +274,9 @@ function getChildren(permissions: PermissionTree[]): PermissionTree[] {
const children: PermissionTree[] = [];
permissions.forEach((permission) => {
children.push(permission);
- permission.children && children.push(...getChildren(permission.children));
+ if (permission.children) {
+ children.push(...getChildren(permission.children));
+ }
});
return children;
}
@@ -293,8 +297,7 @@ function onSearchPermissionGrant() {
+ v-bind="getPermissionState">
{{ $t('AbpPermissionManagement.SelectAllInAllTabs') }}
@@ -306,15 +309,13 @@ function onSearchPermissionGrant() {
+ :tab-key="permission.name">
onCheckNodeAll(e, permission)"
- >
+ @change="(e: any) => onCheckNodeAll(e, permission)">
{{ $t('AbpPermissionManagement.SelectAllInThisTab') }}
@@ -335,8 +336,7 @@ function onSearchPermissionGrant() {
onCheckNode(permission, keys, info)
"
@expand="onExpandNode"
- @select="onSelectNode"
- />
+ @select="onSelectNode" />
diff --git a/apps/vben5/packages/@abp/permissions/src/components/state-check/PermissionStateCheck.vue b/apps/vben5/packages/@abp/permissions/src/components/state-check/PermissionStateCheck.vue
index 54632cd6b..22de06e52 100644
--- a/apps/vben5/packages/@abp/permissions/src/components/state-check/PermissionStateCheck.vue
+++ b/apps/vben5/packages/@abp/permissions/src/components/state-check/PermissionStateCheck.vue
@@ -111,8 +111,7 @@ onMounted(onInit);
+ @change="onChange">
{{
$t('component.simple_state_checking.requirePermissions.requiresAll')
}}
@@ -125,8 +124,7 @@ onMounted(onInit);
:field-names="{ label: 'displayName', value: 'name' }"
:value="getRequiredPermissions"
@blur="emits('blur')"
- @change="onPermissionsChange"
- />
+ @change="onPermissionsChange" />
diff --git a/apps/vben5/packages/@abp/permissions/src/types/definitions.ts b/apps/vben5/packages/@abp/permissions/src/types/definitions.ts
index 699929135..c1274f9df 100644
--- a/apps/vben5/packages/@abp/permissions/src/types/definitions.ts
+++ b/apps/vben5/packages/@abp/permissions/src/types/definitions.ts
@@ -13,8 +13,7 @@ interface PermissionDefinitionCreateOrUpdateDto extends IHasExtraProperties {
stateCheckers: string;
}
-interface PermissionDefinitionCreateDto
- extends PermissionDefinitionCreateOrUpdateDto {
+interface PermissionDefinitionCreateDto extends PermissionDefinitionCreateOrUpdateDto {
groupName: string;
name: string;
}
diff --git a/apps/vben5/packages/@abp/permissions/src/types/groups.ts b/apps/vben5/packages/@abp/permissions/src/types/groups.ts
index ed1c5245e..a050ad4eb 100644
--- a/apps/vben5/packages/@abp/permissions/src/types/groups.ts
+++ b/apps/vben5/packages/@abp/permissions/src/types/groups.ts
@@ -1,11 +1,9 @@
import type { IHasExtraProperties } from '@abp/core';
-interface PermissionGroupDefinitionCreateOrUpdateDto
- extends IHasExtraProperties {
+interface PermissionGroupDefinitionCreateOrUpdateDto extends IHasExtraProperties {
displayName: string;
}
-interface PermissionGroupDefinitionCreateDto
- extends PermissionGroupDefinitionCreateOrUpdateDto {
+interface PermissionGroupDefinitionCreateDto extends PermissionGroupDefinitionCreateOrUpdateDto {
name: string;
}
interface PermissionGroupDefinitionDto extends IHasExtraProperties {
diff --git a/apps/vben5/packages/@abp/platform/src/components/data-dictionaries/DataDictionaryItemDrawer.vue b/apps/vben5/packages/@abp/platform/src/components/data-dictionaries/DataDictionaryItemDrawer.vue
index 6c6449a1b..9f47f7eca 100644
--- a/apps/vben5/packages/@abp/platform/src/components/data-dictionaries/DataDictionaryItemDrawer.vue
+++ b/apps/vben5/packages/@abp/platform/src/components/data-dictionaries/DataDictionaryItemDrawer.vue
@@ -238,8 +238,7 @@ function onDelete(row: DataItemDto) {
:icon="h(EditOutlined)"
block
type="link"
- @click="onUpdate(row)"
- >
+ @click="onUpdate(row)">
{{ $t('AbpUi.Edit') }}
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
diff --git a/apps/vben5/packages/@abp/platform/src/components/data-dictionaries/DataDictionaryItemModal.vue b/apps/vben5/packages/@abp/platform/src/components/data-dictionaries/DataDictionaryItemModal.vue
index 2c7eeb55c..98142e87c 100644
--- a/apps/vben5/packages/@abp/platform/src/components/data-dictionaries/DataDictionaryItemModal.vue
+++ b/apps/vben5/packages/@abp/platform/src/components/data-dictionaries/DataDictionaryItemModal.vue
@@ -234,7 +234,7 @@ function mapToDefaultValue(valueType: ValueType, defaultValue?: any) {
return (defaultValue as string[]).join(',');
}
case ValueType.Boolean: {
- return (defaultValue as Boolean).toString();
+ return (defaultValue as boolean).toString();
}
case ValueType.Date: {
return formatToDate(defaultValue);
@@ -243,7 +243,7 @@ function mapToDefaultValue(valueType: ValueType, defaultValue?: any) {
return formatToDateTime(defaultValue);
}
case ValueType.Numeic: {
- return (defaultValue as Number).toString();
+ return (defaultValue as number).toString();
}
}
return defaultValue;
diff --git a/apps/vben5/packages/@abp/platform/src/components/data-dictionaries/DataDictionaryTable.vue b/apps/vben5/packages/@abp/platform/src/components/data-dictionaries/DataDictionaryTable.vue
index 0c0c29414..a516dc703 100644
--- a/apps/vben5/packages/@abp/platform/src/components/data-dictionaries/DataDictionaryTable.vue
+++ b/apps/vben5/packages/@abp/platform/src/components/data-dictionaries/DataDictionaryTable.vue
@@ -244,8 +244,7 @@ onMounted(onGet);
v-if="isGranted([DataDictionaryPermissions.Create])"
:icon="h(PlusOutlined)"
type="primary"
- @click="onCreate"
- >
+ @click="onCreate">
{{ $t('AppPlatform.Data:AddNew') }}
@@ -267,8 +266,7 @@ onMounted(onGet);
:icon="h(EditOutlined)"
block
type="link"
- @click="onUpdate(row)"
- >
+ @click="onUpdate(row)">
{{ $t('AbpUi.Edit') }}
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
+ ">
diff --git a/apps/vben5/packages/@abp/platform/src/components/messages/email/EmailMessageTable.vue b/apps/vben5/packages/@abp/platform/src/components/messages/email/EmailMessageTable.vue
index ca9b8f00d..4da7a0f82 100644
--- a/apps/vben5/packages/@abp/platform/src/components/messages/email/EmailMessageTable.vue
+++ b/apps/vben5/packages/@abp/platform/src/components/messages/email/EmailMessageTable.vue
@@ -304,8 +304,7 @@ async function onMenuClick(row: EmailMessageDto, info: MenuInfo) {
:icon="h(EditOutlined)"
block
type="link"
- @click="onUpdate(row)"
- >
+ @click="onUpdate(row)">
{{ $t('AbpUi.Edit') }}
+ @click="onDelete(row)">
{{ $t('AbpUi.Delete') }}
@@ -323,8 +321,7 @@ async function onMenuClick(row: EmailMessageDto, info: MenuInfo) {
diff --git a/apps/vben5/packages/@core/ui-kit/layout-ui/src/components/layout-tabbar.vue b/apps/vben5/packages/@core/ui-kit/layout-ui/src/components/layout-tabbar.vue
index 148600e13..d7ca57b4a 100644
--- a/apps/vben5/packages/@core/ui-kit/layout-ui/src/components/layout-tabbar.vue
+++ b/apps/vben5/packages/@core/ui-kit/layout-ui/src/components/layout-tabbar.vue
@@ -23,8 +23,7 @@ const style = computed((): CSSProperties => {
+ class="flex w-full border-b border-border bg-background transition-all">
diff --git a/apps/vben5/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-collapse-button.vue b/apps/vben5/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-collapse-button.vue
index fa4abe95a..63bb42c56 100644
--- a/apps/vben5/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-collapse-button.vue
+++ b/apps/vben5/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-collapse-button.vue
@@ -10,9 +10,8 @@ function handleCollapsed() {
+ class="absolute bottom-2 left-3 z-10 flex-center cursor-pointer rounded-sm bg-accent p-1 text-foreground/60 hover:bg-accent-hover hover:text-foreground"
+ @click.stop="handleCollapsed">
diff --git a/apps/vben5/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-fixed-button.vue b/apps/vben5/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-fixed-button.vue
index 5cfd89b63..500808665 100644
--- a/apps/vben5/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-fixed-button.vue
+++ b/apps/vben5/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-fixed-button.vue
@@ -10,9 +10,8 @@ function toggleFixed() {
+ class="absolute right-3 bottom-2 z-10 flex-center cursor-pointer rounded-sm bg-accent p-1.25 text-foreground/60 transition-all duration-300 hover:bg-accent-hover hover:text-foreground"
+ @click="toggleFixed">
diff --git a/apps/vben5/packages/@core/ui-kit/layout-ui/src/hooks/use-sidebar-drag.ts b/apps/vben5/packages/@core/ui-kit/layout-ui/src/hooks/use-sidebar-drag.ts
index c72468891..2138877ce 100644
--- a/apps/vben5/packages/@core/ui-kit/layout-ui/src/hooks/use-sidebar-drag.ts
+++ b/apps/vben5/packages/@core/ui-kit/layout-ui/src/hooks/use-sidebar-drag.ts
@@ -1,9 +1,8 @@
-import { onUnmounted } from 'vue';
+import { ref } from 'vue';
interface DragOptions {
max: number;
min: number;
- startWidth: number;
}
interface DragElements {
@@ -14,35 +13,9 @@ interface DragElements {
type DragCallback = (newWidth: number) => void;
export function useSidebarDrag() {
- const state: {
- cleanup: (() => void) | null;
- isDragging: boolean;
- originalStyles: {
- bodyCursor: string;
- bodyUserSelect: string;
- dragBarLeft: string;
- dragBarRight: string;
- dragBarTransition: string;
- targetTransition: string;
- };
- startLeft: number;
- startWidth: number;
- startX: number;
- } = {
- cleanup: null,
- isDragging: false,
- startLeft: 0,
- startWidth: 0,
- startX: 0,
- originalStyles: {
- bodyCursor: '',
- bodyUserSelect: '',
- dragBarLeft: '',
- dragBarRight: '',
- dragBarTransition: '',
- targetTransition: '',
- },
- };
+ const isDragging = ref(false);
+ let cleanup: (() => void) | null = null;
+ let dragOverlay: HTMLElement | null = null;
const startDrag = (
e: MouseEvent,
@@ -50,108 +23,130 @@ export function useSidebarDrag() {
elements: DragElements,
onDrag: DragCallback,
) => {
- const { min, max, startWidth } = options;
+ const { min, max } = options;
const { dragBar, target } = elements;
- if (state.isDragging || !dragBar || !target) return;
+ if (isDragging.value || !dragBar || !target) return;
e.preventDefault();
e.stopPropagation();
- state.isDragging = true;
+ isDragging.value = true;
- state.startX = e.clientX;
- state.startWidth = startWidth;
- state.startLeft = dragBar.offsetLeft;
+ const startX = e.clientX;
+ const startWidth = target.getBoundingClientRect().width;
+ const startLeft = dragBar.offsetLeft;
- state.originalStyles = {
- bodyCursor: document.body.style.cursor,
- bodyUserSelect: document.body.style.userSelect,
- dragBarLeft: dragBar.style.left,
- dragBarRight: dragBar.style.right,
- dragBarTransition: dragBar.style.transition,
- targetTransition: target.style.transition,
- };
+ dragBar.classList.add('bg-primary');
+ dragBar.classList.remove('bg-primary/30');
- document.body.style.cursor = 'col-resize';
- document.body.style.userSelect = 'none';
+ const dragBarTransition = dragBar.style.transition;
+ const targetTransition = target.style.transition;
- dragBar.style.left = `${state.startLeft}px`;
- dragBar.style.right = 'auto';
dragBar.style.transition = 'none';
target.style.transition = 'none';
+ dragOverlay = document.createElement('div');
+ dragOverlay.style.position = 'fixed';
+ dragOverlay.style.inset = '0';
+ dragOverlay.style.zIndex = '9999';
+ dragOverlay.style.cursor = 'col-resize';
+ dragOverlay.style.userSelect = 'none';
+ dragOverlay.style.outline = 'none';
+ dragOverlay.tabIndex = -1;
+ dragOverlay.style.background = 'rgba(0,0,0,0)';
+ document.body.append(dragOverlay);
+
const onMouseMove = (moveEvent: MouseEvent) => {
- if (!state.isDragging || !dragBar) return;
+ if (!isDragging.value || !dragBar || !target) {
+ endDrag();
+ return;
+ }
- const deltaX = moveEvent.clientX - state.startX;
- let newLeft = state.startLeft + deltaX;
+ const deltaX = moveEvent.clientX - startX;
+ let currentWidth = startWidth + deltaX;
- if (newLeft < min) newLeft = min;
- if (newLeft > max) newLeft = max;
+ const isOutOfMin = currentWidth < min;
+ const isOutOfMax = currentWidth > max;
+ const isOutOfBounds = isOutOfMin || isOutOfMax;
+
+ if (isOutOfMin) currentWidth = min;
+ if (isOutOfMax) currentWidth = max;
+
+ const newLeft = startLeft + (currentWidth - startWidth);
+
+ if (dragOverlay)
+ dragOverlay.style.cursor = isOutOfBounds ? 'not-allowed' : 'col-resize';
dragBar.style.left = `${newLeft}px`;
- dragBar.classList.add('bg-primary');
+
+ if (isOutOfBounds) {
+ dragBar.classList.add('bg-primary/30');
+ dragBar.classList.remove('bg-primary');
+ } else {
+ dragBar.classList.add('bg-primary');
+ dragBar.classList.remove('bg-primary/30');
+ }
};
const onMouseUp = (upEvent: MouseEvent) => {
- if (!state.isDragging || !dragBar || !target) return;
+ if (!isDragging.value || !dragBar || !target) {
+ endDrag();
+ return;
+ }
- const deltaX = upEvent.clientX - state.startX;
- let newWidth = state.startWidth + deltaX;
+ const deltaX = upEvent.clientX - startX;
+ let newWidth = startWidth + deltaX;
newWidth = Math.min(max, Math.max(min, newWidth));
- dragBar.classList.remove('bg-primary');
-
- onDrag?.(newWidth);
+ dragBar.classList.remove('bg-primary', 'bg-primary/30');
- endDrag();
+ try {
+ onDrag?.(Math.round(newWidth));
+ } finally {
+ endDrag();
+ }
};
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
- const cleanup = () => {
- if (!state.cleanup) return;
+ cleanup = () => {
+ if (!cleanup) return;
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
- document.body.style.cursor = state.originalStyles.bodyCursor;
- document.body.style.userSelect = state.originalStyles.bodyUserSelect;
-
if (dragBar) {
- dragBar.style.left = state.originalStyles.dragBarLeft;
- dragBar.style.right = state.originalStyles.dragBarRight;
- dragBar.style.transition = state.originalStyles.dragBarTransition;
- dragBar.classList.remove('bg-primary');
+ dragBar.style.transition = dragBarTransition;
+ dragBar.style.left = '';
+ dragBar.classList.remove('bg-primary', 'bg-primary/30');
}
if (target) {
- target.style.transition = state.originalStyles.targetTransition;
+ target.style.transition = targetTransition;
}
- state.isDragging = false;
- state.cleanup = null;
- };
+ if (dragOverlay) {
+ dragOverlay.remove();
+ dragOverlay = null;
+ }
- state.cleanup = cleanup;
+ isDragging.value = false;
+ cleanup = null;
+ };
};
const endDrag = () => {
- state.cleanup?.();
+ cleanup?.();
};
- onUnmounted(() => {
- endDrag();
- });
-
return {
startDrag,
endDrag,
get isDragging() {
- return state.isDragging;
+ return isDragging.value;
},
};
}
diff --git a/apps/vben5/packages/@core/ui-kit/layout-ui/src/vben-layout.ts b/apps/vben5/packages/@core/ui-kit/layout-ui/src/vben-layout.ts
index b6b413751..6cbe54f5b 100644
--- a/apps/vben5/packages/@core/ui-kit/layout-ui/src/vben-layout.ts
+++ b/apps/vben5/packages/@core/ui-kit/layout-ui/src/vben-layout.ts
@@ -126,6 +126,10 @@ interface VbenLayoutProps {
* @default 48
*/
sidebarExtraCollapsedWidth?: number;
+ /**
+ * 扩展区域extra-title的高度
+ */
+ sidebarExtraTitleHeight?: number;
/**
* 侧边菜单折叠按钮是否固定
* @default true
@@ -136,6 +140,10 @@ interface VbenLayoutProps {
* @default false
*/
sidebarHidden?: boolean;
+ /**
+ * 侧边栏 Logo 区域是否显示
+ */
+ sidebarLogoVisible: boolean;
/**
* 混合侧边栏宽度
* @default 80
diff --git a/apps/vben5/packages/@core/ui-kit/layout-ui/src/vben-layout.vue b/apps/vben5/packages/@core/ui-kit/layout-ui/src/vben-layout.vue
index 7e444068f..e4b3a1438 100644
--- a/apps/vben5/packages/@core/ui-kit/layout-ui/src/vben-layout.vue
+++ b/apps/vben5/packages/@core/ui-kit/layout-ui/src/vben-layout.vue
@@ -67,7 +67,7 @@ const props = withDefaults(defineProps(), {
const emit = defineEmits<{
sideMouseLeave: [];
toggleSidebar: [];
- 'update:sidebar-width': [value: number];
+ 'update:sidebarWidth': [value: number];
}>();
const sidebarDraggable = defineModel('sidebarDraggable', {
default: true,
@@ -275,7 +275,7 @@ const mainStyle = computed(() => {
// 计算 tabbar 的样式
const tabbarStyle = computed((): CSSProperties => {
- let width = '';
+ let width: string;
let marginLeft = 0;
// 如果不是混合导航,tabbar 的宽度为 100%
@@ -368,6 +368,17 @@ const maskStyle = computed((): CSSProperties => {
return { zIndex: props.zIndex };
});
+/**
+ * 侧边栏 Logo 区域是否显示
+ */
+const sidebarHeaderHeight = computed(() => {
+ if (isMixedNav.value || !props.sidebarLogoVisible) {
+ return 0;
+ }
+
+ return props.headerHeight;
+});
+
const showHeaderToggleButton = computed(() => {
return (
props.isMobile ||
@@ -510,7 +521,10 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
:dom-visible="!isMobile"
:extra-width="sidebarExtraWidth"
:fixed-extra="sidebarExpandOnHover"
- :header-height="isMixedNav ? 0 : headerHeight"
+ :header-height="sidebarHeaderHeight"
+ :extra-title-height="
+ isSidebarMixedNav || isHeaderMixedNav ? sidebarExtraTitleHeight : 0
+ "
:is-sidebar-mixed="isSidebarMixedNav || isHeaderMixedNav"
:margin-top="sidebarMarginTop"
:mixed-width="sidebarMixedWidth"
@@ -520,9 +534,8 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
:width="getSidebarWidth"
:z-index="sidebarZIndex"
@leave="() => emit('sideMouseLeave')"
- @update:width="(val) => emit('update:sidebar-width', val)"
- >
-
+ @update:width="(val) => emit('update:sidebarWidth', val)">
+
@@ -543,8 +556,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
+ class="flex flex-1 flex-col overflow-hidden transition-all duration-300 ease-in">
+ class="overflow-hidden transition-all duration-200">
+ :logo-visible="sidebarLogoVisible">
@@ -574,8 +585,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
+ @click="handleHeaderToggle">
@@ -586,8 +596,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
+ :style="tabbarStyle">
@@ -603,8 +612,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
:padding-right="contentPaddingRight"
:padding-top="contentPaddingTop"
:style="contentStyle"
- class="transition-[margin-top] duration-200"
- >
+ class="transition-[margin-top] duration-200">
@@ -618,8 +626,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
:height="footerHeight"
:show="!isFullContent"
:width="footerWidth"
- :z-index="zIndex"
- >
+ :z-index="zIndex">
@@ -627,8 +634,7 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
+ class="fixed top-0 left-0 size-full bg-overlay transition-[background-color] duration-200"
+ @click="handleClickMask">
diff --git a/apps/vben5/packages/@core/ui-kit/layout-ui/tailwind.config.mjs b/apps/vben5/packages/@core/ui-kit/layout-ui/tailwind.config.mjs
deleted file mode 100644
index f17f556fa..000000000
--- a/apps/vben5/packages/@core/ui-kit/layout-ui/tailwind.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@vben/tailwind-config';
diff --git a/apps/vben5/packages/@core/ui-kit/layout-ui/tsdown.config.ts b/apps/vben5/packages/@core/ui-kit/layout-ui/tsdown.config.ts
new file mode 100644
index 000000000..43e8cf1c5
--- /dev/null
+++ b/apps/vben5/packages/@core/ui-kit/layout-ui/tsdown.config.ts
@@ -0,0 +1,21 @@
+import { defineConfig } from 'tsdown';
+import Vue from 'unplugin-vue/rolldown';
+
+export default defineConfig({
+ clean: true,
+ deps: {
+ skipNodeModulesBundle: true,
+ },
+ dts: {
+ vue: true,
+ },
+ entry: ['src/index.ts'],
+ format: ['esm'],
+ outExtensions: () => ({
+ dts: '.d.ts',
+ js: '.mjs',
+ }),
+ platform: 'neutral',
+ plugins: [Vue({ isProduction: true })],
+ unbundle: true,
+});
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/build.config.ts b/apps/vben5/packages/@core/ui-kit/menu-ui/build.config.ts
deleted file mode 100644
index 1ff78fabb..000000000
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/build.config.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { defineBuildConfig } from 'unbuild';
-
-export default defineBuildConfig({
- clean: true,
- declaration: true,
- entries: [
- {
- builder: 'mkdist',
- input: './src',
- pattern: ['**/*'],
- },
- {
- builder: 'mkdist',
- input: './src',
- loaders: ['vue'],
- pattern: ['**/*.vue'],
- },
- {
- builder: 'mkdist',
- format: 'esm',
- input: './src',
- loaders: ['js'],
- pattern: ['**/*.ts'],
- },
- ],
-});
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/package.json b/apps/vben5/packages/@core/ui-kit/menu-ui/package.json
index 9f7435200..5290fb1a8 100644
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/package.json
+++ b/apps/vben5/packages/@core/ui-kit/menu-ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@vben-core/menu-ui",
- "version": "5.6.0",
+ "version": "5.7.0",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {
@@ -11,7 +11,7 @@
"license": "MIT",
"type": "module",
"scripts": {
- "build": "pnpm unbuild",
+ "build": "pnpm exec tsdown",
"prepublishOnly": "npm run build"
},
"files": [
@@ -22,27 +22,36 @@
],
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./src/index.ts",
"development": "./src/index.ts",
+ "production": "./src/index.ts",
"default": "./dist/index.mjs"
}
},
"publishConfig": {
"exports": {
".": {
+ "types": "./dist/index.d.ts",
"default": "./dist/index.mjs"
}
}
},
"dependencies": {
"@vben-core/composables": "workspace:*",
+ "@vben-core/design": "workspace:*",
"@vben-core/icons": "workspace:*",
"@vben-core/shadcn-ui": "workspace:*",
"@vben-core/shared": "workspace:*",
"@vben-core/typings": "workspace:*",
"@vueuse/core": "catalog:",
+ "qs": "catalog:",
"vue": "catalog:"
+ },
+ "devDependencies": {
+ "@types/qs": "catalog:",
+ "unplugin-vue": "catalog:"
}
}
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/postcss.config.mjs b/apps/vben5/packages/@core/ui-kit/menu-ui/postcss.config.mjs
deleted file mode 100644
index 3d8070455..000000000
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/postcss.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@vben/tailwind-config/postcss';
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu-badge-dot.vue b/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu-badge-dot.vue
index 823cbd745..97a1ec40d 100644
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu-badge-dot.vue
+++ b/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu-badge-dot.vue
@@ -16,13 +16,11 @@ withDefaults(defineProps(), {
+ class="absolute inline-flex size-full animate-ping rounded-full opacity-75">
+ class="relative inline-flex size-1.5 rounded-full">
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu-badge.vue b/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu-badge.vue
index 9a329c50f..b822fca9d 100644
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu-badge.vue
+++ b/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu-badge.vue
@@ -49,8 +49,7 @@ const badgeStyle = computed(() => {
v-else
:class="badgeClass"
:style="badgeStyle"
- class="flex-center rounded-xl px-1.5 py-0.5 text-[10px] text-primary-foreground"
- >
+ class="flex-center rounded-xl px-1.5 py-0.5 text-[10px] text-primary-foreground">
{{ badge }}
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu-item.vue b/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu-item.vue
index 691c25cde..193456977 100644
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu-item.vue
+++ b/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu-item.vue
@@ -5,6 +5,9 @@ import { computed, onBeforeUnmount, onMounted, reactive, useSlots } from 'vue';
import { useNamespace } from '@vben-core/composables';
import { VbenIcon, VbenTooltip } from '@vben-core/shadcn-ui';
+import { isHttpUrl } from '@vben-core/shared/utils';
+
+import qs from 'qs';
import { MenuBadge } from '../components';
import { useMenu, useMenuContext, useSubMenuContext } from '../hooks';
@@ -31,8 +34,10 @@ const menuIcon = computed(() =>
active.value ? props.activeIcon || props.icon : props.icon,
);
+const isHttp = computed(() => isHttpUrl(item.parentPaths.at(-1)));
+
const isTopLevelMenuItem = computed(
- () => parentMenu.value?.type.name === 'Menu',
+ () => parentMenu.value?.type.name === 'MenuUI',
);
const collapseShowTitle = computed(
@@ -54,6 +59,7 @@ const item: MenuItemRegistered = reactive({
active,
parentPaths: parentPaths.value,
path: props.path || '',
+ query: props.query,
});
/**
@@ -81,42 +87,48 @@ onBeforeUnmount(() => {
});
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu.vue b/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu.vue
index d7894a177..3986570c4 100644
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu.vue
+++ b/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/menu.vue
@@ -37,7 +37,7 @@ import SubMenu from './sub-menu.vue';
interface Props extends MenuProps {}
-defineOptions({ name: 'Menu' });
+defineOptions({ name: 'MenuUI' });
const props = withDefaults(defineProps(), {
accordion: true,
@@ -212,15 +212,15 @@ const enableScroll = computed(
() => props.scrollToActive && props.mode === 'vertical' && !props.collapse,
);
-const { scrollToActiveItem } = useMenuScroll(activePath, {
+useMenuScroll(activePath, {
enable: enableScroll,
delay: 320,
});
// 监听 activePath 变化,自动滚动到激活项
-watch(activePath, () => {
- scrollToActiveItem();
-});
+// watch(activePath, () => {
+// scrollToActiveItem();
+// });
// 默认展开菜单
function initMenu() {
@@ -348,8 +348,7 @@ function getActivePaths() {
is('menu-align', mode === 'horizontal'),
]"
:style="menuStyle"
- role="menu"
- >
+ role="menu">
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue b/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue
index 11661386f..fa8bc1385 100644
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue
+++ b/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue
@@ -42,14 +42,12 @@ function menuIcon(menu: MenuRecordRaw) {
is(theme, true),
is('rounded', rounded),
]"
- class="relative"
- >
+ class="relative">
emit('select', menu)"
- @mouseenter="() => emit('enter', menu)"
- >
+ @mouseenter="() => emit('enter', menu)">
{{ menu.name }}
@@ -57,10 +55,10 @@ function menuIcon(menu: MenuRecordRaw) {
-
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/sub-menu-content.vue b/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/sub-menu-content.vue
index 5622d413a..1c8561ea0 100644
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/sub-menu-content.vue
+++ b/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/sub-menu-content.vue
@@ -78,16 +78,14 @@ const iconArrowStyle = computed(() => {
b(),
is('collapse-show-title', getCollapseShowTitle),
is('more', isMenuMore),
- ]"
- >
+ ]">
+ fallback />
@@ -99,7 +97,6 @@ const iconArrowStyle = computed(() => {
v-show="showArrowIcon"
:class="[e('icon-arrow')]"
:style="iconArrowStyle"
- class="size-4"
- />
+ class="size-4" />
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue b/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue
index 5c8c8a860..af956f681 100644
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue
+++ b/apps/vben5/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue
@@ -54,7 +54,7 @@ const opened = computed(() => {
return rootMenu?.openedMenus.includes(props.path);
});
const isTopLevelMenuSubmenu = computed(
- () => parentMenu.value?.type.name === 'Menu',
+ () => parentMenu.value?.type.name === 'MenuUI',
);
const mode = computed(() => rootMenu?.props.mode ?? 'vertical');
const rounded = computed(() => rootMenu?.props.rounded);
@@ -199,8 +199,7 @@ onBeforeUnmount(() => {
]"
@focus="handleMouseenter"
@mouseenter="handleMouseenter"
- @mouseleave="() => handleMouseleave()"
- >
+ @mouseleave="() => handleMouseleave()">
+ :open-delay="0">
{
:is-top-level-menu-submenu="isTopLevelMenuSubmenu"
:level="currentLevel"
:path="path"
- @click.stop="handleClick"
- >
+ @click.stop="handleClick">
@@ -235,12 +232,10 @@ onBeforeUnmount(() => {
:class="[nsMenu.is(mode, true), nsMenu.e('popup')]"
@focus="(e) => handleMouseenter(e, 100)"
@mouseenter="(e) => handleMouseenter(e, 100)"
- @mouseleave="() => handleMouseleave(true)"
- >
+ @mouseleave="() => handleMouseleave(true)">
@@ -255,8 +250,7 @@ onBeforeUnmount(() => {
:is-top-level-menu-submenu="isTopLevelMenuSubmenu"
:level="currentLevel"
:path="path"
- @click.stop="handleClick"
- >
+ @click.stop="handleClick">
@@ -266,8 +260,7 @@ onBeforeUnmount(() => {
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-context.ts b/apps/vben5/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-context.ts
index 357b296b9..1e3955dcd 100644
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-context.ts
+++ b/apps/vben5/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-context.ts
@@ -42,7 +42,7 @@ function useSubMenuContext() {
if (!instance) {
throw new Error('instance is required');
}
- const parentMenu = findComponentUpward(instance, ['Menu', 'SubMenu']);
+ const parentMenu = findComponentUpward(instance, ['MenuUI', 'SubMenu']);
const subMenu = inject(`subMenu:${parentMenu?.uid}`) as SubMenuProvider;
return subMenu;
}
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-scroll.ts b/apps/vben5/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-scroll.ts
index 270a0348d..e18bae755 100644
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-scroll.ts
+++ b/apps/vben5/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-scroll.ts
@@ -20,7 +20,7 @@ export function useMenuScroll(
if (!isEnabled) return;
const activeElement = document.querySelector(
- `aside li[role=menuitem].is-active`,
+ `aside a[role=menuitem].is-active`,
);
if (activeElement) {
activeElement.scrollIntoView({
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/src/hooks/use-menu.ts b/apps/vben5/packages/@core/ui-kit/menu-ui/src/hooks/use-menu.ts
index 9207445f9..0ee93fd9a 100644
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/src/hooks/use-menu.ts
+++ b/apps/vben5/packages/@core/ui-kit/menu-ui/src/hooks/use-menu.ts
@@ -16,7 +16,7 @@ function useMenu() {
const parentPaths = computed(() => {
let parent = instance.parent;
const paths: string[] = [instance.props.path as string];
- while (parent?.type.name !== 'Menu') {
+ while (parent?.type.name !== 'MenuUI') {
if (parent?.props.path) {
paths.unshift(parent.props.path as string);
}
@@ -27,7 +27,7 @@ function useMenu() {
});
const parentMenu = computed(() => {
- return findComponentUpward(instance, ['Menu', 'SubMenu']);
+ return findComponentUpward(instance, ['MenuUI', 'SubMenu']);
});
return {
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/src/sub-menu.vue b/apps/vben5/packages/@core/ui-kit/menu-ui/src/sub-menu.vue
index e4d471fb0..641c4ac8f 100644
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/src/sub-menu.vue
+++ b/apps/vben5/packages/@core/ui-kit/menu-ui/src/sub-menu.vue
@@ -4,7 +4,6 @@ import type { MenuRecordRaw } from '@vben-core/typings';
import { computed } from 'vue';
import { MenuBadge, MenuItem, SubMenu as SubMenuComp } from './components';
-// eslint-disable-next-line import/no-self-import
import SubMenu from './sub-menu.vue';
interface Props {
@@ -41,7 +40,7 @@ const hasChildren = computed(() => {
:badge-variants="menu.badgeVariants"
:icon="menu.icon"
:path="menu.path"
- >
+ :query="menu.query">
{{ menu.name }}
@@ -51,15 +50,13 @@ const hasChildren = computed(() => {
:key="`${menu.path}_sub`"
:active-icon="menu.activeIcon"
:icon="menu.icon"
- :path="menu.path"
- >
+ :path="menu.path">
+ class="right-6" />
{{ menu.name }}
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/src/types.ts b/apps/vben5/packages/@core/ui-kit/menu-ui/src/types.ts
index f3b9c4212..4654e2f52 100644
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/src/types.ts
+++ b/apps/vben5/packages/@core/ui-kit/menu-ui/src/types.ts
@@ -1,6 +1,10 @@
import type { Component, Ref } from 'vue';
-import type { MenuRecordBadgeRaw, ThemeModeType } from '@vben-core/typings';
+import type {
+ MenuRecordBadgeRaw,
+ Recordable,
+ ThemeModeType,
+} from '@vben-core/typings';
interface MenuProps {
/**
@@ -91,12 +95,17 @@ interface MenuItemProps extends MenuRecordBadgeRaw {
* @zh_CN menuitem 名称
*/
path: string;
+ /**
+ * @zh_CN 菜单所携带的参数
+ */
+ query?: Recordable;
}
interface MenuItemRegistered {
active: boolean;
parentPaths: string[];
path: string;
+ query?: Recordable;
}
interface MenuItemClicked {
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/tailwind.config.mjs b/apps/vben5/packages/@core/ui-kit/menu-ui/tailwind.config.mjs
deleted file mode 100644
index f17f556fa..000000000
--- a/apps/vben5/packages/@core/ui-kit/menu-ui/tailwind.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@vben/tailwind-config';
diff --git a/apps/vben5/packages/@core/ui-kit/menu-ui/tsdown.config.ts b/apps/vben5/packages/@core/ui-kit/menu-ui/tsdown.config.ts
new file mode 100644
index 000000000..43e8cf1c5
--- /dev/null
+++ b/apps/vben5/packages/@core/ui-kit/menu-ui/tsdown.config.ts
@@ -0,0 +1,21 @@
+import { defineConfig } from 'tsdown';
+import Vue from 'unplugin-vue/rolldown';
+
+export default defineConfig({
+ clean: true,
+ deps: {
+ skipNodeModulesBundle: true,
+ },
+ dts: {
+ vue: true,
+ },
+ entry: ['src/index.ts'],
+ format: ['esm'],
+ outExtensions: () => ({
+ dts: '.d.ts',
+ js: '.mjs',
+ }),
+ platform: 'neutral',
+ plugins: [Vue({ isProduction: true })],
+ unbundle: true,
+});
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/build.config.ts b/apps/vben5/packages/@core/ui-kit/popup-ui/build.config.ts
deleted file mode 100644
index 18eaa604c..000000000
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/build.config.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { defineBuildConfig } from 'unbuild';
-
-export default defineBuildConfig({
- clean: true,
- declaration: true,
- entries: [
- {
- builder: 'mkdist',
- input: './src',
- loaders: ['vue'],
- pattern: ['**/*.vue'],
- },
- {
- builder: 'mkdist',
- format: 'esm',
- input: './src',
- loaders: ['js'],
- pattern: ['**/*.ts'],
- },
- ],
-});
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/package.json b/apps/vben5/packages/@core/ui-kit/popup-ui/package.json
index 338da305f..0114a01be 100644
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/package.json
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@vben-core/popup-ui",
- "version": "5.6.0",
+ "version": "5.7.0",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {
@@ -11,7 +11,7 @@
"license": "MIT",
"type": "module",
"scripts": {
- "build": "pnpm unbuild",
+ "build": "pnpm exec tsdown",
"prepublishOnly": "npm run build"
},
"files": [
@@ -22,16 +22,19 @@
],
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./src/index.ts",
"development": "./src/index.ts",
+ "production": "./src/index.ts",
"default": "./dist/index.mjs"
}
},
"publishConfig": {
"exports": {
".": {
+ "types": "./dist/index.d.ts",
"default": "./dist/index.mjs"
}
}
@@ -39,10 +42,14 @@
"dependencies": {
"@vben-core/composables": "workspace:*",
"@vben-core/icons": "workspace:*",
+ "@vben-core/preferences": "workspace:*",
"@vben-core/shadcn-ui": "workspace:*",
"@vben-core/shared": "workspace:*",
"@vben-core/typings": "workspace:*",
"@vueuse/core": "catalog:",
"vue": "catalog:"
+ },
+ "devDependencies": {
+ "unplugin-vue": "catalog:"
}
}
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/postcss.config.mjs b/apps/vben5/packages/@core/ui-kit/popup-ui/postcss.config.mjs
deleted file mode 100644
index 3d8070455..000000000
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/postcss.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@vben/tailwind-config/postcss';
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/src/alert/alert.ts b/apps/vben5/packages/@core/ui-kit/popup-ui/src/alert/alert.ts
index 5a214fa2d..ff19830e1 100644
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/src/alert/alert.ts
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/src/alert/alert.ts
@@ -36,6 +36,8 @@ export type AlertProps = {
contentClass?: string;
/** 执行beforeClose回调期间,在内容区域显示一个loading遮罩*/
contentMasking?: boolean;
+ /** 按下Esc时是否关闭弹窗 */
+ escapeKeyClose?: boolean;
/** 弹窗底部内容(与按钮在同一个容器中) */
footer?: Component | string;
/** 弹窗的图标(在标题的前面) */
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/src/alert/alert.vue b/apps/vben5/packages/@core/ui-kit/popup-ui/src/alert/alert.vue
index 6b52a1121..253a8ab99 100644
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/src/alert/alert.vue
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/src/alert/alert.vue
@@ -14,6 +14,7 @@ import {
Info,
X,
} from '@vben-core/icons';
+import { usePreferences } from '@vben-core/preferences';
import {
AlertDialog,
AlertDialogAction,
@@ -34,8 +35,10 @@ const props = withDefaults(defineProps(), {
bordered: true,
buttonAlign: 'end',
centered: true,
+ escapeKeyClose: true,
});
const emits = defineEmits(['closed', 'confirm', 'opened']);
+const { globalEscapeShortcutKey } = usePreferences();
const open = defineModel('open', { default: false });
const { $t } = useSimpleLocale();
const components = globalShareState.getComponents();
@@ -46,8 +49,14 @@ function onAlertClosed() {
isConfirm.value = false;
}
-function onEscapeKeyDown() {
+function onEscapeKeyDown(e: KeyboardEvent) {
+ // 先标记是按 Esc 触发的(用于后续 isConfirm 判断等)
isConfirm.value = false;
+
+ // 只有当组件参数和全局配置都为false时才阻止关闭,其任意一个为true都需要让esc生效
+ if (!props.escapeKeyClose && !globalEscapeShortcutKey.value) {
+ e.preventDefault();
+ }
}
const getIconRender = computed(() => {
@@ -136,25 +145,25 @@ async function handleOpenChange(val: boolean) {
}
-
+
+ ">
@@ -166,31 +175,28 @@ async function handleOpenChange(val: boolean) {
size="icon"
class="rounded-full"
:disabled="loading"
- @click="handleCancel"
- >
+ @click="handleCancel">
-
+
+ :class="`justify-${buttonAlign}`">
+ variant="outline"
+ @click="handleCancel">
{{ cancelText || $t('cancel') }}
@@ -198,8 +204,7 @@ async function handleOpenChange(val: boolean) {
+ @click="handleConfirm">
{{ confirmText || $t('confirm') }}
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/__tests__/drawer-api.test.ts b/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/__tests__/drawer-api.test.ts
index 365a2e4a0..8420bb9cb 100644
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/__tests__/drawer-api.test.ts
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/__tests__/drawer-api.test.ts
@@ -13,21 +13,20 @@ vi.mock('@vben-core/shared/store', () => {
return this._state;
}
private _state: DrawerState;
+ private subscribers: Array<(state: DrawerState) => void> = [];
- private options: any;
-
- constructor(initialState: DrawerState, options: any) {
+ constructor(initialState: DrawerState) {
this._state = initialState;
- this.options = options;
- }
-
- batch(cb: () => void) {
- cb();
}
setState(fn: (prev: DrawerState) => DrawerState) {
this._state = fn(this._state);
- this.options.onUpdate();
+ this.subscribers.forEach((sub) => sub(this._state));
+ }
+
+ subscribe(fn: (state: DrawerState) => void) {
+ this.subscribers.push(fn);
+ return { unsubscribe: () => {} };
}
},
};
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts b/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts
index a4a3ac4aa..f5d9f3365 100644
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts
@@ -56,23 +56,18 @@ export class DrawerApi {
title: '',
};
- this.store = new Store
(
- {
- ...defaultState,
- ...storeState,
- },
- {
- onUpdate: () => {
- const state = this.store.state;
- if (state?.isOpen === this.state?.isOpen) {
- this.state = state;
- } else {
- this.state = state;
- this.api.onOpenChange?.(!!state?.isOpen);
- }
- },
- },
- );
+ this.store = new Store({
+ ...defaultState,
+ ...storeState,
+ });
+
+ this.store.subscribe((state) => {
+ const prevIsOpen = this.state?.isOpen;
+ this.state = state;
+ if (state?.isOpen !== prevIsOpen) {
+ this.api.onOpenChange?.(!!state?.isOpen);
+ }
+ });
this.state = this.store.state;
this.api = {
onBeforeClose,
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts b/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts
index 89cd3b5f8..1b2ba4e39 100644
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts
@@ -75,7 +75,7 @@ export interface DrawerProps {
*/
headerClass?: ClassType;
/**
- * 弹窗是否显示
+ * 抽屉加载状态
* @default false
*/
loading?: boolean;
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue b/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue
index ceca51e77..d76933e98 100644
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue
@@ -180,16 +180,20 @@ const getForceMount = computed(() => {
drawerApi?.close()"
- >
+ @update:open="() => drawerApi?.close()">
{
@interact-outside="interactOutside"
@open-auto-focus="handerOpenAutoFocus"
@opened="() => drawerApi?.onOpened()"
- @pointer-down-outside="pointerDownOutside"
- >
+ @pointer-down-outside="pointerDownOutside">
+ ">
+ class="ml-0.5 cursor-pointer rounded-full opacity-80 transition-opacity hover:opacity-100 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-secondary">
@@ -234,10 +235,9 @@ const getForceMount = computed(() => {
+ orientation="vertical" />
{{ title }}
@@ -265,8 +265,7 @@ const getForceMount = computed(() => {
v-if="closable && closeIconPlacement === 'right'"
as-child
:disabled="submitting"
- class="ml-[2px] cursor-pointer rounded-full opacity-80 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none data-[state=open]:bg-secondary"
- >
+ class="ml-0.5 cursor-pointer rounded-full opacity-80 transition-opacity hover:opacity-100 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-secondary">
@@ -287,8 +286,7 @@ const getForceMount = computed(() => {
cn('relative flex-1 overflow-y-auto p-3', contentClass, {
'pointer-events-none': showLoading || submitting,
})
- "
- >
+ ">
@@ -299,8 +297,7 @@ const getForceMount = computed(() => {
'w-full flex-row items-center justify-end border-t p-2 px-3',
footerClass,
)
- "
- >
+ ">
{
v-if="showCancelButton"
variant="ghost"
:disabled="submitting"
- @click="() => drawerApi?.onCancel()"
- >
+ @click="() => drawerApi?.onCancel()">
{{ cancelText || $t('cancel') }}
@@ -319,8 +315,7 @@ const getForceMount = computed(() => {
:is="components.PrimaryButton || VbenButton"
v-if="showConfirmButton"
:loading="confirmLoading || submitting"
- @click="() => drawerApi?.onConfirm()"
- >
+ @click="() => drawerApi?.onConfirm()">
{{ confirmText || $t('confirm') }}
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/use-drawer.ts b/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/use-drawer.ts
index 86555e3a1..46ae4fd58 100644
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/use-drawer.ts
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/src/drawer/use-drawer.ts
@@ -14,13 +14,19 @@ import {
ref,
} from 'vue';
-import { useStore } from '@vben-core/shared/store';
+import { usePreferences } from '@vben-core/preferences';
+import { useSelector } from '@vben-core/shared/store';
import { DrawerApi } from './drawer-api';
import VbenDrawer from './drawer.vue';
const USER_DRAWER_INJECT_KEY = Symbol('VBEN_DRAWER_INJECT');
+const { globalEscapeShortcutKey } = usePreferences();
+
+/**
+ * 默认配置
+ */
const DEFAULT_DRAWER_PROPS: Partial = {};
export function setDefaultDrawerProps(props: Partial) {
@@ -33,6 +39,10 @@ export function useVbenDrawer<
// Drawer一般会抽离出来,所以如果有传入 connectedComponent,则表示为外部调用,与内部组件进行连接
// 外部的Drawer通过provide/inject传递api
+ const defaultOptions = {
+ closeOnPressEscape: globalEscapeShortcutKey.value, // 全局Esc快捷键配置
+ ...options,
+ };
const { connectedComponent } = options;
if (connectedComponent) {
const extendedApi = reactive({});
@@ -45,7 +55,7 @@ export function useVbenDrawer<
// 不能用 Object.assign,会丢失 api 的原型函数
Object.setPrototypeOf(extendedApi, api);
},
- options,
+ options: defaultOptions,
async reCreateDrawer() {
isDrawerReady.value = false;
await nextTick();
@@ -79,7 +89,7 @@ export function useVbenDrawer<
const mergedOptions = {
...DEFAULT_DRAWER_PROPS,
...injectData.options,
- ...options,
+ ...defaultOptions,
} as DrawerApiOptions;
mergedOptions.onOpenChange = (isOpen: boolean) => {
@@ -99,7 +109,7 @@ export function useVbenDrawer<
const extendedApi: ExtendedDrawerApi = api as never;
extendedApi.useStore = (selector) => {
- return useStore(api.store, selector);
+ return useSelector(api.store, selector);
};
const Drawer = defineComponent(
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/__tests__/modal-api.test.ts b/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/__tests__/modal-api.test.ts
index 1edd477e7..5067f5fa5 100644
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/__tests__/modal-api.test.ts
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/__tests__/modal-api.test.ts
@@ -12,21 +12,20 @@ vi.mock('@vben-core/shared/store', () => {
return this._state;
}
private _state: ModalState;
+ private subscribers: Array<(state: ModalState) => void> = [];
- private options: any;
-
- constructor(initialState: ModalState, options: any) {
+ constructor(initialState: ModalState) {
this._state = initialState;
- this.options = options;
- }
-
- batch(cb: () => void) {
- cb();
}
setState(fn: (prev: ModalState) => ModalState) {
this._state = fn(this._state);
- this.options.onUpdate();
+ this.subscribers.forEach((sub) => sub(this._state));
+ }
+
+ subscribe(fn: (state: ModalState) => void) {
+ this.subscribers.push(fn);
+ return { unsubscribe: () => {} };
}
},
};
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/modal-api.ts b/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/modal-api.ts
index f60870b3f..9bfda4e90 100644
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/modal-api.ts
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/modal-api.ts
@@ -46,6 +46,7 @@ export class ModalApi {
contentClass: '',
destroyOnClose: true,
draggable: false,
+ overflow: false,
footer: true,
footerClass: '',
fullscreen: false,
@@ -62,25 +63,19 @@ export class ModalApi {
animationType: 'slide',
};
- this.store = new Store(
- {
- ...defaultState,
- ...storeState,
- },
- {
- onUpdate: () => {
- const state = this.store.state;
-
- // 每次更新状态时,都会调用 onOpenChange 回调函数
- if (state?.isOpen === this.state?.isOpen) {
- this.state = state;
- } else {
- this.state = state;
- this.api.onOpenChange?.(!!state?.isOpen);
- }
- },
- },
- );
+ this.store = new Store({
+ ...defaultState,
+ ...storeState,
+ });
+
+ this.store.subscribe((state) => {
+ // 每次更新状态时,都会调用 onOpenChange 回调函数
+ const prevIsOpen = this.state?.isOpen;
+ this.state = state;
+ if (state?.isOpen !== prevIsOpen) {
+ this.api.onOpenChange?.(!!state?.isOpen);
+ }
+ });
this.state = this.store.state;
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/modal.ts b/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/modal.ts
index fa41344ee..5a7a50286 100644
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/modal.ts
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/modal.ts
@@ -1,6 +1,6 @@
import type { Component, Ref } from 'vue';
-import type { MaybePromise } from '@vben-core/typings';
+import type { ClassType, MaybePromise } from '@vben-core/typings';
import type { ModalApi } from './modal-api';
@@ -30,7 +30,7 @@ export interface ModalProps {
*/
centered?: boolean;
- class?: string;
+ class?: ClassType;
/**
* 是否显示右上角的关闭按钮
@@ -60,7 +60,7 @@ export interface ModalProps {
* 确定按钮文字
*/
confirmText?: string;
- contentClass?: string;
+ contentClass?: ClassType;
/**
* 弹窗描述
*/
@@ -79,7 +79,7 @@ export interface ModalProps {
* @default true
*/
footer?: boolean;
- footerClass?: string;
+ footerClass?: ClassType;
/**
* 是否全屏
* @default false
@@ -95,9 +95,9 @@ export interface ModalProps {
* @default true
*/
header?: boolean;
- headerClass?: string;
+ headerClass?: ClassType;
/**
- * 弹窗是否显示
+ * 弹窗加载状态
* @default false
*/
loading?: boolean;
@@ -110,6 +110,11 @@ export interface ModalProps {
* 是否自动聚焦
*/
openAutoFocus?: boolean;
+ /**
+ * 拖动范围是否可以超出可视区
+ * @default false
+ */
+ overflow?: boolean;
/**
* 弹窗遮罩模糊效果
*/
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/modal.vue b/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/modal.vue
index e93e4c12d..e36d9f15b 100644
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/modal.vue
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/modal.vue
@@ -12,11 +12,7 @@ import {
watch,
} from 'vue';
-import {
- useIsMobile,
- usePriorityValues,
- useSimpleLocale,
-} from '@vben-core/composables';
+import { usePriorityValues, useSimpleLocale } from '@vben-core/composables';
import { Expand, Shrink } from '@vben-core/icons';
import {
Dialog,
@@ -57,14 +53,13 @@ const headerRef = ref();
// @ts-expect-error unused
const footerRef = ref();
-const id = useId();
-
-provide('DISMISSABLE_MODAL_ID', id);
-
const { $t } = useSimpleLocale();
-const { isMobile } = useIsMobile();
const state = props.modalApi?.useStore?.();
+const id = useId();
+// 遮罩层通过该 id 标记,仅当点击发生在当前 Modal 的遮罩上时才允许关闭
+provide('DISMISSABLE_MODAL_ID', id);
+
const {
appendToMain,
bordered,
@@ -81,6 +76,7 @@ const {
description,
destroyOnClose,
draggable,
+ overflow,
footer: showFooter,
footerClass,
fullscreen,
@@ -100,7 +96,7 @@ const {
zIndex,
} = usePriorityValues(props, state);
-const shouldFullscreen = computed(() => fullscreen.value || isMobile.value);
+const shouldFullscreen = computed(() => fullscreen.value);
const shouldDraggable = computed(
() => draggable.value && !shouldFullscreen.value && header.value,
@@ -122,6 +118,7 @@ const { dragging, transform } = useModalDraggable(
shouldDraggable,
getAppendTo,
shouldCentered,
+ overflow,
);
const firstOpened = ref(false);
@@ -214,6 +211,10 @@ function handleFocusOutside(e: Event) {
e.stopPropagation();
}
+function handleCloseAutoFocus(_e: Event) {
+ // allow reka-ui to return focus to the trigger element on close
+}
+
const getForceMount = computed(() => {
return !unref(destroyOnClose) && unref(firstOpened);
});
@@ -233,36 +234,37 @@ function handleClosed() {
+ class="absolute top-3 right-10 flex-center size-6 rounded-full px-1 text-lg text-foreground/80 opacity-70 transition-opacity hover:bg-accent hover:text-accent-foreground hover:opacity-100 focus:outline-hidden disabled:pointer-events-none"
+ @click="handleFullscreen">
@@ -338,17 +336,15 @@ function handleClosed() {
},
footerClass,
)
- "
- >
+ ">
modalApi?.onCancel()"
- >
+ @click="() => modalApi?.onCancel()">
{{ cancelText || $t('cancel') }}
@@ -359,8 +355,7 @@ function handleClosed() {
v-if="showConfirmButton"
:disabled="confirmDisabled"
:loading="confirmLoading || submitting"
- @click="() => modalApi?.onConfirm()"
- >
+ @click="() => modalApi?.onConfirm()">
{{ confirmText || $t('confirm') }}
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/use-modal-draggable.ts b/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/use-modal-draggable.ts
index 84910b744..a7bcba555 100644
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/use-modal-draggable.ts
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/use-modal-draggable.ts
@@ -15,6 +15,7 @@ export function useModalDraggable(
draggable: ComputedRef,
containerSelector?: ComputedRef,
centered?: ComputedRef,
+ overflow?: ComputedRef,
) {
const transform = reactive({
offsetX: 0,
@@ -67,8 +68,10 @@ export function useModalDraggable(
let moveX = offsetX + e.clientX - downX;
let moveY = offsetY + e.clientY - downY;
- moveX = Math.min(Math.max(moveX, minLeft), maxLeft);
- moveY = Math.min(Math.max(moveY, minTop), maxTop);
+ if (!overflow?.value) {
+ moveX = Math.min(Math.max(moveX, minLeft), maxLeft);
+ moveY = Math.min(Math.max(moveY, minTop), maxTop);
+ }
transform.offsetX = moveX;
transform.offsetY = moveY;
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/use-modal.ts b/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/use-modal.ts
index 2decdffdf..87456a16d 100644
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/use-modal.ts
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/src/modal/use-modal.ts
@@ -10,13 +10,18 @@ import {
ref,
} from 'vue';
-import { useStore } from '@vben-core/shared/store';
+import { usePreferences } from '@vben-core/preferences';
+import { useSelector } from '@vben-core/shared/store';
import { ModalApi } from './modal-api';
import VbenModal from './modal.vue';
const USER_MODAL_INJECT_KEY = Symbol('VBEN_MODAL_INJECT');
+const { globalEscapeShortcutKey } = usePreferences();
+/**
+ * 默认配置
+ */
const DEFAULT_MODAL_PROPS: Partial = {};
export function setDefaultModalProps(props: Partial) {
@@ -29,6 +34,10 @@ export function useVbenModal(
// Modal一般会抽离出来,所以如果有传入 connectedComponent,则表示为外部调用,与内部组件进行连接
// 外部的Modal通过provide/inject传递api
+ const defaultOptions = {
+ closeOnPressEscape: globalEscapeShortcutKey.value, // 全局Esc快捷键配置
+ ...options,
+ };
const { connectedComponent } = options;
if (connectedComponent) {
const extendedApi = reactive({});
@@ -42,7 +51,7 @@ export function useVbenModal(
Object.setPrototypeOf(extendedApi, api);
},
consumed: false,
- options,
+ options: defaultOptions,
async reCreateModal() {
isModalReady.value = false;
await nextTick();
@@ -85,7 +94,7 @@ export function useVbenModal(
const mergedOptions = {
...DEFAULT_MODAL_PROPS,
...injectData.options,
- ...options,
+ ...defaultOptions,
} as ModalApiOptions;
mergedOptions.onOpenChange = (isOpen: boolean) => {
@@ -107,7 +116,7 @@ export function useVbenModal(
const extendedApi: ExtendedModalApi = api as never;
extendedApi.useStore = (selector) => {
- return useStore(api.store, selector);
+ return useSelector(api.store, selector);
};
const Modal = defineComponent(
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/tailwind.config.mjs b/apps/vben5/packages/@core/ui-kit/popup-ui/tailwind.config.mjs
deleted file mode 100644
index f17f556fa..000000000
--- a/apps/vben5/packages/@core/ui-kit/popup-ui/tailwind.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@vben/tailwind-config';
diff --git a/apps/vben5/packages/@core/ui-kit/popup-ui/tsdown.config.ts b/apps/vben5/packages/@core/ui-kit/popup-ui/tsdown.config.ts
new file mode 100644
index 000000000..43e8cf1c5
--- /dev/null
+++ b/apps/vben5/packages/@core/ui-kit/popup-ui/tsdown.config.ts
@@ -0,0 +1,21 @@
+import { defineConfig } from 'tsdown';
+import Vue from 'unplugin-vue/rolldown';
+
+export default defineConfig({
+ clean: true,
+ deps: {
+ skipNodeModulesBundle: true,
+ },
+ dts: {
+ vue: true,
+ },
+ entry: ['src/index.ts'],
+ format: ['esm'],
+ outExtensions: () => ({
+ dts: '.d.ts',
+ js: '.mjs',
+ }),
+ platform: 'neutral',
+ plugins: [Vue({ isProduction: true })],
+ unbundle: true,
+});
diff --git a/apps/vben5/packages/@core/ui-kit/shadcn-ui/build.config.ts b/apps/vben5/packages/@core/ui-kit/shadcn-ui/build.config.ts
deleted file mode 100644
index aec93408a..000000000
--- a/apps/vben5/packages/@core/ui-kit/shadcn-ui/build.config.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { defineBuildConfig } from 'unbuild';
-
-export default defineBuildConfig({
- clean: true,
- declaration: true,
- entries: [
- {
- builder: 'mkdist',
- input: './src',
-
- pattern: ['**/*'],
- },
- {
- builder: 'mkdist',
- input: './src',
- loaders: ['vue'],
- pattern: ['**/*.vue'],
- },
- {
- builder: 'mkdist',
- format: 'esm',
- input: './src',
- loaders: ['js'],
- pattern: ['**/*.ts'],
- },
- ],
-});
diff --git a/apps/vben5/packages/@core/ui-kit/shadcn-ui/components.json b/apps/vben5/packages/@core/ui-kit/shadcn-ui/components.json
index b497f43c3..dcb1940fb 100644
--- a/apps/vben5/packages/@core/ui-kit/shadcn-ui/components.json
+++ b/apps/vben5/packages/@core/ui-kit/shadcn-ui/components.json
@@ -3,12 +3,11 @@
"style": "new-york",
"typescript": true,
"tailwind": {
- "config": "tailwind.config.mjs",
+ "config": "",
"css": "src/assets/index.css",
"baseColor": "slate",
"cssVariables": true
},
- "framework": "vite",
"aliases": {
"components": "@vben-core/shadcn-ui/components",
"utils": "@vben-core/shared/utils"
diff --git a/apps/vben5/packages/@core/ui-kit/shadcn-ui/package.json b/apps/vben5/packages/@core/ui-kit/shadcn-ui/package.json
index dacb41abf..49513aa34 100644
--- a/apps/vben5/packages/@core/ui-kit/shadcn-ui/package.json
+++ b/apps/vben5/packages/@core/ui-kit/shadcn-ui/package.json
@@ -1,8 +1,6 @@
{
"name": "@vben-core/shadcn-ui",
- "version": "5.6.0",
- "#main": "./dist/index.mjs",
- "#module": "./dist/index.mjs",
+ "version": "5.7.0",
"homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": {
@@ -12,41 +10,41 @@
},
"license": "MIT",
"type": "module",
- "scripts": {
- "#build": "pnpm unbuild",
- "#prepublishOnly": "npm run build"
- },
"files": [
- "dist"
+ "components.json",
+ "src"
],
"sideEffects": [
"**/*.css"
],
"main": "./src/index.ts",
"module": "./src/index.ts",
+ "types": "./src/index.ts",
"exports": {
".": {
"types": "./src/index.ts",
"development": "./src/index.ts",
- "default": "./src/index.ts",
- "//default": "./dist/index.mjs"
+ "default": "./src/index.ts"
}
},
"publishConfig": {
"exports": {
".": {
+ "types": "./src/index.ts",
+ "development": "./src/index.ts",
"default": "./src/index.ts"
}
}
},
"dependencies": {
+ "@lucide/vue": "catalog:",
"@vben-core/composables": "workspace:*",
+ "@vben-core/design": "workspace:*",
"@vben-core/icons": "workspace:*",
"@vben-core/shared": "workspace:*",
"@vben-core/typings": "workspace:*",
"@vueuse/core": "catalog:",
"class-variance-authority": "catalog:",
- "lucide-vue-next": "catalog:",
"reka-ui": "catalog:",
"vee-validate": "catalog:",
"vue": "catalog:"
diff --git a/apps/vben5/packages/@core/ui-kit/shadcn-ui/postcss.config.mjs b/apps/vben5/packages/@core/ui-kit/shadcn-ui/postcss.config.mjs
deleted file mode 100644
index 3d8070455..000000000
--- a/apps/vben5/packages/@core/ui-kit/shadcn-ui/postcss.config.mjs
+++ /dev/null
@@ -1 +0,0 @@
-export { default } from '@vben/tailwind-config/postcss';
diff --git a/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/assets/index.css b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/assets/index.css
new file mode 100644
index 000000000..cbd9be68b
--- /dev/null
+++ b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/assets/index.css
@@ -0,0 +1 @@
+@reference "@vben/tailwind-config/theme";
diff --git a/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/avatar/avatar.vue b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/avatar/avatar.vue
index 2b2ed2f2f..651376df7 100644
--- a/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/avatar/avatar.vue
+++ b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/avatar/avatar.vue
@@ -60,8 +60,7 @@ const rootStyle = computed(() => {
+ class="relative flex shrink-0 items-center">
{{ text }}
@@ -69,8 +68,7 @@ const rootStyle = computed(() => {
+ class="border-background absolute right-0 bottom-0 size-3 rounded-full border-2">
diff --git a/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/back-top/back-top.vue b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/back-top/back-top.vue
index 8da6cde42..2035952de 100644
--- a/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/back-top/back-top.vue
+++ b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/back-top/back-top.vue
@@ -32,11 +32,10 @@ const { handleClick, visible } = useBackTop(props);
+ variant="ghost"
+ @click="handleClick">
diff --git a/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb-background.vue b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb-background.vue
index 5659ec8f3..f161b0393 100644
--- a/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb-background.vue
+++ b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb-background.vue
@@ -22,22 +22,19 @@ function handleClick(index: number, path?: string) {
+ :key="`${item.path}-${item.title}-${index}`">
+ @click.stop="handleClick(index, item.path)">
+ class="mr-1 size-4 shrink-0" />
{{ item.title }}
@@ -50,12 +47,14 @@ function handleClick(index: number, path?: string) {
diff --git a/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible.vue b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible.vue
new file mode 100644
index 000000000..237eb5b1d
--- /dev/null
+++ b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/index.ts b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/index.ts
new file mode 100644
index 000000000..8563a7e59
--- /dev/null
+++ b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/index.ts
@@ -0,0 +1,4 @@
+export { default as VbenCollapsibleParams } from './collapsible-params.vue';
+export { default as VbenCollapsible } from './collapsible.vue';
+
+export * from './type';
diff --git a/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/type.ts b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/type.ts
new file mode 100644
index 000000000..f2fcffdf3
--- /dev/null
+++ b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/type.ts
@@ -0,0 +1,22 @@
+export interface CollapsibleParamsProps {
+ defaultOpen?: boolean;
+ maxHeight?: number | string;
+ params: CollapsibleParamSchema[];
+ visibleCount?: number;
+}
+
+export interface CollapsibleParamOption {
+ [key: string]: any;
+ max?: number;
+ min?: number;
+ precision?: number;
+ step?: number;
+ type?: 'exponential' | 'number' | 'select' | 'string';
+}
+
+export interface CollapsibleParamSchema {
+ defaultValue?: number | number[] | string | string[];
+ description: string;
+ key: string;
+ option: CollapsibleParamOption;
+}
diff --git a/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/context-menu.vue b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/context-menu.vue
index 009f267ef..d44c5bb66 100644
--- a/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/context-menu.vue
+++ b/apps/vben5/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/context-menu.vue
@@ -9,7 +9,7 @@ import type { ClassType } from '@vben-core/typings';
import type { IContextMenuItem } from './interface';
-import { computed } from 'vue';
+import { computed, onMounted, onUnmounted, ref } from 'vue';
import { useForwardPropsEmits } from 'reka-ui';
@@ -35,6 +35,14 @@ const props = defineProps<
const emits = defineEmits();
+const NATIVE_CONTEXT_SELECTORS = [
+ 'input',
+ 'textarea',
+ 'select',
+ '[contenteditable]:not([contenteditable="false"])',
+ '.allow-native-context',
+].join(', ');
+
const delegatedProps = computed(() => {
const {
class: _cls,
@@ -59,18 +67,39 @@ function handleClick(menu: IContextMenuItem) {
}
menu?.handler?.(props.handlerData);
}
+
+const triggerRef = ref(null);
+
+function onContextMenuCapture(e: MouseEvent) {
+ if ((e.target as HTMLElement).closest(NATIVE_CONTEXT_SELECTORS)) {
+ e.stopPropagation();
+ }
+}
+
+onMounted(() => {
+ triggerRef.value?.addEventListener('contextmenu', onContextMenuCapture, {
+ capture: true,
+ });
+});
+
+onUnmounted(() => {
+ triggerRef.value?.removeEventListener('contextmenu', onContextMenuCapture, {
+ capture: true,
+ });
+});
-
+
+
+