mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-26 00:26:20 +08:00
Compare commits
106 Commits
electron-v
...
v5.5.7
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b9aef618fe | ||
![]() |
4102cc2211 | ||
![]() |
ea776aa710 | ||
![]() |
feb96dc8ea | ||
![]() |
470fd43b49 | ||
![]() |
76d106e474 | ||
![]() |
78c3c9da6f | ||
![]() |
081d08a7f8 | ||
![]() |
96a10ca83f | ||
![]() |
f31360ba4e | ||
![]() |
4eb16d6d3a | ||
![]() |
53304514b6 | ||
![]() |
6fbf1387f5 | ||
![]() |
e5c937396d | ||
![]() |
af186f878d | ||
![]() |
97894a940e | ||
![]() |
48d70182b4 | ||
![]() |
a1091bad46 | ||
![]() |
9f9be21e2a | ||
![]() |
a2bdcd6e49 | ||
![]() |
11b2b5bcc2 | ||
![]() |
ebef2c91e2 | ||
![]() |
0c3edb10b0 | ||
![]() |
8ac97688da | ||
![]() |
2efacb3e5b | ||
![]() |
dae46abb71 | ||
![]() |
5ee2a74e2d | ||
![]() |
d0b8349a2d | ||
![]() |
34c4ecb047 | ||
![]() |
3d9dba965f | ||
![]() |
024c01d350 | ||
![]() |
2adb8acd80 | ||
![]() |
a23bc4cb5c | ||
![]() |
cf17a45d8d | ||
![]() |
b46ebe756e | ||
![]() |
e89cf400c0 | ||
![]() |
9e67929ee7 | ||
![]() |
90625782c0 | ||
![]() |
84ef207d9c | ||
![]() |
e68fff58e8 | ||
![]() |
bf70539221 | ||
![]() |
5949c73a30 | ||
![]() |
cc6c9bf7a0 | ||
![]() |
6b1aab9c67 | ||
![]() |
8f4d3d418d | ||
![]() |
3b3f8e4e44 | ||
![]() |
f94ca10adf | ||
![]() |
4471bc7a5d | ||
![]() |
5689ac60ff | ||
![]() |
045bc4e5ee | ||
![]() |
17a18fc9ba | ||
![]() |
41152d1722 | ||
![]() |
f1af9f8f6e | ||
![]() |
0517a7014f | ||
![]() |
3e6d608a2f | ||
![]() |
5de954baa4 | ||
![]() |
add1e61b6f | ||
![]() |
20c15f352f | ||
![]() |
8aa7dabeff | ||
![]() |
78c7c1589a | ||
![]() |
dd833ca56b | ||
![]() |
681c1dc267 | ||
![]() |
4545422ee0 | ||
![]() |
ca94ca906f | ||
![]() |
76de450c71 | ||
![]() |
dd2b1ed580 | ||
![]() |
baec89f896 | ||
![]() |
7c7051a11e | ||
![]() |
aa27a2f7a1 | ||
![]() |
9ee6d06d50 | ||
![]() |
0cc1cb5a7b | ||
![]() |
0a9fc4e02d | ||
![]() |
be840460d8 | ||
![]() |
cb45987fe2 | ||
![]() |
5ffd7db8e0 | ||
![]() |
14377705e7 | ||
![]() |
b985ff0584 | ||
![]() |
b148b8ec92 | ||
![]() |
79de6bcbf7 | ||
![]() |
14bd6dd25d | ||
![]() |
7f269e0d69 | ||
![]() |
4baec83db5 | ||
![]() |
f7a4d13a4c | ||
![]() |
0936861da1 | ||
![]() |
3318d76bab | ||
![]() |
8f3881eabf | ||
![]() |
5252480b09 | ||
![]() |
d18f56177c | ||
![]() |
333998b518 | ||
![]() |
3fb4fba1cb | ||
![]() |
c7e6210c8d | ||
![]() |
d864085c13 | ||
![]() |
fcdc1a1602 | ||
![]() |
bf7496f0d5 | ||
![]() |
9700150653 | ||
![]() |
f0e9e55af2 | ||
![]() |
ff88274554 | ||
![]() |
afce9dc5c0 | ||
![]() |
b5700bd0b1 | ||
![]() |
a8c4786311 | ||
![]() |
2971ccc0b7 | ||
![]() |
4a2c7b313f | ||
![]() |
36bf6fc149 | ||
![]() |
f46ec30995 | ||
![]() |
9bd5a190c2 | ||
![]() |
86da3cedc2 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,7 +5,6 @@ dist-ssr
|
||||
dist.zip
|
||||
dist.tar
|
||||
dist.war
|
||||
dist-electron
|
||||
.nitro
|
||||
.output
|
||||
*-dist.zip
|
||||
|
3
.npmrc
3
.npmrc
@@ -1,8 +1,5 @@
|
||||
registry = "https://registry.npmmirror.com"
|
||||
ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
|
||||
public-hoist-pattern[]=lefthook
|
||||
ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
|
||||
public-hoist-pattern[]=husky
|
||||
public-hoist-pattern[]=eslint
|
||||
public-hoist-pattern[]=prettier
|
||||
public-hoist-pattern[]=prettier-plugin-tailwindcss
|
||||
|
28
apps/backend-mock/api/demo/bigint.ts
Normal file
28
apps/backend-mock/api/demo/bigint.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export default eventHandler(async (event) => {
|
||||
const userinfo = verifyAccessToken(event);
|
||||
if (!userinfo) {
|
||||
return unAuthorizedResponse(event);
|
||||
}
|
||||
const data = `
|
||||
{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": [
|
||||
{
|
||||
"id": 123456789012345678901234567890123456789012345678901234567890,
|
||||
"name": "John Doe",
|
||||
"age": 30,
|
||||
"email": "john-doe@demo.com"
|
||||
},
|
||||
{
|
||||
"id": 987654321098765432109876543210987654321098765432109876543210,
|
||||
"name": "Jane Smith",
|
||||
"age": 25,
|
||||
"email": "jane@demo.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
setHeader(event, 'Content-Type', 'application/json');
|
||||
return data;
|
||||
});
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/web-antd",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://vben.pro",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -8,40 +8,42 @@ import type { ComponentType } from './component';
|
||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
setupVbenForm<ComponentType>({
|
||||
config: {
|
||||
// ant design vue组件库默认都是 v-model:value
|
||||
baseModelPropName: 'value',
|
||||
async function initSetupVbenForm() {
|
||||
setupVbenForm<ComponentType>({
|
||||
config: {
|
||||
// ant design vue组件库默认都是 v-model:value
|
||||
baseModelPropName: 'value',
|
||||
|
||||
// 一些组件是 v-model:checked 或者 v-model:fileList
|
||||
modelPropNameMap: {
|
||||
Checkbox: 'checked',
|
||||
Radio: 'checked',
|
||||
Switch: 'checked',
|
||||
Upload: 'fileList',
|
||||
// 一些组件是 v-model:checked 或者 v-model:fileList
|
||||
modelPropNameMap: {
|
||||
Checkbox: 'checked',
|
||||
Radio: 'checked',
|
||||
Switch: 'checked',
|
||||
Upload: 'fileList',
|
||||
},
|
||||
},
|
||||
},
|
||||
defineRules: {
|
||||
// 输入项目必填国际化适配
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('ui.formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
defineRules: {
|
||||
// 输入项目必填国际化适配
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('ui.formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// 选择项目必填国际化适配
|
||||
selectRequired: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null) {
|
||||
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
// 选择项目必填国际化适配
|
||||
selectRequired: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null) {
|
||||
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const useVbenForm = useForm<ComponentType>;
|
||||
|
||||
export { useVbenForm, z };
|
||||
export { initSetupVbenForm, useVbenForm, z };
|
||||
|
||||
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||
export type { VbenFormProps };
|
||||
|
@@ -12,6 +12,7 @@ import { useTitle } from '@vueuse/core';
|
||||
import { $t, setupI18n } from '#/locales';
|
||||
|
||||
import { initComponentAdapter } from './adapter/component';
|
||||
import { initSetupVbenForm } from './adapter/form';
|
||||
import App from './app.vue';
|
||||
import { router } from './router';
|
||||
|
||||
@@ -19,6 +20,9 @@ async function bootstrap(namespace: string) {
|
||||
// 初始化组件适配器
|
||||
await initComponentAdapter();
|
||||
|
||||
// 初始化表单组件
|
||||
await initSetupVbenForm();
|
||||
|
||||
// // 设置弹窗的默认配置
|
||||
// setDefaultModalProps({
|
||||
// fullscreenButton: false,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/web-ele",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://vben.pro",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -8,32 +8,34 @@ import type { ComponentType } from './component';
|
||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
setupVbenForm<ComponentType>({
|
||||
config: {
|
||||
modelPropNameMap: {
|
||||
Upload: 'fileList',
|
||||
CheckboxGroup: 'model-value',
|
||||
async function initSetupVbenForm() {
|
||||
setupVbenForm<ComponentType>({
|
||||
config: {
|
||||
modelPropNameMap: {
|
||||
Upload: 'fileList',
|
||||
CheckboxGroup: 'model-value',
|
||||
},
|
||||
},
|
||||
},
|
||||
defineRules: {
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('ui.formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
defineRules: {
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('ui.formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
selectRequired: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null) {
|
||||
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
selectRequired: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null) {
|
||||
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const useVbenForm = useForm<ComponentType>;
|
||||
|
||||
export { useVbenForm, z };
|
||||
export { initSetupVbenForm, useVbenForm, z };
|
||||
|
||||
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||
export type { VbenFormProps };
|
||||
|
@@ -13,12 +13,17 @@ import { ElLoading } from 'element-plus';
|
||||
import { $t, setupI18n } from '#/locales';
|
||||
|
||||
import { initComponentAdapter } from './adapter/component';
|
||||
import { initSetupVbenForm } from './adapter/form';
|
||||
import App from './app.vue';
|
||||
import { router } from './router';
|
||||
|
||||
async function bootstrap(namespace: string) {
|
||||
// 初始化组件适配器
|
||||
await initComponentAdapter();
|
||||
|
||||
// 初始化表单组件
|
||||
await initSetupVbenForm();
|
||||
|
||||
// // 设置弹窗的默认配置
|
||||
// setDefaultModalProps({
|
||||
// fullscreenButton: false,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/web-naive",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://vben.pro",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -8,36 +8,38 @@ import type { ComponentType } from './component';
|
||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
setupVbenForm<ComponentType>({
|
||||
config: {
|
||||
// naive-ui组件的空值为null,不能是undefined,否则重置表单时不生效
|
||||
emptyStateValue: null,
|
||||
baseModelPropName: 'value',
|
||||
modelPropNameMap: {
|
||||
Checkbox: 'checked',
|
||||
Radio: 'checked',
|
||||
Upload: 'fileList',
|
||||
async function initSetupVbenForm() {
|
||||
setupVbenForm<ComponentType>({
|
||||
config: {
|
||||
// naive-ui组件的空值为null,不能是undefined,否则重置表单时不生效
|
||||
emptyStateValue: null,
|
||||
baseModelPropName: 'value',
|
||||
modelPropNameMap: {
|
||||
Checkbox: 'checked',
|
||||
Radio: 'checked',
|
||||
Upload: 'fileList',
|
||||
},
|
||||
},
|
||||
},
|
||||
defineRules: {
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('ui.formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
defineRules: {
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('ui.formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
selectRequired: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null) {
|
||||
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
selectRequired: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null) {
|
||||
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const useVbenForm = useForm<ComponentType>;
|
||||
|
||||
export { useVbenForm, z };
|
||||
export { initSetupVbenForm, useVbenForm, z };
|
||||
|
||||
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||
export type { VbenFormProps };
|
||||
|
@@ -12,12 +12,16 @@ import { useTitle } from '@vueuse/core';
|
||||
import { $t, setupI18n } from '#/locales';
|
||||
|
||||
import { initComponentAdapter } from './adapter/component';
|
||||
import { initSetupVbenForm } from './adapter/form';
|
||||
import App from './app.vue';
|
||||
import { router } from './router';
|
||||
|
||||
async function bootstrap(namespace: string) {
|
||||
// 初始化组件适配器
|
||||
initComponentAdapter();
|
||||
await initComponentAdapter();
|
||||
|
||||
// 初始化表单组件
|
||||
await initSetupVbenForm();
|
||||
|
||||
// // 设置弹窗的默认配置
|
||||
// setDefaultModalProps({
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/docs",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "vitepress build",
|
||||
|
@@ -26,6 +26,12 @@ outline: deep
|
||||
|
||||
<DemoPreview dir="demos/vben-ellipsis-text/tooltip" />
|
||||
|
||||
## 自动显示 tooltip
|
||||
|
||||
通过`tooltip-when-ellipsis`设置,仅在文本长度超出导致省略号出现时才触发 tooltip。
|
||||
|
||||
<DemoPreview dir="demos/vben-ellipsis-text/auto-display" />
|
||||
|
||||
## API
|
||||
|
||||
### Props
|
||||
@@ -37,6 +43,8 @@ outline: deep
|
||||
| maxWidth | 文本区域最大宽度 | `number \| string` | `'100%'` |
|
||||
| placement | 提示浮层的位置 | `'bottom'\|'left'\|'right'\|'top'` | `'top'` |
|
||||
| tooltip | 启用文本提示 | `boolean` | `true` |
|
||||
| tooltipWhenEllipsis | 内容超出,自动启用文本提示 | `boolean` | `false` |
|
||||
| ellipsisThreshold | 设置 tooltipWhenEllipsis 后才生效,文本截断检测的像素差异阈值,越大则判断越严格,如果碰见异常情况可以自己设置阈值 | `number` | `3` |
|
||||
| tooltipBackgroundColor | 提示文本的背景颜色 | `string` | - |
|
||||
| tooltipColor | 提示文本的颜色 | `string` | - |
|
||||
| tooltipFontSize | 提示文本的大小 | `string` | - |
|
||||
|
16
docs/src/demos/vben-ellipsis-text/auto-display/index.vue
Normal file
16
docs/src/demos/vben-ellipsis-text/auto-display/index.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import { EllipsisText } from '@vben/common-ui';
|
||||
|
||||
const text = `
|
||||
Vben Admin 是一个基于 Vue3.0、Vite、 TypeScript 的后台解决方案,目标是为开发中大型项目提供开箱即用的解决方案。
|
||||
`;
|
||||
</script>
|
||||
<template>
|
||||
<EllipsisText :line="2" :tooltip-when-ellipsis="true">
|
||||
{{ text }}
|
||||
</EllipsisText>
|
||||
|
||||
<EllipsisText :line="3" :tooltip-when-ellipsis="true">
|
||||
{{ text }}
|
||||
</EllipsisText>
|
||||
</template>
|
@@ -21,7 +21,7 @@ The rules are consistent with [Vite Env Variables and Modes](https://vitejs.dev/
|
||||
console.log(import.meta.env.VITE_PROT);
|
||||
```
|
||||
|
||||
- Variables starting with `VITE_GLOB_*` will be added to the `_app.config.js` configuration file during packaging. :::
|
||||
- Variables starting with `VITE_GLOB_*` will be added to the `_app.config.js` configuration file during packaging.
|
||||
|
||||
:::
|
||||
|
||||
@@ -138,6 +138,27 @@ To add a new dynamically modifiable configuration item, simply follow the steps
|
||||
}
|
||||
```
|
||||
|
||||
- In `packages/effects/hooks/src/use-app-config.ts`, add the corresponding configuration item, such as:
|
||||
|
||||
```ts
|
||||
export function useAppConfig(
|
||||
env: Record<string, any>,
|
||||
isProduction: boolean,
|
||||
): ApplicationConfig {
|
||||
// In production environment, directly use the window._VBEN_ADMIN_PRO_APP_CONF_ global variable
|
||||
const config = isProduction
|
||||
? window._VBEN_ADMIN_PRO_APP_CONF_
|
||||
: (env as VbenAdminProAppConfigRaw);
|
||||
|
||||
const { VITE_GLOB_API_URL, VITE_GLOB_OTHER_API_URL } = config; // [!code ++]
|
||||
|
||||
return {
|
||||
apiURL: VITE_GLOB_API_URL,
|
||||
otherApiURL: VITE_GLOB_OTHER_API_URL, // [!code ++]
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
At this point, you can use the `useAppConfig` method within the project to access the newly added configuration item.
|
||||
|
||||
```ts
|
||||
@@ -186,6 +207,12 @@ const defaultPreferences: Preferences = {
|
||||
colorWeakMode: false,
|
||||
compact: false,
|
||||
contentCompact: 'wide',
|
||||
contentCompactWidth: 1200,
|
||||
contentPadding: 0,
|
||||
contentPaddingBottom: 0,
|
||||
contentPaddingLeft: 0,
|
||||
contentPaddingRight: 0,
|
||||
contentPaddingTop: 0,
|
||||
defaultAvatar:
|
||||
'https://unpkg.com/@vbenjs/static-source@0.1.7/source/avatar-v1.webp',
|
||||
defaultHomePath: '/analytics',
|
||||
@@ -200,6 +227,7 @@ const defaultPreferences: Preferences = {
|
||||
name: 'Vben Admin',
|
||||
preferencesButtonPosition: 'auto',
|
||||
watermark: false,
|
||||
zIndex: 200,
|
||||
},
|
||||
breadcrumb: {
|
||||
enable: true,
|
||||
@@ -220,15 +248,18 @@ const defaultPreferences: Preferences = {
|
||||
footer: {
|
||||
enable: false,
|
||||
fixed: false,
|
||||
height: 32,
|
||||
},
|
||||
header: {
|
||||
enable: true,
|
||||
height: 50,
|
||||
hidden: false,
|
||||
menuAlign: 'start',
|
||||
mode: 'fixed',
|
||||
},
|
||||
logo: {
|
||||
enable: true,
|
||||
fit: 'contain',
|
||||
source: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
|
||||
},
|
||||
navigation: {
|
||||
@@ -248,11 +279,14 @@ const defaultPreferences: Preferences = {
|
||||
collapsed: false,
|
||||
collapsedButton: true,
|
||||
collapsedShowTitle: false,
|
||||
collapseWidth: 60,
|
||||
enable: true,
|
||||
expandOnHover: true,
|
||||
extraCollapse: false,
|
||||
extraCollapsedWidth: 60,
|
||||
fixedButton: true,
|
||||
hidden: false,
|
||||
mixedWidth: 80,
|
||||
width: 224,
|
||||
},
|
||||
tabbar: {
|
||||
@@ -319,6 +353,18 @@ interface AppPreferences {
|
||||
compact: boolean;
|
||||
/** Whether to enable content compact mode */
|
||||
contentCompact: ContentCompactType;
|
||||
/** Content compact width */
|
||||
contentCompactWidth: number;
|
||||
/** Content padding */
|
||||
contentPadding: number;
|
||||
/** Content bottom padding */
|
||||
contentPaddingBottom: number;
|
||||
/** Content left padding */
|
||||
contentPaddingLeft: number;
|
||||
/** Content right padding */
|
||||
contentPaddingRight: number;
|
||||
/** Content top padding */
|
||||
contentPaddingTop: number;
|
||||
// /** Default application avatar */
|
||||
defaultAvatar: string;
|
||||
/** Default homepage path */
|
||||
@@ -349,6 +395,8 @@ interface AppPreferences {
|
||||
* @zh_CN Whether to enable watermark
|
||||
*/
|
||||
watermark: boolean;
|
||||
/** z-index */
|
||||
zIndex: number;
|
||||
}
|
||||
interface BreadcrumbPreferences {
|
||||
/** Whether breadcrumbs are enabled */
|
||||
@@ -385,11 +433,15 @@ interface FooterPreferences {
|
||||
enable: boolean;
|
||||
/** Whether the footer is fixed */
|
||||
fixed: boolean;
|
||||
/** Footer height */
|
||||
height: number;
|
||||
}
|
||||
|
||||
interface HeaderPreferences {
|
||||
/** Whether the header is enabled */
|
||||
enable: boolean;
|
||||
/** Header height */
|
||||
height: number;
|
||||
/** Whether the header is hidden, css-hidden */
|
||||
hidden: boolean;
|
||||
/** Header menu alignment */
|
||||
@@ -401,6 +453,8 @@ interface HeaderPreferences {
|
||||
interface LogoPreferences {
|
||||
/** Whether the logo is visible */
|
||||
enable: boolean;
|
||||
/** Logo image fitting method */
|
||||
fit: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
||||
/** Logo URL */
|
||||
source: string;
|
||||
}
|
||||
@@ -422,16 +476,22 @@ interface SidebarPreferences {
|
||||
collapsedButton: boolean;
|
||||
/** Whether to show title when sidebar is collapsed */
|
||||
collapsedShowTitle: boolean;
|
||||
/** Sidebar collapse width */
|
||||
collapseWidth: number;
|
||||
/** Whether the sidebar is visible */
|
||||
enable: boolean;
|
||||
/** Menu auto-expand state */
|
||||
expandOnHover: boolean;
|
||||
/** Whether the sidebar extension area is collapsed */
|
||||
extraCollapse: boolean;
|
||||
/** Sidebar extension area collapse width */
|
||||
extraCollapsedWidth: number;
|
||||
/** Whether the sidebar fixed button is visible */
|
||||
fixedButton: boolean;
|
||||
/** Whether the sidebar is hidden - css */
|
||||
hidden: boolean;
|
||||
/** Mixed sidebar width */
|
||||
mixedWidth: number;
|
||||
/** Sidebar width */
|
||||
width: number;
|
||||
}
|
||||
|
@@ -4,10 +4,11 @@ outline: deep
|
||||
|
||||
# Access Control
|
||||
|
||||
The framework has built-in two types of access control methods:
|
||||
The framework has built-in three types of access control methods:
|
||||
|
||||
- Determining whether a menu or button can be accessed based on user roles
|
||||
- Determining whether a menu or button can be accessed through an API
|
||||
- Mixed mode: Using both frontend and backend access control simultaneously
|
||||
|
||||
## Frontend Access Control
|
||||
|
||||
@@ -151,6 +152,43 @@ const dashboardMenus = [
|
||||
|
||||
At this point, the configuration is complete. You need to ensure that after logging in, the format of the menu returned by the interface is correct; otherwise, access will not be possible.
|
||||
|
||||
## Mixed Access Control
|
||||
|
||||
**Implementation Principle**: Mixed mode combines both frontend access control and backend access control methods. The system processes frontend fixed route permissions and backend dynamic menu data in parallel, ultimately merging both parts of routes to provide a more flexible access control solution.
|
||||
|
||||
**Advantages**: Combines the performance advantages of frontend control with the flexibility of backend control, suitable for complex business scenarios requiring permission management.
|
||||
|
||||
### Steps
|
||||
|
||||
- Ensure the current mode is set to mixed access control
|
||||
|
||||
Adjust `preferences.ts` in the corresponding application directory to ensure `accessMode='mixed'`.
|
||||
|
||||
```ts
|
||||
import { defineOverridesPreferences } from '@vben/preferences';
|
||||
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
accessMode: 'mixed',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
- Configure frontend route permissions
|
||||
|
||||
Same as the route permission configuration method in [Frontend Access Control](#frontend-access-control) mode.
|
||||
|
||||
- Configure backend menu interface
|
||||
|
||||
Same as the interface configuration method in [Backend Access Control](#backend-access-control) mode.
|
||||
|
||||
- Ensure roles and permissions match
|
||||
|
||||
Must satisfy both frontend route permission configuration and backend menu data return requirements, ensuring user roles match the permission configurations of both modes.
|
||||
|
||||
At this point, the configuration is complete. Mixed mode will automatically merge frontend and backend routes, providing complete access control functionality.
|
||||
|
||||
## Fine-grained Control of Buttons
|
||||
|
||||
In some cases, we need to control the display of buttons with fine granularity. We can control the display of buttons through interfaces or roles.
|
||||
|
@@ -214,7 +214,7 @@ server {
|
||||
|
||||
使用 nginx 处理项目部署后的跨域问题
|
||||
|
||||
1. 配置前端项目接口地址,在项目目录下的``.env.production`文件中配置:
|
||||
1. 配置前端项目接口地址,在项目目录下的`.env.production`文件中配置:
|
||||
|
||||
```bash
|
||||
VITE_GLOB_API_URL=/api
|
||||
|
@@ -21,7 +21,7 @@
|
||||
console.log(import.meta.env.VITE_PROT);
|
||||
```
|
||||
|
||||
- 以 `VITE_GLOB_*` 开头的的变量,在打包的时候,会被加入 `_app.config.js`配置文件当中. :::
|
||||
- 以 `VITE_GLOB_*` 开头的的变量,在打包的时候,会被加入 `_app.config.js`配置文件当中.
|
||||
|
||||
:::
|
||||
|
||||
@@ -137,6 +137,27 @@ const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||
}
|
||||
```
|
||||
|
||||
- 在 `packages/effects/hooks/src/use-app-config.ts` 中,新增对应的配置项,如:
|
||||
|
||||
```ts
|
||||
export function useAppConfig(
|
||||
env: Record<string, any>,
|
||||
isProduction: boolean,
|
||||
): ApplicationConfig {
|
||||
// 生产环境下,直接使用 window._VBEN_ADMIN_PRO_APP_CONF_ 全局变量
|
||||
const config = isProduction
|
||||
? window._VBEN_ADMIN_PRO_APP_CONF_
|
||||
: (env as VbenAdminProAppConfigRaw);
|
||||
|
||||
const { VITE_GLOB_API_URL, VITE_GLOB_OTHER_API_URL } = config; // [!code ++]
|
||||
|
||||
return {
|
||||
apiURL: VITE_GLOB_API_URL,
|
||||
otherApiURL: VITE_GLOB_OTHER_API_URL, // [!code ++]
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
到这里,就可以在项目内使用 `useAppConfig`方法获取到新增的配置项了。
|
||||
|
||||
```ts
|
||||
@@ -185,6 +206,12 @@ const defaultPreferences: Preferences = {
|
||||
colorWeakMode: false,
|
||||
compact: false,
|
||||
contentCompact: 'wide',
|
||||
contentCompactWidth: 1200,
|
||||
contentPadding: 0,
|
||||
contentPaddingBottom: 0,
|
||||
contentPaddingLeft: 0,
|
||||
contentPaddingRight: 0,
|
||||
contentPaddingTop: 0,
|
||||
defaultAvatar:
|
||||
'https://unpkg.com/@vbenjs/static-source@0.1.7/source/avatar-v1.webp',
|
||||
defaultHomePath: '/analytics',
|
||||
@@ -199,6 +226,7 @@ const defaultPreferences: Preferences = {
|
||||
name: 'Vben Admin',
|
||||
preferencesButtonPosition: 'auto',
|
||||
watermark: false,
|
||||
zIndex: 200,
|
||||
},
|
||||
breadcrumb: {
|
||||
enable: true,
|
||||
@@ -219,15 +247,18 @@ const defaultPreferences: Preferences = {
|
||||
footer: {
|
||||
enable: false,
|
||||
fixed: false,
|
||||
height: 32,
|
||||
},
|
||||
header: {
|
||||
enable: true,
|
||||
height: 50,
|
||||
hidden: false,
|
||||
menuAlign: 'start',
|
||||
mode: 'fixed',
|
||||
},
|
||||
logo: {
|
||||
enable: true,
|
||||
fit: 'contain',
|
||||
source: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
|
||||
},
|
||||
navigation: {
|
||||
@@ -247,11 +278,14 @@ const defaultPreferences: Preferences = {
|
||||
collapsed: false,
|
||||
collapsedButton: true,
|
||||
collapsedShowTitle: false,
|
||||
collapseWidth: 60,
|
||||
enable: true,
|
||||
expandOnHover: true,
|
||||
extraCollapse: false,
|
||||
extraCollapsedWidth: 60,
|
||||
fixedButton: true,
|
||||
hidden: false,
|
||||
mixedWidth: 80,
|
||||
width: 224,
|
||||
},
|
||||
tabbar: {
|
||||
@@ -318,6 +352,18 @@ interface AppPreferences {
|
||||
compact: boolean;
|
||||
/** 是否开启内容紧凑模式 */
|
||||
contentCompact: ContentCompactType;
|
||||
/** 内容紧凑宽度 */
|
||||
contentCompactWidth: number;
|
||||
/** 内容内边距 */
|
||||
contentPadding: number;
|
||||
/** 内容底部内边距 */
|
||||
contentPaddingBottom: number;
|
||||
/** 内容左侧内边距 */
|
||||
contentPaddingLeft: number;
|
||||
/** 内容右侧内边距 */
|
||||
contentPaddingRight: number;
|
||||
/** 内容顶部内边距 */
|
||||
contentPaddingTop: number;
|
||||
// /** 应用默认头像 */
|
||||
defaultAvatar: string;
|
||||
/** 默认首页地址 */
|
||||
@@ -348,6 +394,8 @@ interface AppPreferences {
|
||||
* @zh_CN 是否开启水印
|
||||
*/
|
||||
watermark: boolean;
|
||||
/** z-index */
|
||||
zIndex: number;
|
||||
}
|
||||
|
||||
interface BreadcrumbPreferences {
|
||||
@@ -385,11 +433,15 @@ interface FooterPreferences {
|
||||
enable: boolean;
|
||||
/** 底栏是否固定 */
|
||||
fixed: boolean;
|
||||
/** 底栏高度 */
|
||||
height: number;
|
||||
}
|
||||
|
||||
interface HeaderPreferences {
|
||||
/** 顶栏是否启用 */
|
||||
enable: boolean;
|
||||
/** 顶栏高度 */
|
||||
height: number;
|
||||
/** 顶栏是否隐藏,css-隐藏 */
|
||||
hidden: boolean;
|
||||
/** 顶栏菜单位置 */
|
||||
@@ -401,6 +453,8 @@ interface HeaderPreferences {
|
||||
interface LogoPreferences {
|
||||
/** logo是否可见 */
|
||||
enable: boolean;
|
||||
/** logo图片适应方式 */
|
||||
fit: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
||||
/** logo地址 */
|
||||
source: string;
|
||||
}
|
||||
@@ -423,16 +477,22 @@ interface SidebarPreferences {
|
||||
collapsedButton: boolean;
|
||||
/** 侧边栏折叠时,是否显示title */
|
||||
collapsedShowTitle: boolean;
|
||||
/** 侧边栏折叠宽度 */
|
||||
collapseWidth: number;
|
||||
/** 侧边栏是否可见 */
|
||||
enable: boolean;
|
||||
/** 菜单自动展开状态 */
|
||||
expandOnHover: boolean;
|
||||
/** 侧边栏扩展区域是否折叠 */
|
||||
extraCollapse: boolean;
|
||||
/** 侧边栏扩展区域折叠宽度 */
|
||||
extraCollapsedWidth: number;
|
||||
/** 侧边栏固定按钮是否可见 */
|
||||
fixedButton: boolean;
|
||||
/** 侧边栏是否隐藏 - css */
|
||||
hidden: boolean;
|
||||
/** 混合侧边栏宽度 */
|
||||
mixedWidth: number;
|
||||
/** 侧边栏宽度 */
|
||||
width: number;
|
||||
}
|
||||
|
@@ -4,10 +4,11 @@ outline: deep
|
||||
|
||||
# 权限
|
||||
|
||||
框架内置了两种权限控制方式:
|
||||
框架内置了三种权限控制方式:
|
||||
|
||||
- 通过用户角色来判断菜单或者按钮是否可以访问
|
||||
- 通过接口来判断菜单或者按钮是否可以访问
|
||||
- 混合模式:同时使用前端和后端权限控制
|
||||
|
||||
## 前端访问控制
|
||||
|
||||
@@ -159,6 +160,43 @@ const dashboardMenus = [
|
||||
|
||||
到这里,就已经配置完成,你需要确保登录后,接口返回的菜单格式正确,否则无法访问。
|
||||
|
||||
## 混合访问控制
|
||||
|
||||
**实现原理**: 混合模式同时结合了前端访问控制和后端访问控制两种方式。系统会并行处理前端固定路由权限和后端动态菜单数据,最终将两部分路由合并,提供更灵活的权限控制方案。
|
||||
|
||||
**优点**: 兼具前端控制的性能优势和后端控制的灵活性,适合复杂业务场景下的权限管理。
|
||||
|
||||
### 步骤
|
||||
|
||||
- 确保当前模式为混合访问控制模式
|
||||
|
||||
调整对应应用目录下的`preferences.ts`,确保`accessMode='mixed'`。
|
||||
|
||||
```ts
|
||||
import { defineOverridesPreferences } from '@vben/preferences';
|
||||
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
accessMode: 'mixed',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
- 配置前端路由权限
|
||||
|
||||
同[前端访问控制](#前端访问控制)模式的路由权限配置方式。
|
||||
|
||||
- 配置后端菜单接口
|
||||
|
||||
同[后端访问控制](#后端访问控制)模式的接口配置方式。
|
||||
|
||||
- 确保角色和权限匹配
|
||||
|
||||
需要同时满足前端路由权限配置和后端菜单数据返回的要求,确保用户角色与两种模式的权限配置都匹配。
|
||||
|
||||
到这里,就已经配置完成,混合模式会自动合并前端和后端的路由,提供完整的权限控制功能。
|
||||
|
||||
## 按钮细粒度控制
|
||||
|
||||
在某些情况下,我们需要对按钮进行细粒度的控制,我们可以借助接口或者角色来控制按钮的显示。
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/commitlint-config",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/stylelint-config",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/node-utils",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/tailwind-config",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/tsconfig",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"private": true,
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
|
@@ -47,15 +47,12 @@
|
||||
"@vitejs/plugin-vue-jsx": "catalog:",
|
||||
"dayjs": "catalog:",
|
||||
"dotenv": "catalog:",
|
||||
"electron": "catalog:",
|
||||
"rollup": "catalog:",
|
||||
"rollup-plugin-visualizer": "catalog:",
|
||||
"sass": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vite-plugin-compression": "catalog:",
|
||||
"vite-plugin-dts": "catalog:",
|
||||
"vite-plugin-electron": "catalog:",
|
||||
"vite-plugin-electron-renderer": "catalog:",
|
||||
"vite-plugin-html": "catalog:",
|
||||
"vite-plugin-lazy-import": "catalog:"
|
||||
}
|
||||
|
@@ -1,40 +0,0 @@
|
||||
import type { PluginOption } from 'vite';
|
||||
|
||||
import type { CommonPluginOptions } from '../typing';
|
||||
|
||||
import fs from 'node:fs';
|
||||
|
||||
import electron from 'vite-plugin-electron/simple';
|
||||
|
||||
export const viteElectronPlugin = (
|
||||
options: CommonPluginOptions,
|
||||
): PluginOption => {
|
||||
fs.rmSync('dist-electron', { force: true, recursive: true });
|
||||
|
||||
const isServe = !options.isBuild;
|
||||
const isBuild = options.isBuild;
|
||||
const sourcemap = isServe || !!process.env.VSCODE_DEBUG;
|
||||
return electron({
|
||||
main: {
|
||||
entry: 'electron/main.ts',
|
||||
vite: {
|
||||
build: {
|
||||
minify: isBuild,
|
||||
outDir: 'dist-electron/main',
|
||||
sourcemap,
|
||||
},
|
||||
},
|
||||
},
|
||||
preload: {
|
||||
input: 'electron/preload.ts',
|
||||
vite: {
|
||||
build: {
|
||||
minify: isBuild,
|
||||
outDir: 'dist-electron/preload',
|
||||
sourcemap: sourcemap ? 'inline' : undefined, // #332
|
||||
},
|
||||
},
|
||||
},
|
||||
renderer: {},
|
||||
});
|
||||
};
|
@@ -18,7 +18,6 @@ import { VitePWA } from 'vite-plugin-pwa';
|
||||
import viteVueDevTools from 'vite-plugin-vue-devtools';
|
||||
|
||||
import { viteArchiverPlugin } from './archiver';
|
||||
import { viteElectronPlugin } from './electron';
|
||||
import { viteExtraAppConfigPlugin } from './extra-app-config';
|
||||
import { viteImportMapPlugin } from './importmap';
|
||||
import { viteInjectAppLoadingPlugin } from './inject-app-loading';
|
||||
@@ -98,7 +97,6 @@ async function loadApplicationPlugins(
|
||||
archiverPluginOptions,
|
||||
compress,
|
||||
compressTypes,
|
||||
electron,
|
||||
extraAppConfig,
|
||||
html,
|
||||
i18n,
|
||||
@@ -215,10 +213,6 @@ async function loadApplicationPlugins(
|
||||
return [await viteArchiverPlugin(archiverPluginOptions)];
|
||||
},
|
||||
},
|
||||
{
|
||||
condition: electron,
|
||||
plugins: () => [viteElectronPlugin(commonOptions)],
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
|
@@ -3,6 +3,23 @@ import type { ConfigEnv, PluginOption, UserConfig } from 'vite';
|
||||
import type { PluginOptions } from 'vite-plugin-dts';
|
||||
import type { Options as PwaPluginOptions } from 'vite-plugin-pwa';
|
||||
|
||||
/**
|
||||
* ImportMap 配置接口
|
||||
* @description 用于配置模块导入映射,支持自定义导入路径和范围
|
||||
* @example
|
||||
* ```typescript
|
||||
* {
|
||||
* imports: {
|
||||
* 'vue': 'https://unpkg.com/vue@3.2.47/dist/vue.esm-browser.js'
|
||||
* },
|
||||
* scopes: {
|
||||
* 'https://site.com/': {
|
||||
* 'vue': 'https://unpkg.com/vue@3.2.47/dist/vue.esm-browser.js'
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
interface IImportMap {
|
||||
/** 模块导入映射 */
|
||||
imports?: Record<string, string>;
|
||||
@@ -185,8 +202,6 @@ interface ApplicationPluginOptions extends CommonPluginOptions {
|
||||
* @description 可选的压缩类型
|
||||
*/
|
||||
compressTypes?: ('brotli' | 'gzip')[];
|
||||
/** 启用electron */
|
||||
electron?: boolean;
|
||||
/**
|
||||
* 是否抽离配置文件
|
||||
* @default false
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vben-admin-monorepo",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"private": true,
|
||||
"keywords": [
|
||||
"monorepo",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/design",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/icons",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -45,7 +45,6 @@ export {
|
||||
Menu,
|
||||
Minimize,
|
||||
Minimize2,
|
||||
Minus,
|
||||
MoonStar,
|
||||
Palette,
|
||||
PanelLeft,
|
||||
@@ -59,8 +58,6 @@ export {
|
||||
Settings,
|
||||
Shrink,
|
||||
Square,
|
||||
SquareArrowDownLeft,
|
||||
SquareArrowUpRight,
|
||||
SquareCheckBig,
|
||||
SquareMinus,
|
||||
Sun,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/shared",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
@@ -98,8 +98,6 @@
|
||||
"@types/lodash.get": "catalog:",
|
||||
"@types/lodash.isequal": "catalog:",
|
||||
"@types/lodash.set": "catalog:",
|
||||
"@types/nprogress": "catalog:",
|
||||
"@vben-core/typings": "workspace:*",
|
||||
"electron": "catalog:"
|
||||
"@types/nprogress": "catalog:"
|
||||
}
|
||||
}
|
||||
|
@@ -28,11 +28,10 @@ function openWindow(url: string, options: OpenWindowOptions = {}): void {
|
||||
* @param path
|
||||
*/
|
||||
function openRouteInNewWindow(path: string) {
|
||||
// const { hash, origin } = location;
|
||||
const { hash, origin } = location;
|
||||
const fullPath = path.startsWith('/') ? path : `/${path}`;
|
||||
// const url = `${origin}${hash ? '/#' : ''}${fullPath}`;
|
||||
// openWindow(url, { target: '_blank' });
|
||||
window.ipcRenderer.invoke('open-win', fullPath);
|
||||
const url = `${origin}${hash ? '/#' : ''}${fullPath}`;
|
||||
openWindow(url, { target: '_blank' });
|
||||
}
|
||||
|
||||
export { openRouteInNewWindow, openWindow };
|
||||
|
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/tsconfig/library.json",
|
||||
"compilerOptions": {
|
||||
"types": ["@vben-core/typings/electron"]
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
27
packages/@core/base/typings/electron.d.ts
vendored
27
packages/@core/base/typings/electron.d.ts
vendored
@@ -1,27 +0,0 @@
|
||||
import type { IpcRendererEvent } from 'electron';
|
||||
|
||||
export type IpcRendererInvoke =
|
||||
| 'app-close'
|
||||
| 'app-maximize'
|
||||
| 'app-minimize'
|
||||
| 'is-maximized'
|
||||
| 'open-win';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
ipcRenderer: {
|
||||
invoke: (channel: IpcRendererInvoke, ...args: any[]) => Promise<any>;
|
||||
off: (
|
||||
channel: string,
|
||||
listener: (event: IpcRendererEvent, ...args: any[]) => void,
|
||||
) => void;
|
||||
on: (
|
||||
channel: string,
|
||||
listener: (event: IpcRendererEvent, ...args: any[]) => void,
|
||||
) => void;
|
||||
send: (channel: string, data: any) => Promise<any>;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/typings",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
@@ -27,9 +27,6 @@
|
||||
},
|
||||
"./vue-router": {
|
||||
"types": "./vue-router.d.ts"
|
||||
},
|
||||
"./electron": {
|
||||
"types": "./electron.d.ts"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
|
3
packages/@core/base/typings/src/app.d.ts
vendored
3
packages/@core/base/typings/src/app.d.ts
vendored
@@ -60,8 +60,9 @@ type BreadcrumbStyleType = 'background' | 'normal';
|
||||
* 权限模式
|
||||
* backend 后端权限模式
|
||||
* frontend 前端权限模式
|
||||
* mixed 混合权限模式
|
||||
*/
|
||||
type AccessModeType = 'backend' | 'frontend';
|
||||
type AccessModeType = 'backend' | 'frontend' | 'mixed';
|
||||
|
||||
/**
|
||||
* 导航风格
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/composables",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -10,6 +10,12 @@ exports[`defaultPreferences immutability test > should not modify the config obj
|
||||
"colorWeakMode": false,
|
||||
"compact": false,
|
||||
"contentCompact": "wide",
|
||||
"contentCompactWidth": 1200,
|
||||
"contentPadding": 0,
|
||||
"contentPaddingBottom": 0,
|
||||
"contentPaddingLeft": 0,
|
||||
"contentPaddingRight": 0,
|
||||
"contentPaddingTop": 0,
|
||||
"defaultAvatar": "https://unpkg.com/@vbenjs/static-source@0.1.7/source/avatar-v1.webp",
|
||||
"defaultHomePath": "/analytics",
|
||||
"dynamicTitle": true,
|
||||
@@ -23,6 +29,7 @@ exports[`defaultPreferences immutability test > should not modify the config obj
|
||||
"name": "Vben Admin",
|
||||
"preferencesButtonPosition": "auto",
|
||||
"watermark": false,
|
||||
"zIndex": 200,
|
||||
},
|
||||
"breadcrumb": {
|
||||
"enable": true,
|
||||
@@ -43,15 +50,18 @@ exports[`defaultPreferences immutability test > should not modify the config obj
|
||||
"footer": {
|
||||
"enable": false,
|
||||
"fixed": false,
|
||||
"height": 32,
|
||||
},
|
||||
"header": {
|
||||
"enable": true,
|
||||
"height": 50,
|
||||
"hidden": false,
|
||||
"menuAlign": "start",
|
||||
"mode": "fixed",
|
||||
},
|
||||
"logo": {
|
||||
"enable": true,
|
||||
"fit": "contain",
|
||||
"source": "https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp",
|
||||
},
|
||||
"navigation": {
|
||||
@@ -68,14 +78,17 @@ exports[`defaultPreferences immutability test > should not modify the config obj
|
||||
},
|
||||
"sidebar": {
|
||||
"autoActivateChild": false,
|
||||
"collapseWidth": 60,
|
||||
"collapsed": false,
|
||||
"collapsedButton": true,
|
||||
"collapsedShowTitle": false,
|
||||
"enable": true,
|
||||
"expandOnHover": true,
|
||||
"extraCollapse": false,
|
||||
"extraCollapsedWidth": 60,
|
||||
"fixedButton": true,
|
||||
"hidden": false,
|
||||
"mixedWidth": 80,
|
||||
"width": 224,
|
||||
},
|
||||
"tabbar": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/preferences",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -9,6 +9,12 @@ const defaultPreferences: Preferences = {
|
||||
colorWeakMode: false,
|
||||
compact: false,
|
||||
contentCompact: 'wide',
|
||||
contentCompactWidth: 1200,
|
||||
contentPadding: 0,
|
||||
contentPaddingBottom: 0,
|
||||
contentPaddingLeft: 0,
|
||||
contentPaddingRight: 0,
|
||||
contentPaddingTop: 0,
|
||||
defaultAvatar:
|
||||
'https://unpkg.com/@vbenjs/static-source@0.1.7/source/avatar-v1.webp',
|
||||
defaultHomePath: '/analytics',
|
||||
@@ -23,6 +29,7 @@ const defaultPreferences: Preferences = {
|
||||
name: 'Vben Admin',
|
||||
preferencesButtonPosition: 'auto',
|
||||
watermark: false,
|
||||
zIndex: 200,
|
||||
},
|
||||
breadcrumb: {
|
||||
enable: true,
|
||||
@@ -43,15 +50,19 @@ const defaultPreferences: Preferences = {
|
||||
footer: {
|
||||
enable: false,
|
||||
fixed: false,
|
||||
height: 32,
|
||||
},
|
||||
header: {
|
||||
enable: true,
|
||||
height: 50,
|
||||
hidden: false,
|
||||
menuAlign: 'start',
|
||||
mode: 'fixed',
|
||||
},
|
||||
|
||||
logo: {
|
||||
enable: true,
|
||||
fit: 'contain',
|
||||
source: 'https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp',
|
||||
},
|
||||
navigation: {
|
||||
@@ -71,11 +82,14 @@ const defaultPreferences: Preferences = {
|
||||
collapsed: false,
|
||||
collapsedButton: true,
|
||||
collapsedShowTitle: false,
|
||||
collapseWidth: 60,
|
||||
enable: true,
|
||||
expandOnHover: true,
|
||||
extraCollapse: false,
|
||||
extraCollapsedWidth: 60,
|
||||
fixedButton: true,
|
||||
hidden: false,
|
||||
mixedWidth: 80,
|
||||
width: 224,
|
||||
},
|
||||
tabbar: {
|
||||
|
@@ -33,6 +33,18 @@ interface AppPreferences {
|
||||
compact: boolean;
|
||||
/** 是否开启内容紧凑模式 */
|
||||
contentCompact: ContentCompactType;
|
||||
/** 内容紧凑宽度 */
|
||||
contentCompactWidth: number;
|
||||
/** 内容内边距 */
|
||||
contentPadding: number;
|
||||
/** 内容底部内边距 */
|
||||
contentPaddingBottom: number;
|
||||
/** 内容左侧内边距 */
|
||||
contentPaddingLeft: number;
|
||||
/** 内容右侧内边距 */
|
||||
contentPaddingRight: number;
|
||||
/** 内容顶部内边距 */
|
||||
contentPaddingTop: number;
|
||||
// /** 应用默认头像 */
|
||||
defaultAvatar: string;
|
||||
/** 默认首页地址 */
|
||||
@@ -63,6 +75,8 @@ interface AppPreferences {
|
||||
* @zh_CN 是否开启水印
|
||||
*/
|
||||
watermark: boolean;
|
||||
/** z-index */
|
||||
zIndex: number;
|
||||
}
|
||||
|
||||
interface BreadcrumbPreferences {
|
||||
@@ -100,11 +114,15 @@ interface FooterPreferences {
|
||||
enable: boolean;
|
||||
/** 底栏是否固定 */
|
||||
fixed: boolean;
|
||||
/** 底栏高度 */
|
||||
height: number;
|
||||
}
|
||||
|
||||
interface HeaderPreferences {
|
||||
/** 顶栏是否启用 */
|
||||
enable: boolean;
|
||||
/** 顶栏高度 */
|
||||
height: number;
|
||||
/** 顶栏是否隐藏,css-隐藏 */
|
||||
hidden: boolean;
|
||||
/** 顶栏菜单位置 */
|
||||
@@ -116,6 +134,8 @@ interface HeaderPreferences {
|
||||
interface LogoPreferences {
|
||||
/** logo是否可见 */
|
||||
enable: boolean;
|
||||
/** logo图片适应方式 */
|
||||
fit: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
||||
/** logo地址 */
|
||||
source: string;
|
||||
}
|
||||
@@ -138,16 +158,22 @@ interface SidebarPreferences {
|
||||
collapsedButton: boolean;
|
||||
/** 侧边栏折叠时,是否显示title */
|
||||
collapsedShowTitle: boolean;
|
||||
/** 侧边栏折叠宽度 */
|
||||
collapseWidth: number;
|
||||
/** 侧边栏是否可见 */
|
||||
enable: boolean;
|
||||
/** 菜单自动展开状态 */
|
||||
expandOnHover: boolean;
|
||||
/** 侧边栏扩展区域是否折叠 */
|
||||
extraCollapse: boolean;
|
||||
/** 侧边栏扩展区域折叠宽度 */
|
||||
extraCollapsedWidth: number;
|
||||
/** 侧边栏固定按钮是否可见 */
|
||||
fixedButton: boolean;
|
||||
/** 侧边栏是否隐藏 - css */
|
||||
hidden: boolean;
|
||||
/** 混合侧边栏宽度 */
|
||||
mixedWidth: number;
|
||||
/** 侧边栏宽度 */
|
||||
width: number;
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/form-ui",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -11,7 +11,7 @@ import type { Recordable } from '@vben-core/typings';
|
||||
|
||||
import type { FormActions, FormSchema, VbenFormProps } from './types';
|
||||
|
||||
import { toRaw } from 'vue';
|
||||
import { isRef, toRaw } from 'vue';
|
||||
|
||||
import { Store } from '@vben-core/shared/store';
|
||||
import {
|
||||
@@ -100,9 +100,26 @@ export class FormApi {
|
||||
getFieldComponentRef<T = ComponentPublicInstance>(
|
||||
fieldName: string,
|
||||
): T | undefined {
|
||||
return this.componentRefMap.has(fieldName)
|
||||
? (this.componentRefMap.get(fieldName) as T)
|
||||
let target = this.componentRefMap.has(fieldName)
|
||||
? (this.componentRefMap.get(fieldName) as ComponentPublicInstance)
|
||||
: undefined;
|
||||
if (
|
||||
target &&
|
||||
target.$.type.name === 'AsyncComponentWrapper' &&
|
||||
target.$.subTree.ref
|
||||
) {
|
||||
if (Array.isArray(target.$.subTree.ref)) {
|
||||
if (
|
||||
target.$.subTree.ref.length > 0 &&
|
||||
isRef(target.$.subTree.ref[0]?.r)
|
||||
) {
|
||||
target = target.$.subTree.ref[0]?.r.value as ComponentPublicInstance;
|
||||
}
|
||||
} else if (isRef(target.$.subTree.ref.r)) {
|
||||
target = target.$.subTree.ref.r.value as ComponentPublicInstance;
|
||||
}
|
||||
}
|
||||
return target as T;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -10,7 +10,7 @@ import { createContext } from '@vben-core/shadcn-ui';
|
||||
import { isString, mergeWithArrayOverride, set } from '@vben-core/shared/utils';
|
||||
|
||||
import { useForm } from 'vee-validate';
|
||||
import { object } from 'zod';
|
||||
import { object, ZodIntersection, ZodNumber, ZodObject, ZodString } from 'zod';
|
||||
import { getDefaultsForSchema } from 'zod-defaults';
|
||||
|
||||
type ExtendFormProps = VbenFormProps & { formApi: ExtendedFormApi };
|
||||
@@ -52,7 +52,12 @@ export function useFormInitial(
|
||||
if (Reflect.has(item, 'defaultValue')) {
|
||||
set(initialValues, item.fieldName, item.defaultValue);
|
||||
} else if (item.rules && !isString(item.rules)) {
|
||||
// 检查规则是否适合提取默认值
|
||||
const customDefaultValue = getCustomDefaultValue(item.rules);
|
||||
zodObject[item.fieldName] = item.rules;
|
||||
if (customDefaultValue !== undefined) {
|
||||
initialValues[item.fieldName] = customDefaultValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -64,6 +69,38 @@ export function useFormInitial(
|
||||
}
|
||||
return mergeWithArrayOverride(initialValues, zodDefaults);
|
||||
}
|
||||
// 自定义默认值提取逻辑
|
||||
function getCustomDefaultValue(rule: any): any {
|
||||
if (rule instanceof ZodString) {
|
||||
return ''; // 默认为空字符串
|
||||
} else if (rule instanceof ZodNumber) {
|
||||
return null; // 默认为 null(避免显示 0)
|
||||
} else if (rule instanceof ZodObject) {
|
||||
// 递归提取嵌套对象的默认值
|
||||
const defaultValues: Record<string, any> = {};
|
||||
for (const [key, valueSchema] of Object.entries(rule.shape)) {
|
||||
defaultValues[key] = getCustomDefaultValue(valueSchema);
|
||||
}
|
||||
return defaultValues;
|
||||
} else if (rule instanceof ZodIntersection) {
|
||||
// 对于交集类型,从schema 提取默认值
|
||||
const leftDefaultValue = getCustomDefaultValue(rule._def.left);
|
||||
const rightDefaultValue = getCustomDefaultValue(rule._def.right);
|
||||
|
||||
// 如果左右两边都能提取默认值,合并它们
|
||||
if (
|
||||
typeof leftDefaultValue === 'object' &&
|
||||
typeof rightDefaultValue === 'object'
|
||||
) {
|
||||
return { ...leftDefaultValue, ...rightDefaultValue };
|
||||
}
|
||||
|
||||
// 否则优先使用左边的默认值
|
||||
return leftDefaultValue ?? rightDefaultValue;
|
||||
} else {
|
||||
return undefined; // 其他类型不提供默认值
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
delegatedSlots,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/layout-ui",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -64,7 +64,7 @@ const logoStyle = computed((): CSSProperties => {
|
||||
<header
|
||||
:class="theme"
|
||||
:style="style"
|
||||
class="app-header border-border bg-header top-0 flex w-full flex-[0_0_auto] items-center border-b pl-2 transition-[margin-top] duration-200"
|
||||
class="border-border bg-header top-0 flex w-full flex-[0_0_auto] items-center border-b pl-2 transition-[margin-top] duration-200"
|
||||
>
|
||||
<div v-if="slots.logo" :style="logoStyle">
|
||||
<slot name="logo"></slot>
|
||||
@@ -75,17 +75,3 @@ const logoStyle = computed((): CSSProperties => {
|
||||
<slot></slot>
|
||||
</header>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.bg-header {
|
||||
app-region: drag;
|
||||
user-select: none;
|
||||
|
||||
:deep(.cursor-pointer),
|
||||
:deep(.vben-sub-menu),
|
||||
:deep(.vben-menu-item),
|
||||
:deep(button),
|
||||
:deep(a) {
|
||||
app-region: no-drag;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/menu-ui",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -34,7 +34,6 @@ const props = withDefaults(defineProps<AlertProps>(), {
|
||||
bordered: true,
|
||||
buttonAlign: 'end',
|
||||
centered: true,
|
||||
containerClass: 'w-[520px]',
|
||||
});
|
||||
const emits = defineEmits(['closed', 'confirm', 'opened']);
|
||||
const open = defineModel<boolean>('open', { default: false });
|
||||
@@ -148,7 +147,7 @@ async function handleOpenChange(val: boolean) {
|
||||
:class="
|
||||
cn(
|
||||
containerClass,
|
||||
'left-0 right-0 mx-auto flex max-h-[80%] flex-col p-0 duration-300 sm:rounded-[var(--radius)] md:w-[520px] md:max-w-[80%]',
|
||||
'left-0 right-0 mx-auto flex max-h-[80%] flex-col p-0 duration-300 sm:w-[520px] sm:max-w-[80%] sm:rounded-[var(--radius)]',
|
||||
{
|
||||
'border-border border': bordered,
|
||||
'shadow-3xl': !bordered,
|
||||
|
@@ -94,8 +94,9 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
|
||||
injectData.options?.onOpenChange?.(isOpen);
|
||||
};
|
||||
|
||||
const onClosed = mergedOptions.onClosed;
|
||||
mergedOptions.onClosed = () => {
|
||||
options.onClosed?.();
|
||||
onClosed?.();
|
||||
if (mergedOptions.destroyOnClose) {
|
||||
injectData.reCreateModal?.();
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/shadcn-ui",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"#main": "./dist/index.mjs",
|
||||
"#module": "./dist/index.mjs",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
|
@@ -5,6 +5,8 @@ import type {
|
||||
AvatarRootProps,
|
||||
} from 'radix-vue';
|
||||
|
||||
import type { CSSProperties } from 'vue';
|
||||
|
||||
import type { ClassType } from '@vben-core/typings';
|
||||
|
||||
import { computed } from 'vue';
|
||||
@@ -16,6 +18,7 @@ interface Props extends AvatarFallbackProps, AvatarImageProps, AvatarRootProps {
|
||||
class?: ClassType;
|
||||
dot?: boolean;
|
||||
dotClass?: ClassType;
|
||||
fit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
||||
size?: number;
|
||||
}
|
||||
|
||||
@@ -28,6 +31,15 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
as: 'button',
|
||||
dot: false,
|
||||
dotClass: 'bg-green-500',
|
||||
fit: 'cover',
|
||||
});
|
||||
|
||||
const imageStyle = computed<CSSProperties>(() => {
|
||||
const { fit } = props;
|
||||
if (fit) {
|
||||
return { objectFit: fit };
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
const text = computed(() => {
|
||||
@@ -51,7 +63,7 @@ const rootStyle = computed(() => {
|
||||
class="relative flex flex-shrink-0 items-center"
|
||||
>
|
||||
<Avatar :class="props.class" class="size-full">
|
||||
<AvatarImage :alt="alt" :src="src" />
|
||||
<AvatarImage :alt="alt" :src="src" :style="imageStyle" />
|
||||
<AvatarFallback>{{ text }}</AvatarFallback>
|
||||
</Avatar>
|
||||
<span
|
||||
|
@@ -29,14 +29,25 @@ export type ValueType = boolean | number | string;
|
||||
|
||||
export interface VbenButtonGroupProps
|
||||
extends Pick<VbenButtonProps, 'disabled'> {
|
||||
/** 单选模式下允许清除选中 */
|
||||
allowClear?: boolean;
|
||||
/** 值改变前的回调 */
|
||||
beforeChange?: (
|
||||
value: ValueType,
|
||||
isChecked: boolean,
|
||||
) => boolean | PromiseLike<boolean | undefined> | undefined;
|
||||
/** 按钮样式 */
|
||||
btnClass?: any;
|
||||
/** 按钮间隔距离 */
|
||||
gap?: number;
|
||||
/** 多选模式下限制最多选择的数量。0表示不限制 */
|
||||
maxCount?: number;
|
||||
/** 是否允许多选 */
|
||||
multiple?: boolean;
|
||||
/** 选项 */
|
||||
options?: { [key: string]: any; label: CustomRenderType; value: ValueType }[];
|
||||
/** 显示图标 */
|
||||
showIcon?: boolean;
|
||||
/** 尺寸 */
|
||||
size?: 'large' | 'middle' | 'small';
|
||||
}
|
||||
|
@@ -19,6 +19,8 @@ const props = withDefaults(defineProps<VbenButtonGroupProps>(), {
|
||||
multiple: false,
|
||||
showIcon: true,
|
||||
size: 'middle',
|
||||
allowClear: false,
|
||||
maxCount: 0,
|
||||
});
|
||||
const emit = defineEmits(['btnClick']);
|
||||
const btnDefaultProps = computed(() => {
|
||||
@@ -82,12 +84,22 @@ async function onBtnClick(value: ValueType) {
|
||||
if (innerValue.value.includes(value)) {
|
||||
innerValue.value = innerValue.value.filter((item) => item !== value);
|
||||
} else {
|
||||
if (props.maxCount > 0 && innerValue.value.length >= props.maxCount) {
|
||||
innerValue.value = innerValue.value.slice(0, props.maxCount - 1);
|
||||
}
|
||||
innerValue.value.push(value);
|
||||
}
|
||||
modelValue.value = innerValue.value;
|
||||
} else {
|
||||
innerValue.value = [value];
|
||||
modelValue.value = value;
|
||||
if (props.allowClear && innerValue.value.includes(value)) {
|
||||
innerValue.value = [];
|
||||
modelValue.value = undefined;
|
||||
emit('btnClick', undefined);
|
||||
return;
|
||||
} else {
|
||||
innerValue.value = [value];
|
||||
modelValue.value = value;
|
||||
}
|
||||
}
|
||||
emit('btnClick', value);
|
||||
}
|
||||
@@ -112,12 +124,18 @@ async function onBtnClick(value: ValueType) {
|
||||
@click="onBtnClick(btn.value)"
|
||||
>
|
||||
<div class="icon-wrapper" v-if="props.showIcon">
|
||||
<LoaderCircle
|
||||
class="animate-spin"
|
||||
v-if="loadingValues.includes(btn.value)"
|
||||
/>
|
||||
<CircleCheckBig v-else-if="innerValue.includes(btn.value)" />
|
||||
<Circle v-else />
|
||||
<slot
|
||||
name="icon"
|
||||
:loading="loadingValues.includes(btn.value)"
|
||||
:checked="innerValue.includes(btn.value)"
|
||||
>
|
||||
<LoaderCircle
|
||||
class="animate-spin"
|
||||
v-if="loadingValues.includes(btn.value)"
|
||||
/>
|
||||
<CircleCheckBig v-else-if="innerValue.includes(btn.value)" />
|
||||
<Circle v-else />
|
||||
</slot>
|
||||
</div>
|
||||
<slot name="option" :label="btn.label" :value="btn.value" :data="btn">
|
||||
<VbenRenderContent :content="btn.label" />
|
||||
|
@@ -6,6 +6,10 @@ interface Props {
|
||||
* @zh_CN 是否收起文本
|
||||
*/
|
||||
collapsed?: boolean;
|
||||
/**
|
||||
* @zh_CN Logo 图片适应方式
|
||||
*/
|
||||
fit?: 'contain' | 'cover' | 'fill' | 'none' | 'scale-down';
|
||||
/**
|
||||
* @zh_CN Logo 跳转地址
|
||||
*/
|
||||
@@ -38,6 +42,7 @@ withDefaults(defineProps<Props>(), {
|
||||
logoSize: 32,
|
||||
src: '',
|
||||
theme: 'light',
|
||||
fit: 'cover',
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -53,6 +58,7 @@ withDefaults(defineProps<Props>(), {
|
||||
:alt="text"
|
||||
:src="src"
|
||||
:size="logoSize"
|
||||
:fit="fit"
|
||||
class="relative rounded-none bg-transparent"
|
||||
/>
|
||||
<template v-if="!collapsed">
|
||||
|
@@ -80,7 +80,7 @@ defineExpose({
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'z-popup bg-background w-full p-6 shadow-lg outline-none sm:rounded-xl',
|
||||
'z-popup bg-background p-6 shadow-lg outline-none sm:rounded-xl',
|
||||
'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
|
||||
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
|
||||
{
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben-core/tabs-ui",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/constants",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/access",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -96,6 +96,15 @@ async function generateRoutes(
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'mixed': {
|
||||
const [frontend_resultRoutes, backend_resultRoutes] = await Promise.all([
|
||||
generateRoutesByFrontend(routes, roles || [], forbiddenComponent),
|
||||
generateRoutesByBackend(options),
|
||||
]);
|
||||
|
||||
resultRoutes = [...frontend_resultRoutes, ...backend_resultRoutes];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/common-ui",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,7 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import type { CSSProperties } from 'vue';
|
||||
|
||||
import { computed, ref, watchEffect } from 'vue';
|
||||
import {
|
||||
computed,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
onUpdated,
|
||||
ref,
|
||||
watchEffect,
|
||||
} from 'vue';
|
||||
|
||||
import { VbenTooltip } from '@vben-core/shadcn-ui';
|
||||
|
||||
@@ -33,6 +40,16 @@ interface Props {
|
||||
* @default true
|
||||
*/
|
||||
tooltip?: boolean;
|
||||
/**
|
||||
* 是否只在文本被截断时显示提示框
|
||||
* @default false
|
||||
*/
|
||||
tooltipWhenEllipsis?: boolean;
|
||||
/**
|
||||
* 文本截断检测的像素差异阈值,越大则判断越严格
|
||||
* @default 3
|
||||
*/
|
||||
ellipsisThreshold?: number;
|
||||
/**
|
||||
* 提示框背景颜色,优先级高于 overlayStyle
|
||||
*/
|
||||
@@ -62,12 +79,15 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
maxWidth: '100%',
|
||||
placement: 'top',
|
||||
tooltip: true,
|
||||
tooltipWhenEllipsis: false,
|
||||
ellipsisThreshold: 3,
|
||||
tooltipBackgroundColor: '',
|
||||
tooltipColor: '',
|
||||
tooltipFontSize: 14,
|
||||
tooltipMaxWidth: undefined,
|
||||
tooltipOverlayStyle: () => ({ textAlign: 'justify' }),
|
||||
});
|
||||
|
||||
const emit = defineEmits<{ expandChange: [boolean] }>();
|
||||
|
||||
const textMaxWidth = computed(() => {
|
||||
@@ -79,9 +99,67 @@ const textMaxWidth = computed(() => {
|
||||
const ellipsis = ref();
|
||||
const isExpand = ref(false);
|
||||
const defaultTooltipMaxWidth = ref();
|
||||
const isEllipsis = ref(false);
|
||||
|
||||
const { width: eleWidth } = useElementSize(ellipsis);
|
||||
|
||||
// 检测文本是否被截断
|
||||
const checkEllipsis = () => {
|
||||
if (!ellipsis.value || !props.tooltipWhenEllipsis) return;
|
||||
|
||||
const element = ellipsis.value;
|
||||
|
||||
const originalText = element.textContent || '';
|
||||
const originalTrimmed = originalText.trim();
|
||||
|
||||
// 对于空文本直接返回 false
|
||||
if (!originalTrimmed) {
|
||||
isEllipsis.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const widthDiff = element.scrollWidth - element.clientWidth;
|
||||
const heightDiff = element.scrollHeight - element.clientHeight;
|
||||
|
||||
// 使用足够大的差异阈值确保只有真正被截断的文本才会显示 tooltip
|
||||
isEllipsis.value =
|
||||
props.line === 1
|
||||
? widthDiff > props.ellipsisThreshold
|
||||
: heightDiff > props.ellipsisThreshold;
|
||||
};
|
||||
|
||||
// 使用 ResizeObserver 监听尺寸变化
|
||||
let resizeObserver: null | ResizeObserver = null;
|
||||
|
||||
onMounted(() => {
|
||||
if (typeof ResizeObserver !== 'undefined' && props.tooltipWhenEllipsis) {
|
||||
resizeObserver = new ResizeObserver(() => {
|
||||
checkEllipsis();
|
||||
});
|
||||
|
||||
if (ellipsis.value) {
|
||||
resizeObserver.observe(ellipsis.value);
|
||||
}
|
||||
}
|
||||
|
||||
// 初始检测
|
||||
checkEllipsis();
|
||||
});
|
||||
|
||||
// 使用onUpdated钩子检测内容变化
|
||||
onUpdated(() => {
|
||||
if (props.tooltipWhenEllipsis) {
|
||||
checkEllipsis();
|
||||
}
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (resizeObserver) {
|
||||
resizeObserver.disconnect();
|
||||
resizeObserver = null;
|
||||
}
|
||||
});
|
||||
|
||||
watchEffect(
|
||||
() => {
|
||||
if (props.tooltip && eleWidth.value) {
|
||||
@@ -91,9 +169,13 @@ watchEffect(
|
||||
},
|
||||
{ flush: 'post' },
|
||||
);
|
||||
|
||||
function onExpand() {
|
||||
isExpand.value = !isExpand.value;
|
||||
emit('expandChange', isExpand.value);
|
||||
if (props.tooltipWhenEllipsis) {
|
||||
checkEllipsis();
|
||||
}
|
||||
}
|
||||
|
||||
function handleExpand() {
|
||||
@@ -110,7 +192,9 @@ function handleExpand() {
|
||||
color: tooltipColor,
|
||||
backgroundColor: tooltipBackgroundColor,
|
||||
}"
|
||||
:disabled="!props.tooltip || isExpand"
|
||||
:disabled="
|
||||
!props.tooltip || isExpand || (props.tooltipWhenEllipsis && !isEllipsis)
|
||||
"
|
||||
:side="placement"
|
||||
>
|
||||
<slot name="tooltip">
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/hooks",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -193,67 +193,107 @@ export function useElementPlusDesignTokens() {
|
||||
|
||||
'--el-border-radius-base': getCssVariableValue('--radius', false),
|
||||
'--el-color-danger': getCssVariableValue('--destructive-500'),
|
||||
'--el-color-danger-dark-2': getCssVariableValue('--destructive'),
|
||||
'--el-color-danger-light-3': getCssVariableValue('--destructive-400'),
|
||||
'--el-color-danger-light-5': getCssVariableValue('--destructive-300'),
|
||||
'--el-color-danger-light-7': getCssVariableValue('--destructive-200'),
|
||||
'--el-color-danger-dark-2': isDark.value
|
||||
? getCssVariableValue('--destructive-400')
|
||||
: getCssVariableValue('--destructive-600'),
|
||||
'--el-color-danger-light-3': isDark.value
|
||||
? getCssVariableValue('--destructive-600')
|
||||
: getCssVariableValue('--destructive-400'),
|
||||
'--el-color-danger-light-5': isDark.value
|
||||
? getCssVariableValue('--destructive-700')
|
||||
: getCssVariableValue('--destructive-300'),
|
||||
'--el-color-danger-light-7': isDark.value
|
||||
? getCssVariableValue('--destructive-800')
|
||||
: getCssVariableValue('--destructive-200'),
|
||||
'--el-color-danger-light-8': isDark.value
|
||||
? border
|
||||
? getCssVariableValue('--destructive-900')
|
||||
: getCssVariableValue('--destructive-100'),
|
||||
'--el-color-danger-light-9': isDark.value
|
||||
? accent
|
||||
? getCssVariableValue('--destructive-950')
|
||||
: getCssVariableValue('--destructive-50'),
|
||||
|
||||
'--el-color-error': getCssVariableValue('--destructive-500'),
|
||||
'--el-color-error-dark-2': getCssVariableValue('--destructive'),
|
||||
'--el-color-error-light-3': getCssVariableValue('--destructive-400'),
|
||||
'--el-color-error-light-5': getCssVariableValue('--destructive-300'),
|
||||
'--el-color-error-light-7': getCssVariableValue('--destructive-200'),
|
||||
'--el-color-error-dark-2': isDark.value
|
||||
? getCssVariableValue('--destructive-400')
|
||||
: getCssVariableValue('--destructive-600'),
|
||||
'--el-color-error-light-3': isDark.value
|
||||
? getCssVariableValue('--destructive-600')
|
||||
: getCssVariableValue('--destructive-400'),
|
||||
'--el-color-error-light-5': isDark.value
|
||||
? getCssVariableValue('--destructive-700')
|
||||
: getCssVariableValue('--destructive-300'),
|
||||
'--el-color-error-light-7': isDark.value
|
||||
? getCssVariableValue('--destructive-800')
|
||||
: getCssVariableValue('--destructive-200'),
|
||||
'--el-color-error-light-8': isDark.value
|
||||
? border
|
||||
? getCssVariableValue('--destructive-900')
|
||||
: getCssVariableValue('--destructive-100'),
|
||||
'--el-color-error-light-9': isDark.value
|
||||
? accent
|
||||
? getCssVariableValue('--destructive-950')
|
||||
: getCssVariableValue('--destructive-50'),
|
||||
|
||||
'--el-color-info-light-5': border,
|
||||
'--el-color-info-light-8': border,
|
||||
'--el-color-info-light-9': getCssVariableValue('--info'), // getCssVariableValue('--secondary'),
|
||||
|
||||
'--el-color-primary': getCssVariableValue('--primary-500'),
|
||||
'--el-color-primary-dark-2': getCssVariableValue('--primary'),
|
||||
'--el-color-primary-light-3': getCssVariableValue('--primary-400'),
|
||||
'--el-color-primary-light-5': getCssVariableValue('--primary-300'),
|
||||
'--el-color-primary-dark-2': isDark.value
|
||||
? getCssVariableValue('--primary-400')
|
||||
: getCssVariableValue('--primary-600'),
|
||||
'--el-color-primary-light-3': isDark.value
|
||||
? getCssVariableValue('--primary-600')
|
||||
: getCssVariableValue('--primary-400'),
|
||||
'--el-color-primary-light-5': isDark.value
|
||||
? getCssVariableValue('--primary-700')
|
||||
: getCssVariableValue('--primary-300'),
|
||||
'--el-color-primary-light-7': isDark.value
|
||||
? border
|
||||
? getCssVariableValue('--primary-800')
|
||||
: getCssVariableValue('--primary-200'),
|
||||
'--el-color-primary-light-8': isDark.value
|
||||
? border
|
||||
? getCssVariableValue('--primary-900')
|
||||
: getCssVariableValue('--primary-100'),
|
||||
'--el-color-primary-light-9': isDark.value
|
||||
? accent
|
||||
? getCssVariableValue('--primary-950')
|
||||
: getCssVariableValue('--primary-50'),
|
||||
|
||||
'--el-color-success': getCssVariableValue('--success-500'),
|
||||
'--el-color-success-dark-2': getCssVariableValue('--success'),
|
||||
'--el-color-success-light-3': getCssVariableValue('--success-400'),
|
||||
'--el-color-success-light-5': getCssVariableValue('--success-300'),
|
||||
'--el-color-success-light-7': getCssVariableValue('--success-200'),
|
||||
'--el-color-success-dark-2': isDark.value
|
||||
? getCssVariableValue('--success-400')
|
||||
: getCssVariableValue('--success-600'),
|
||||
'--el-color-success-light-3': isDark.value
|
||||
? getCssVariableValue('--success-600')
|
||||
: getCssVariableValue('--success-400'),
|
||||
'--el-color-success-light-5': isDark.value
|
||||
? getCssVariableValue('--success-700')
|
||||
: getCssVariableValue('--success-300'),
|
||||
'--el-color-success-light-7': isDark.value
|
||||
? getCssVariableValue('--success-800')
|
||||
: getCssVariableValue('--success-200'),
|
||||
'--el-color-success-light-8': isDark.value
|
||||
? border
|
||||
? getCssVariableValue('--success-900')
|
||||
: getCssVariableValue('--success-100'),
|
||||
'--el-color-success-light-9': isDark.value
|
||||
? accent
|
||||
? getCssVariableValue('--success-950')
|
||||
: getCssVariableValue('--success-50'),
|
||||
|
||||
'--el-color-warning': getCssVariableValue('--warning-500'),
|
||||
'--el-color-warning-dark-2': getCssVariableValue('--warning'),
|
||||
'--el-color-warning-light-3': getCssVariableValue('--warning-400'),
|
||||
'--el-color-warning-light-5': getCssVariableValue('--warning-300'),
|
||||
'--el-color-warning-light-7': getCssVariableValue('--warning-200'),
|
||||
'--el-color-warning-dark-2': isDark.value
|
||||
? getCssVariableValue('--warning-400')
|
||||
: getCssVariableValue('--warning-600'),
|
||||
'--el-color-warning-light-3': isDark.value
|
||||
? getCssVariableValue('--warning-600')
|
||||
: getCssVariableValue('--warning-400'),
|
||||
'--el-color-warning-light-5': isDark.value
|
||||
? getCssVariableValue('--warning-700')
|
||||
: getCssVariableValue('--warning-300'),
|
||||
'--el-color-warning-light-7': isDark.value
|
||||
? getCssVariableValue('--warning-800')
|
||||
: getCssVariableValue('--warning-200'),
|
||||
'--el-color-warning-light-8': isDark.value
|
||||
? border
|
||||
? getCssVariableValue('--warning-900')
|
||||
: getCssVariableValue('--warning-100'),
|
||||
'--el-color-warning-light-9': isDark.value
|
||||
? accent
|
||||
? getCssVariableValue('--warning-950')
|
||||
: getCssVariableValue('--warning-50'),
|
||||
|
||||
'--el-fill-color': getCssVariableValue('--accent'),
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/layouts",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
@@ -39,8 +39,5 @@
|
||||
"@vueuse/core": "catalog:",
|
||||
"vue": "catalog:",
|
||||
"vue-router": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vben-core/typings": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
@@ -6,9 +6,6 @@ import { computed } from 'vue';
|
||||
import { preferences } from '@vben/preferences';
|
||||
|
||||
import {
|
||||
AppClose,
|
||||
AppMaxmize,
|
||||
AppMinmize,
|
||||
AuthenticationColorToggle,
|
||||
AuthenticationLayoutToggle,
|
||||
LanguageToggle,
|
||||
@@ -31,8 +28,6 @@ const showColor = computed(() => props.toolbarList.includes('color'));
|
||||
const showLayout = computed(() => props.toolbarList.includes('layout'));
|
||||
const showLanguage = computed(() => props.toolbarList.includes('language'));
|
||||
const showTheme = computed(() => props.toolbarList.includes('theme'));
|
||||
|
||||
const isElectron = window?.ipcRenderer !== undefined;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -50,10 +45,5 @@ const isElectron = window?.ipcRenderer !== undefined;
|
||||
<!-- Always show Language and Theme toggles -->
|
||||
<LanguageToggle v-if="showLanguage && preferences.widget.languageToggle" />
|
||||
<ThemeToggle v-if="showTheme && preferences.widget.themeToggle" />
|
||||
<template v-if="isElectron">
|
||||
<AppMinmize />
|
||||
<AppMaxmize />
|
||||
<AppClose />
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -9,9 +9,6 @@ import { useAccessStore } from '@vben/stores';
|
||||
import { VbenFullScreen, VbenIconButton } from '@vben-core/shadcn-ui';
|
||||
|
||||
import {
|
||||
AppClose,
|
||||
AppMaxmize,
|
||||
AppMinmize,
|
||||
GlobalSearch,
|
||||
LanguageToggle,
|
||||
PreferencesButton,
|
||||
@@ -44,13 +41,6 @@ const { refresh } = useRefresh();
|
||||
|
||||
const rightSlots = computed(() => {
|
||||
const list = [{ index: REFERENCE_VALUE + 100, name: 'user-dropdown' }];
|
||||
if (window.ipcRenderer) {
|
||||
list.push(
|
||||
{ index: REFERENCE_VALUE + 110, name: 'app-minimize' },
|
||||
{ index: REFERENCE_VALUE + 120, name: 'app-maximize' },
|
||||
{ index: REFERENCE_VALUE + 120, name: 'app-close' },
|
||||
);
|
||||
}
|
||||
if (preferences.widget.globalSearch) {
|
||||
list.push({
|
||||
index: REFERENCE_VALUE,
|
||||
@@ -176,15 +166,6 @@ function clearPreferencesAndLogout() {
|
||||
<template v-else-if="slot.name === 'fullscreen'">
|
||||
<VbenFullScreen class="mr-1" />
|
||||
</template>
|
||||
<template v-else-if="slot.name === 'app-minimize'">
|
||||
<AppMinmize class="mr-1" />
|
||||
</template>
|
||||
<template v-else-if="slot.name === 'app-maximize'">
|
||||
<AppMaxmize class="mr-1" />
|
||||
</template>
|
||||
<template v-else-if="slot.name === 'app-close'">
|
||||
<AppClose class="mr-1" />
|
||||
</template>
|
||||
</slot>
|
||||
</template>
|
||||
</div>
|
||||
|
@@ -180,8 +180,16 @@ const headerSlots = computed(() => {
|
||||
<VbenAdminLayout
|
||||
v-model:sidebar-extra-visible="sidebarExtraVisible"
|
||||
:content-compact="preferences.app.contentCompact"
|
||||
:content-compact-width="preferences.app.contentCompactWidth"
|
||||
:content-padding="preferences.app.contentPadding"
|
||||
:content-padding-bottom="preferences.app.contentPaddingBottom"
|
||||
:content-padding-left="preferences.app.contentPaddingLeft"
|
||||
:content-padding-right="preferences.app.contentPaddingRight"
|
||||
:content-padding-top="preferences.app.contentPaddingTop"
|
||||
:footer-enable="preferences.footer.enable"
|
||||
:footer-fixed="preferences.footer.fixed"
|
||||
:footer-height="preferences.footer.height"
|
||||
:header-height="preferences.header.height"
|
||||
:header-hidden="preferences.header.hidden"
|
||||
:header-mode="preferences.header.mode"
|
||||
:header-theme="headerTheme"
|
||||
@@ -196,11 +204,15 @@ const headerSlots = computed(() => {
|
||||
:sidebar-fixed-button="preferences.sidebar.fixedButton"
|
||||
:sidebar-expand-on-hover="preferences.sidebar.expandOnHover"
|
||||
:sidebar-extra-collapse="preferences.sidebar.extraCollapse"
|
||||
:sidebar-extra-collapsed-width="preferences.sidebar.extraCollapsedWidth"
|
||||
:sidebar-hidden="preferences.sidebar.hidden"
|
||||
:sidebar-mixed-width="preferences.sidebar.mixedWidth"
|
||||
:sidebar-theme="sidebarTheme"
|
||||
:sidebar-width="preferences.sidebar.width"
|
||||
:side-collapse-width="preferences.sidebar.collapseWidth"
|
||||
:tabbar-enable="preferences.tabbar.enable"
|
||||
:tabbar-height="preferences.tabbar.height"
|
||||
:z-index="preferences.app.zIndex"
|
||||
@side-mouse-leave="handleSideMouseLeave"
|
||||
@toggle-sidebar="toggleSidebar"
|
||||
@update:sidebar-collapse="
|
||||
@@ -222,6 +234,7 @@ const headerSlots = computed(() => {
|
||||
<template #logo>
|
||||
<VbenLogo
|
||||
v-if="preferences.logo.enable"
|
||||
:fit="preferences.logo.fit"
|
||||
:class="logoClass"
|
||||
:collapsed="logoCollapsed"
|
||||
:src="preferences.logo.source"
|
||||
@@ -312,6 +325,7 @@ const headerSlots = computed(() => {
|
||||
<template #side-extra-title>
|
||||
<VbenLogo
|
||||
v-if="preferences.logo.enable"
|
||||
:fit="preferences.logo.fit"
|
||||
:text="preferences.app.name"
|
||||
:theme="theme"
|
||||
>
|
||||
|
@@ -1,16 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { X } from '@vben/icons';
|
||||
|
||||
import { VbenIconButton } from '@vben-core/shadcn-ui';
|
||||
|
||||
function handleAppClose() {
|
||||
window.ipcRenderer?.invoke('app-close');
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex-center mr-2 h-full" @click.stop="handleAppClose()">
|
||||
<VbenIconButton class="bell-button text-foreground relative">
|
||||
<X class="size-4" />
|
||||
</VbenIconButton>
|
||||
</div>
|
||||
</template>
|
@@ -1 +0,0 @@
|
||||
export { default as AppClose } from './app-close.vue';
|
@@ -1,30 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
|
||||
import { SquareArrowDownLeft, SquareArrowUpRight } from '@vben/icons';
|
||||
|
||||
import { VbenIconButton } from '@vben-core/shadcn-ui';
|
||||
|
||||
const isMaximized = ref(false);
|
||||
|
||||
if (window.ipcRenderer) {
|
||||
onMounted(async () => {
|
||||
isMaximized.value = await window.ipcRenderer.invoke('is-maximized');
|
||||
window.ipcRenderer.on('maximize-changed', (_, maximized) => {
|
||||
isMaximized.value = maximized;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function handleAppMaximize() {
|
||||
window.ipcRenderer?.invoke('app-maximize');
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex-center mr-2 h-full" @click.stop="handleAppMaximize()">
|
||||
<VbenIconButton class="bell-button text-foreground relative">
|
||||
<SquareArrowDownLeft v-if="isMaximized" class="size-4" />
|
||||
<SquareArrowUpRight v-else class="size-4" />
|
||||
</VbenIconButton>
|
||||
</div>
|
||||
</template>
|
@@ -1 +0,0 @@
|
||||
export { default as AppMaxmize } from './app-maximize.vue';
|
@@ -1,16 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { Minus } from '@vben/icons';
|
||||
|
||||
import { VbenIconButton } from '@vben-core/shadcn-ui';
|
||||
|
||||
function handleAppMinimize() {
|
||||
window.ipcRenderer?.invoke('app-minimize');
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex-center mr-2 h-full" @click.stop="handleAppMinimize()">
|
||||
<VbenIconButton class="bell-button text-foreground relative">
|
||||
<Minus class="size-4" />
|
||||
</VbenIconButton>
|
||||
</div>
|
||||
</template>
|
@@ -1 +0,0 @@
|
||||
export { default as AppMinmize } from './app-minimize.vue';
|
@@ -6,7 +6,7 @@ import { $t } from '@vben/locales';
|
||||
import { useVbenModal } from '@vben-core/popup-ui';
|
||||
|
||||
interface Props {
|
||||
// 轮训时间,分钟
|
||||
// 轮询时间,分钟
|
||||
checkUpdatesInterval?: number;
|
||||
// 检查更新的地址
|
||||
checkUpdateUrl?: string;
|
||||
@@ -46,6 +46,7 @@ async function getVersionTag() {
|
||||
const response = await fetch(props.checkUpdateUrl, {
|
||||
cache: 'no-cache',
|
||||
method: 'HEAD',
|
||||
redirect: 'manual',
|
||||
});
|
||||
|
||||
return (
|
||||
|
@@ -1,6 +1,3 @@
|
||||
export * from './app-close';
|
||||
export * from './app-maximize';
|
||||
export * from './app-minimize';
|
||||
export { default as Breadcrumb } from './breadcrumb.vue';
|
||||
export * from './check-updates';
|
||||
export { default as AuthenticationColorToggle } from './color-toggle.vue';
|
||||
|
@@ -46,7 +46,11 @@ interface Props {
|
||||
/**
|
||||
* 菜单数组
|
||||
*/
|
||||
menus?: Array<{ handler: AnyFunction; icon?: Component; text: string }>;
|
||||
menus?: Array<{
|
||||
handler: AnyFunction;
|
||||
icon?: Component | Function | string;
|
||||
text: string;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* 标签文本
|
||||
|
@@ -1,9 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/tsconfig/web.json",
|
||||
"compilerOptions": {
|
||||
"types": ["@vben-core/typings/electron"]
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
@@ -3,4 +3,8 @@ export type { VxeTableGridOptions } from './types';
|
||||
export * from './use-vxe-grid';
|
||||
|
||||
export { default as VbenVxeGrid } from './use-vxe-grid.vue';
|
||||
export type { VxeGridListeners, VxeGridProps } from 'vxe-table';
|
||||
export type {
|
||||
VxeGridListeners,
|
||||
VxeGridProps,
|
||||
VxeGridPropTypes,
|
||||
} from 'vxe-table';
|
||||
|
@@ -59,6 +59,7 @@ const FORM_SLOT_PREFIX = 'form-';
|
||||
|
||||
const TOOLBAR_ACTIONS = 'toolbar-actions';
|
||||
const TOOLBAR_TOOLS = 'toolbar-tools';
|
||||
const TABLE_TITLE = 'table-title';
|
||||
|
||||
const gridRef = useTemplateRef<VxeGridInstance>('gridRef');
|
||||
|
||||
@@ -129,7 +130,7 @@ const [Form, formApi] = useTableForm({
|
||||
});
|
||||
|
||||
const showTableTitle = computed(() => {
|
||||
return !!slots.tableTitle?.() || tableTitle.value;
|
||||
return !!slots[TABLE_TITLE]?.() || tableTitle.value;
|
||||
});
|
||||
|
||||
const showToolbar = computed(() => {
|
||||
@@ -277,6 +278,15 @@ const delegatedFormSlots = computed(() => {
|
||||
return resultSlots.map((key) => key.replace(FORM_SLOT_PREFIX, ''));
|
||||
});
|
||||
|
||||
const showDefaultEmpty = computed(() => {
|
||||
// 检查是否有原生的 VXE Table 空状态配置
|
||||
const hasEmptyText = options.value.emptyText !== undefined;
|
||||
const hasEmptyRender = options.value.emptyRender !== undefined;
|
||||
|
||||
// 如果有原生配置,就不显示默认的空状态
|
||||
return !hasEmptyText && !hasEmptyRender;
|
||||
});
|
||||
|
||||
async function init() {
|
||||
await nextTick();
|
||||
const globalGridConfig = VxeUI?.getConfig()?.grid ?? {};
|
||||
@@ -458,7 +468,7 @@ onUnmounted(() => {
|
||||
</slot>
|
||||
</template>
|
||||
<!-- 统一控状态 -->
|
||||
<template #empty>
|
||||
<template v-if="showDefaultEmpty" #empty>
|
||||
<slot name="empty">
|
||||
<EmptyIcon class="mx-auto" />
|
||||
<div class="mt-2">{{ $t('common.noData') }}</div>
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/request",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/icons",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/locales",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/preferences",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/stores",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -208,7 +208,7 @@ export const useTabbarStore = defineStore('core-tabbar', {
|
||||
const keys: string[] = [];
|
||||
|
||||
for (const key of closeKeys) {
|
||||
if (key !== tab.key) {
|
||||
if (key !== getTabKeyFromTab(tab)) {
|
||||
const closeTab = this.tabs.find(
|
||||
(item) => getTabKeyFromTab(item) === key,
|
||||
);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/styles",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/types",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/utils",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
|
@@ -4,11 +4,5 @@ VITE_APP_TITLE=Vben Admin
|
||||
# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
|
||||
VITE_APP_NAMESPACE=vben-web-play
|
||||
|
||||
# vue-router 的模式
|
||||
VITE_ROUTER_HISTORY=hash
|
||||
|
||||
# 对store进行加密的密钥,在将store持久化到localStorage时会使用该密钥进行加密
|
||||
VITE_APP_STORE_SECURE_KEY=please-replace-me-with-your-own-key
|
||||
|
||||
# vue-router 的模式
|
||||
VITE_ROUTER_HISTORY=hash
|
||||
|
@@ -1,4 +1,4 @@
|
||||
VITE_BASE=./
|
||||
VITE_BASE=/
|
||||
|
||||
# 接口地址
|
||||
VITE_GLOB_API_URL=https://mock-napi.vben.pro/api
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 66 KiB |
Binary file not shown.
Before Width: | Height: | Size: 47 KiB |
@@ -1,192 +0,0 @@
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import {
|
||||
app,
|
||||
BrowserWindow,
|
||||
globalShortcut,
|
||||
ipcMain,
|
||||
Menu,
|
||||
shell,
|
||||
} from 'electron';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
process.env.APP_ROOT = path.join(__dirname, '../..');
|
||||
|
||||
export const MAIN_DIST = path.join(process.env.APP_ROOT, 'dist-electron');
|
||||
export const RENDERER_DIST = path.join(process.env.APP_ROOT, 'dist');
|
||||
export const VITE_DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL;
|
||||
|
||||
process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL
|
||||
? path.join(process.env.APP_ROOT, 'public')
|
||||
: RENDERER_DIST;
|
||||
|
||||
// Disable GPU Acceleration for Windows 7
|
||||
if (os.release().startsWith('6.1')) app.disableHardwareAcceleration();
|
||||
|
||||
// Set application name for Windows 10+ notifications
|
||||
if (process.platform === 'win32') app.setAppUserModelId(app.getName());
|
||||
|
||||
if (!app.requestSingleInstanceLock()) {
|
||||
app.quit();
|
||||
}
|
||||
|
||||
let win: BrowserWindow | null = null;
|
||||
const preload = path.join(__dirname, '../preload/preload.mjs');
|
||||
const indexHtml = path.join(RENDERER_DIST, 'index.html');
|
||||
|
||||
async function createWindow() {
|
||||
win = new BrowserWindow({
|
||||
autoHideMenuBar: true,
|
||||
frame: false,
|
||||
height: 900,
|
||||
icon: path.join(process.env.VITE_PUBLIC as string, 'favicon.ico'),
|
||||
movable: true,
|
||||
show: false,
|
||||
title: 'Main window',
|
||||
webPreferences: {
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
preload,
|
||||
webSecurity: true,
|
||||
},
|
||||
width: 1440,
|
||||
});
|
||||
|
||||
// 监听窗口准备好显示的事件
|
||||
win.once('ready-to-show', () => {
|
||||
win?.maximize(); // 最大化窗口
|
||||
win?.show(); // 显示窗口
|
||||
});
|
||||
|
||||
win.on('maximize', () => {
|
||||
win?.webContents.send('maximize-changed', true);
|
||||
});
|
||||
|
||||
win.on('unmaximize', () => {
|
||||
win?.webContents.send('maximize-changed', false);
|
||||
});
|
||||
|
||||
if (VITE_DEV_SERVER_URL) {
|
||||
win.loadURL(VITE_DEV_SERVER_URL);
|
||||
} else {
|
||||
win.loadFile(indexHtml);
|
||||
}
|
||||
|
||||
// Test actively push message to the Electron-Renderer
|
||||
win.webContents.on('did-finish-load', () => {
|
||||
win?.webContents.send('main-process-message', new Date().toLocaleString());
|
||||
});
|
||||
|
||||
// Make all links open with the browser, not with the application
|
||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||
if (url.startsWith('https:')) shell.openExternal(url);
|
||||
return { action: 'deny' };
|
||||
});
|
||||
// win.webContents.on('will-navigate', (event, url) => { }) #344
|
||||
}
|
||||
Menu.setApplicationMenu(null);
|
||||
app
|
||||
.whenReady()
|
||||
.then(createWindow)
|
||||
.then(() => {
|
||||
// 禁用了菜单之后,默认的快捷键也会被禁用,这里重新注册部分常用快捷键
|
||||
if (VITE_DEV_SERVER_URL) {
|
||||
// 开发模式下监听快捷键来打开开发者工具
|
||||
globalShortcut.register('CmdOrCtrl+Shift+I', () => {
|
||||
BrowserWindow.getFocusedWindow()?.webContents.toggleDevTools();
|
||||
});
|
||||
}
|
||||
// 监听快捷键来刷新页面
|
||||
globalShortcut.registerAll(['CmdOrCtrl+R', 'CmdOrCtrl+F5'], () => {
|
||||
BrowserWindow.getFocusedWindow()?.webContents.reload();
|
||||
});
|
||||
// 监听快捷键来强制刷新页面
|
||||
globalShortcut.registerAll(
|
||||
['CmdOrCtrl+Shift+R', 'CmdOrCtrl+Shift+F5'],
|
||||
() => {
|
||||
BrowserWindow.getFocusedWindow()?.webContents.reloadIgnoringCache();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
app.on('window-all-closed', () => {
|
||||
win = null;
|
||||
if (process.platform !== 'darwin') app.quit();
|
||||
});
|
||||
|
||||
app.on('will-quit', () => {
|
||||
globalShortcut.unregisterAll();
|
||||
});
|
||||
|
||||
app.on('second-instance', () => {
|
||||
if (win) {
|
||||
// Focus on the main window if the user tried to open another
|
||||
if (win.isMinimized()) win.restore();
|
||||
win.focus();
|
||||
}
|
||||
});
|
||||
|
||||
app.on('activate', () => {
|
||||
const allWindows = BrowserWindow.getAllWindows();
|
||||
if (allWindows.length > 0) {
|
||||
allWindows[0].focus();
|
||||
} else {
|
||||
createWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// New window example arg: new windows url
|
||||
ipcMain.handle('open-win', (_, arg) => {
|
||||
const childWindow = new BrowserWindow({
|
||||
frame: false,
|
||||
webPreferences: {
|
||||
contextIsolation: true,
|
||||
nodeIntegration: true,
|
||||
preload,
|
||||
webviewTag: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (VITE_DEV_SERVER_URL) {
|
||||
childWindow.loadURL(`${VITE_DEV_SERVER_URL}#${arg}`);
|
||||
} else {
|
||||
childWindow.loadFile(indexHtml, { hash: arg });
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('app-minimize', (event) => {
|
||||
const browserWindow = BrowserWindow.fromWebContents(event.sender);
|
||||
if (browserWindow) {
|
||||
browserWindow.minimize();
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('app-maximize', (event) => {
|
||||
const browserWindow = BrowserWindow.fromWebContents(event.sender);
|
||||
if (browserWindow) {
|
||||
if (browserWindow.isMaximized()) {
|
||||
browserWindow.restore();
|
||||
} else {
|
||||
browserWindow.maximize();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('app-close', (event) => {
|
||||
const browserWindow = BrowserWindow.fromWebContents(event.sender);
|
||||
if (browserWindow) {
|
||||
browserWindow.close();
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('is-maximized', (event) => {
|
||||
const browserWindow = BrowserWindow.fromWebContents(event.sender);
|
||||
if (browserWindow) {
|
||||
return browserWindow.isMaximized();
|
||||
}
|
||||
return false;
|
||||
});
|
@@ -1,113 +0,0 @@
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
|
||||
// --------- Expose some API to the Renderer process ---------
|
||||
contextBridge.exposeInMainWorld('ipcRenderer', {
|
||||
invoke(...args: Parameters<typeof ipcRenderer.invoke>) {
|
||||
const [channel, ...omit] = args;
|
||||
return ipcRenderer.invoke(channel, ...omit);
|
||||
},
|
||||
off(...args: Parameters<typeof ipcRenderer.off>) {
|
||||
const [channel, ...omit] = args;
|
||||
return ipcRenderer.off(channel, ...omit);
|
||||
},
|
||||
on(...args: Parameters<typeof ipcRenderer.on>) {
|
||||
const [channel, listener] = args;
|
||||
return ipcRenderer.on(channel, (event, ...args) =>
|
||||
listener(event, ...args),
|
||||
);
|
||||
},
|
||||
send(...args: Parameters<typeof ipcRenderer.send>) {
|
||||
const [channel, ...omit] = args;
|
||||
return ipcRenderer.send(channel, ...omit);
|
||||
},
|
||||
// You can expose other APTs you need here.
|
||||
// ...
|
||||
});
|
||||
|
||||
// --------- Preload scripts loading ---------
|
||||
// function domReady(
|
||||
// condition: DocumentReadyState[] = ['complete', 'interactive'],
|
||||
// ) {
|
||||
// return new Promise((resolve) => {
|
||||
// if (condition.includes(document.readyState)) {
|
||||
// resolve(true);
|
||||
// } else {
|
||||
// document.addEventListener('readystatechange', () => {
|
||||
// if (condition.includes(document.readyState)) {
|
||||
// resolve(true);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// const safeDOM = {
|
||||
// append(parent: HTMLElement, child: HTMLElement) {
|
||||
// if (![...parent.children].includes(child)) {
|
||||
// return parent.append(child);
|
||||
// }
|
||||
// },
|
||||
// remove(parent: HTMLElement, child: HTMLElement) {
|
||||
// if ([...parent.children].includes(child)) {
|
||||
// return child.remove();
|
||||
// }
|
||||
// },
|
||||
// };
|
||||
|
||||
// function useLoading() {
|
||||
// const className = `loaders-css__square-spin`;
|
||||
// const styleContent = `
|
||||
// @keyframes square-spin {
|
||||
// 25% { transform: perspective(100px) rotateX(180deg) rotateY(0); }
|
||||
// 50% { transform: perspective(100px) rotateX(180deg) rotateY(180deg); }
|
||||
// 75% { transform: perspective(100px) rotateX(0) rotateY(180deg); }
|
||||
// 100% { transform: perspective(100px) rotateX(0) rotateY(0); }
|
||||
// }
|
||||
// .${className} > div {
|
||||
// animation-fill-mode: both;
|
||||
// width: 50px;
|
||||
// height: 50px;
|
||||
// background: #fff;
|
||||
// animation: square-spin 3s 0s cubic-bezier(0.09, 0.57, 0.49, 0.9) infinite;
|
||||
// }
|
||||
// .app-loading-wrap {
|
||||
// position: fixed;
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
// width: 100vw;
|
||||
// height: 100vh;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// justify-content: center;
|
||||
// background: #282c34;
|
||||
// z-index: 9;
|
||||
// }
|
||||
// `;
|
||||
// const oStyle = document.createElement('style');
|
||||
// const oDiv = document.createElement('div');
|
||||
|
||||
// oStyle.id = 'app-loading-style';
|
||||
// oStyle.innerHTML = styleContent;
|
||||
// oDiv.className = 'app-loading-wrap';
|
||||
// oDiv.innerHTML = `<div class="${className}"><div></div></div>`;
|
||||
|
||||
// return {
|
||||
// appendLoading() {
|
||||
// safeDOM.append(document.head, oStyle);
|
||||
// safeDOM.append(document.body, oDiv);
|
||||
// },
|
||||
// removeLoading() {
|
||||
// safeDOM.remove(document.head, oStyle);
|
||||
// safeDOM.remove(document.body, oDiv);
|
||||
// },
|
||||
// };
|
||||
// }
|
||||
|
||||
// const { appendLoading, removeLoading } = useLoading();
|
||||
// domReady().then(appendLoading);
|
||||
|
||||
// window.onmessage = (ev) => {
|
||||
// ev.data.payload === 'removeLoading' && removeLoading();
|
||||
// };
|
||||
|
||||
// setTimeout(removeLoading, 4999);
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vben/playground",
|
||||
"version": "5.5.6",
|
||||
"version": "5.5.7",
|
||||
"homepage": "https://vben.pro",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
@@ -16,61 +16,18 @@
|
||||
},
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "pnpm vite build --mode production && electron-builder",
|
||||
"build": "pnpm vite build --mode production",
|
||||
"build:analyze": "pnpm vite build --mode analyze",
|
||||
"dev": "cross-env ELECTRON_DISABLE_SECURITY_WARNINGS=true pnpm vite --mode development",
|
||||
"dev": "pnpm vite --mode development",
|
||||
"preview": "vite preview",
|
||||
"typecheck": "vue-tsc --noEmit --skipLibCheck",
|
||||
"test:e2e": "playwright test",
|
||||
"test:e2e-ui": "playwright test --ui",
|
||||
"test:e2e-codegen": "playwright codegen"
|
||||
},
|
||||
"main": "dist-electron/main/main.js",
|
||||
"debug": {
|
||||
"env": {
|
||||
"VITE_DEV_SERVER_URL": "http://127.0.0.1:5555/"
|
||||
}
|
||||
},
|
||||
"imports": {
|
||||
"#/*": "./src/*"
|
||||
},
|
||||
"build": {
|
||||
"productName": "VbenAdminPlayground",
|
||||
"appId": "pro.vben.playground",
|
||||
"copyright": "vben.pro © 2025",
|
||||
"compression": "maximum",
|
||||
"artifactName": "${productName}-v${version}-${platform}-${arch}.${ext}",
|
||||
"asar": true,
|
||||
"directories": {
|
||||
"output": "dist-electron/release"
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*",
|
||||
"dist-electron/**/*",
|
||||
"package.json",
|
||||
"!node_modules/**",
|
||||
"!dist-electron/release/**"
|
||||
],
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"perMachine": true,
|
||||
"deleteAppDataOnUninstall": true,
|
||||
"createDesktopShortcut": true,
|
||||
"createStartMenuShortcut": true,
|
||||
"shortcutName": "VbenAdmin"
|
||||
},
|
||||
"win": {
|
||||
"icon": "./electron/logo/logo_256.ico",
|
||||
"target": "nsis"
|
||||
},
|
||||
"mac": {
|
||||
"icon": "./electron/logo/logo_256.ico"
|
||||
},
|
||||
"linux": {
|
||||
"icon": "./electron/logo/logo_256.ico"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/vue-query": "catalog:",
|
||||
"@vben-core/menu-ui": "workspace:*",
|
||||
@@ -91,13 +48,12 @@
|
||||
"@vueuse/core": "catalog:",
|
||||
"ant-design-vue": "catalog:",
|
||||
"dayjs": "catalog:",
|
||||
"json-bigint": "catalog:",
|
||||
"pinia": "catalog:",
|
||||
"vue": "catalog:",
|
||||
"vue-router": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "catalog:",
|
||||
"electron": "catalog:",
|
||||
"electron-builder": "catalog:"
|
||||
"@types/json-bigint": "catalog:"
|
||||
}
|
||||
}
|
||||
|
@@ -8,38 +8,40 @@ import type { ComponentType } from './component';
|
||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
setupVbenForm<ComponentType>({
|
||||
config: {
|
||||
// ant design vue组件库默认都是 v-model:value
|
||||
baseModelPropName: 'value',
|
||||
// 一些组件是 v-model:checked 或者 v-model:fileList
|
||||
modelPropNameMap: {
|
||||
Checkbox: 'checked',
|
||||
Radio: 'checked',
|
||||
Switch: 'checked',
|
||||
Upload: 'fileList',
|
||||
async function initSetupVbenForm() {
|
||||
setupVbenForm<ComponentType>({
|
||||
config: {
|
||||
// ant design vue组件库默认都是 v-model:value
|
||||
baseModelPropName: 'value',
|
||||
// 一些组件是 v-model:checked 或者 v-model:fileList
|
||||
modelPropNameMap: {
|
||||
Checkbox: 'checked',
|
||||
Radio: 'checked',
|
||||
Switch: 'checked',
|
||||
Upload: 'fileList',
|
||||
},
|
||||
},
|
||||
},
|
||||
defineRules: {
|
||||
// 输入项目必填国际化适配
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('ui.formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
defineRules: {
|
||||
// 输入项目必填国际化适配
|
||||
required: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null || value.length === 0) {
|
||||
return $t('ui.formRules.required', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
// 选择项目必填国际化适配
|
||||
selectRequired: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null) {
|
||||
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
// 选择项目必填国际化适配
|
||||
selectRequired: (value, _params, ctx) => {
|
||||
if (value === undefined || value === null) {
|
||||
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const useVbenForm = useForm<ComponentType>;
|
||||
|
||||
export { useVbenForm, z };
|
||||
export { initSetupVbenForm, useVbenForm, z };
|
||||
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||
export type { VbenFormProps };
|
||||
|
10
playground/src/api/examples/json-bigint.ts
Normal file
10
playground/src/api/examples/json-bigint.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 发起请求
|
||||
*/
|
||||
async function getBigIntData() {
|
||||
return requestClient.get('/demo/bigint');
|
||||
}
|
||||
|
||||
export { getBigIntData };
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user