This commit is contained in:
dap 2024-08-27 08:14:19 +08:00
commit 1fe260a391
72 changed files with 374 additions and 138 deletions

34
.vscode/settings.json vendored
View File

@ -14,7 +14,6 @@
"editor.tabSize": 2, "editor.tabSize": 2,
"editor.detectIndentation": false, "editor.detectIndentation": false,
"editor.cursorBlinking": "expand", "editor.cursorBlinking": "expand",
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.largeFileOptimizations": false, "editor.largeFileOptimizations": false,
"editor.accessibilitySupport": "off", "editor.accessibilitySupport": "off",
"editor.cursorSmoothCaretAnimation": "on", "editor.cursorSmoothCaretAnimation": "on",
@ -37,7 +36,34 @@
"source.fixAll.stylelint": "explicit", "source.fixAll.stylelint": "explicit",
"source.organizeImports": "never" "source.organizeImports": "never"
}, },
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// extensions // extensions
"extensions.ignoreRecommendations": true, "extensions.ignoreRecommendations": true,
@ -194,7 +220,7 @@
"i18n-ally.keystyle": "nested", "i18n-ally.keystyle": "nested",
"commentTranslate.multiLineMerge": true, "commentTranslate.multiLineMerge": true,
"vue.server.hybridMode": true, "vue.server.hybridMode": true,
"typescript.tsdk": "node_modules/typescript/lib",
"vitest.disableWorkspaceWarning": true, "vitest.disableWorkspaceWarning": true,
"cSpell.words": ["tinymce"] "cSpell.words": ["tinymce"],
"typescript.tsdk": "node_modules/typescript/lib"
} }

View File

@ -28,13 +28,13 @@
"dependencies": { "dependencies": {
"@tinymce/tinymce-vue": "^6.0.1", "@tinymce/tinymce-vue": "^6.0.1",
"@vben/access": "workspace:*", "@vben/access": "workspace:*",
"@vben/chart-ui": "workspace:*",
"@vben/common-ui": "workspace:*", "@vben/common-ui": "workspace:*",
"@vben/constants": "workspace:*", "@vben/constants": "workspace:*",
"@vben/hooks": "workspace:*", "@vben/hooks": "workspace:*",
"@vben/icons": "workspace:*", "@vben/icons": "workspace:*",
"@vben/layouts": "workspace:*", "@vben/layouts": "workspace:*",
"@vben/locales": "workspace:*", "@vben/locales": "workspace:*",
"@vben/plugins": "workspace:*",
"@vben/preferences": "workspace:*", "@vben/preferences": "workspace:*",
"@vben/request": "workspace:*", "@vben/request": "workspace:*",
"@vben/stores": "workspace:*", "@vben/stores": "workspace:*",

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -27,13 +27,13 @@
}, },
"dependencies": { "dependencies": {
"@vben/access": "workspace:*", "@vben/access": "workspace:*",
"@vben/chart-ui": "workspace:*",
"@vben/common-ui": "workspace:*", "@vben/common-ui": "workspace:*",
"@vben/constants": "workspace:*", "@vben/constants": "workspace:*",
"@vben/hooks": "workspace:*", "@vben/hooks": "workspace:*",
"@vben/icons": "workspace:*", "@vben/icons": "workspace:*",
"@vben/layouts": "workspace:*", "@vben/layouts": "workspace:*",
"@vben/locales": "workspace:*", "@vben/locales": "workspace:*",
"@vben/plugins": "workspace:*",
"@vben/preferences": "workspace:*", "@vben/preferences": "workspace:*",
"@vben/request": "workspace:*", "@vben/request": "workspace:*",
"@vben/stores": "workspace:*", "@vben/stores": "workspace:*",

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -27,13 +27,13 @@
}, },
"dependencies": { "dependencies": {
"@vben/access": "workspace:*", "@vben/access": "workspace:*",
"@vben/chart-ui": "workspace:*",
"@vben/common-ui": "workspace:*", "@vben/common-ui": "workspace:*",
"@vben/constants": "workspace:*", "@vben/constants": "workspace:*",
"@vben/hooks": "workspace:*", "@vben/hooks": "workspace:*",
"@vben/icons": "workspace:*", "@vben/icons": "workspace:*",
"@vben/layouts": "workspace:*", "@vben/layouts": "workspace:*",
"@vben/locales": "workspace:*", "@vben/locales": "workspace:*",
"@vben/plugins": "workspace:*",
"@vben/preferences": "workspace:*", "@vben/preferences": "workspace:*",
"@vben/request": "workspace:*", "@vben/request": "workspace:*",
"@vben/stores": "workspace:*", "@vben/stores": "workspace:*",

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -170,7 +170,7 @@ const defaultPreferences: Preferences = {
locale: 'zh-CN', locale: 'zh-CN',
loginExpiredMode: 'modal', loginExpiredMode: 'modal',
name: 'Vben Admin', name: 'Vben Admin',
preferencesButtonPosition: 'fixed', preferencesButtonPosition: 'auto',
watermark: false, watermark: false,
}, },
breadcrumb: { breadcrumb: {

View File

@ -11,7 +11,7 @@ The framework has built-in two types of access control methods:
## Frontend Access Control ## Frontend Access Control
**Implementation Principle**: The permissions for routes are hardcoded on the frontend, specifying which permissions are required to view certain routes. Only general routes are initialized, and routes that require permissions are not added to the route table. After logging in or obtaining user roles through other means, the roles are used to traverse the route table to generate a route table that the role can access. This table is then added to the router instance using `router.addRoutes`, achieving permission filtering. **Implementation Principle**: The permissions for routes are hardcoded on the frontend, specifying which permissions are required to view certain routes. Only general routes are initialized, and routes that require permissions are not added to the route table. After logging in or obtaining user roles through other means, the roles are used to traverse the route table to generate a route table that the role can access. This table is then added to the router instance using `router.addRoute`, achieving permission filtering.
**Disadvantage**: The permissions are relatively inflexible; if the backend changes roles, the frontend needs to be adjusted accordingly. This is suitable for systems with relatively fixed roles. **Disadvantage**: The permissions are relatively inflexible; if the backend changes roles, the frontend needs to be adjusted accordingly. This is suitable for systems with relatively fixed roles.
@ -71,7 +71,7 @@ Sometimes, we need the menu to be visible but access to it forbidden. This can b
## Backend Access Control ## Backend Access Control
**Implementation Principle**: It is achieved by dynamically generating a routing table through an API, which returns data following a certain structure. The frontend processes this data into a recognizable structure, then adds it to the routing instance using `router.addRoutes`, realizing the dynamic generation of permissions. **Implementation Principle**: It is achieved by dynamically generating a routing table through an API, which returns data following a certain structure. The frontend processes this data into a recognizable structure, then adds it to the routing instance using `router.addRoute`, realizing the dynamic generation of permissions.
**Disadvantage**: The backend needs to provide a data structure that meets the standards, and the frontend needs to process this structure. This is suitable for systems with more complex permissions. **Disadvantage**: The backend needs to provide a data structure that meets the standards, and the frontend needs to process this structure. This is suitable for systems with more complex permissions.

View File

@ -43,7 +43,7 @@ The directory uses Monorepo management, and the project structure is as follows:
│   ├── constants # Constants │   ├── constants # Constants
│   ├── effects # Effects related packages │   ├── effects # Effects related packages
│   │   ├── access # Access control │   │   ├── access # Access control
│   │   ├── chart-ui # Chart UI │   │   ├── plugins # Plugins
│   │   ├── common-ui # Common UI │   │   ├── common-ui # Common UI
│   │   ├── hooks # Composable APIs │   │   ├── hooks # Composable APIs
│   │   ├── layouts # Layouts │   │   ├── layouts # Layouts

View File

@ -190,7 +190,7 @@ const defaultPreferences: Preferences = {
locale: 'zh-CN', locale: 'zh-CN',
loginExpiredMode: 'modal', loginExpiredMode: 'modal',
name: 'Vben Admin', name: 'Vben Admin',
preferencesButtonPosition: 'fixed', preferencesButtonPosition: 'auto',
watermark: false, watermark: false,
}, },
breadcrumb: { breadcrumb: {

View File

@ -11,7 +11,7 @@ outline: deep
## 前端访问控制 ## 前端访问控制
**实现原理**: 在前端固定写死路由的权限,指定路由有哪些权限可以查看。只初始化通用的路由,需要权限才能访问的路由没有被加入路由表内。在登陆后或者其他方式获取用户角色后,通过角色去遍历路由表,获取该角色可以访问的路由表,生成路由表,再通过 `router.addRoutes` 添加到路由实例,实现权限的过滤。 **实现原理**: 在前端固定写死路由的权限,指定路由有哪些权限可以查看。只初始化通用的路由,需要权限才能访问的路由没有被加入路由表内。在登陆后或者其他方式获取用户角色后,通过角色去遍历路由表,获取该角色可以访问的路由表,生成路由表,再通过 `router.addRoute` 添加到路由实例,实现权限的过滤。
**缺点**: 权限相对不自由,如果后台改动角色,前台也需要跟着改动。适合角色较固定的系统 **缺点**: 权限相对不自由,如果后台改动角色,前台也需要跟着改动。适合角色较固定的系统
@ -71,7 +71,7 @@ authStore.setUserInfo(userInfo);
## 后端访问控制 ## 后端访问控制
**实现原理**: 是通过接口动态生成路由表,且遵循一定的数据结构返回。前端根据需要处理该数据为可识别的结构,再通过 router.addRoutes 添加到路由实例,实现权限的动态生成。 **实现原理**: 是通过接口动态生成路由表,且遵循一定的数据结构返回。前端根据需要处理该数据为可识别的结构,再通过 `router.addRoute` 添加到路由实例,实现权限的动态生成。
**缺点**: 后端需要提供符合规范的数据结构,前端需要处理数据结构,适合权限较为复杂的系统。 **缺点**: 后端需要提供符合规范的数据结构,前端需要处理数据结构,适合权限较为复杂的系统。

View File

@ -43,7 +43,7 @@
│   ├── constants # 常量 │   ├── constants # 常量
│   ├── effects # 副作用相关包 │   ├── effects # 副作用相关包
│   │   ├── access # 访问控制 │   │   ├── access # 访问控制
│   │   ├── chart-ui # 图表 UI │   │   ├── plugins # 第三方大型依赖插件
│   │   ├── common-ui # 通用 UI │   │   ├── common-ui # 通用 UI
│   │   ├── hooks # 组合式 API │   │   ├── hooks # 组合式 API
│   │   ├── layouts # 布局 │   │   ├── layouts # 布局

View File

@ -28,7 +28,7 @@
#app, #app,
body, body,
html { html {
@apply size-full overscroll-none; @apply !pointer-events-auto size-full overscroll-none;
} }
body { body {

View File

@ -1,6 +1,6 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'; import { beforeEach, describe, expect, it, vi } from 'vitest';
import { getElementVisibleRect } from '../dom'; // 假设函数位于 utils.ts 中 import { getElementVisibleRect } from '../dom';
describe('getElementVisibleRect', () => { describe('getElementVisibleRect', () => {
// 设置浏览器视口尺寸的 mock // 设置浏览器视口尺寸的 mock

View File

@ -7,7 +7,6 @@ import {
toLowerCaseFirstLetter, toLowerCaseFirstLetter,
} from '../letter'; } from '../letter';
// 编写测试用例
describe('capitalizeFirstLetter', () => { describe('capitalizeFirstLetter', () => {
it('should capitalize the first letter of a string', () => { it('should capitalize the first letter of a string', () => {
expect(capitalizeFirstLetter('hello')).toBe('Hello'); expect(capitalizeFirstLetter('hello')).toBe('Hello');

View File

@ -13,8 +13,7 @@ describe('uniqueByField', () => {
const uniqueItems = uniqueByField(items, 'id'); const uniqueItems = uniqueByField(items, 'id');
// Assert expected results expect(uniqueItems).toHaveLength(3);
expect(uniqueItems).toHaveLength(3); // After deduplication, there should be three objects left
expect(uniqueItems).toEqual([ expect(uniqueItems).toEqual([
{ id: 1, name: 'Item 1' }, { id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' }, { id: 2, name: 'Item 2' },

View File

@ -11,8 +11,9 @@ type ThemeModeType = 'auto' | 'dark' | 'light';
* *
* fixed * fixed
* header * header
* auto
*/ */
type PreferencesButtonPositionType = 'fixed' | 'header'; type PreferencesButtonPositionType = 'auto' | 'fixed' | 'header';
type BuiltinThemeType = type BuiltinThemeType =
| 'custom' | 'custom'

View File

@ -1,4 +1,5 @@
export * from './use-content-style'; export * from './use-content-style';
export * from './use-is-mobile';
export * from './use-namespace'; export * from './use-namespace';
export * from './use-priority-value'; export * from './use-priority-value';
export * from './use-sortable'; export * from './use-sortable';

View File

@ -28,7 +28,7 @@ function useContentStyle() {
position: 'fixed', position: 'fixed',
top: `${top}px`, top: `${top}px`,
width: `${width}px`, width: `${width}px`,
zIndex: 1000, zIndex: 150,
}; };
}); });

View File

@ -0,0 +1,7 @@
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core';
export function useIsMobile() {
const breakpoints = useBreakpoints(breakpointsTailwind);
const isMobile = breakpoints.smaller('md');
return { isMobile };
}

View File

@ -20,7 +20,7 @@ const defaultPreferences: Preferences = {
locale: 'zh-CN', locale: 'zh-CN',
loginExpiredMode: 'page', loginExpiredMode: 'page',
name: 'Vben Admin', name: 'Vben Admin',
preferencesButtonPosition: 'fixed', preferencesButtonPosition: 'auto',
watermark: false, watermark: false,
}, },
breadcrumb: { breadcrumb: {

View File

@ -149,6 +149,45 @@ function usePreferences() {
return enable && globalLockScreen; return enable && globalLockScreen;
}); });
/**
* @zh_CN
*/
const preferencesButtonPosition = computed(() => {
const { enablePreferences, preferencesButtonPosition } = preferences.app;
// 如果没有启用偏好设置按钮
if (!enablePreferences) {
return {
fixed: false,
header: false,
};
}
const { header, sidebar } = preferences;
const headerHidden = header.hidden;
const sidebarHidden = sidebar.hidden;
const contentIsMaximize = headerHidden && sidebarHidden;
const isHeaderPosition = preferencesButtonPosition === 'header';
// 如果设置了固定位置
if (preferencesButtonPosition !== 'auto') {
return {
fixed: preferencesButtonPosition === 'fixed',
header: isHeaderPosition,
};
}
// 如果是全屏模式或者没有固定在顶部,
const fixed = contentIsMaximize || isFullContent.value || isMobile.value;
return {
fixed,
header: !fixed,
};
});
return { return {
authPanelCenter, authPanelCenter,
authPanelLeft, authPanelLeft,
@ -168,6 +207,7 @@ function usePreferences() {
isSideNav, isSideNav,
keepAlive, keepAlive,
layout, layout,
preferencesButtonPosition,
sidebarCollapsed, sidebarCollapsed,
theme, theme,
}; };

View File

@ -4,6 +4,8 @@ import { computed, shallowRef, useSlots, watchEffect } from 'vue';
import { VbenScrollbar } from '@vben-core/shadcn-ui'; import { VbenScrollbar } from '@vben-core/shadcn-ui';
import { useScrollLock } from '@vueuse/core';
import { SidebarCollapseButton, SidebarFixedButton } from './widgets'; import { SidebarCollapseButton, SidebarFixedButton } from './widgets';
interface Props { interface Props {
@ -102,6 +104,7 @@ const expandOnHovering = defineModel<boolean>('expandOnHovering');
const expandOnHover = defineModel<boolean>('expandOnHover'); const expandOnHover = defineModel<boolean>('expandOnHover');
const extraVisible = defineModel<boolean>('extraVisible'); const extraVisible = defineModel<boolean>('extraVisible');
const isLocked = useScrollLock(document.body);
const slots = useSlots(); const slots = useSlots();
const asideRef = shallowRef<HTMLDivElement | null>(); const asideRef = shallowRef<HTMLDivElement | null>();
@ -214,6 +217,7 @@ function handleMouseenter() {
if (!expandOnHovering.value) { if (!expandOnHovering.value) {
collapse.value = false; collapse.value = false;
} }
isLocked.value = true;
expandOnHovering.value = true; expandOnHovering.value = true;
} }
@ -224,6 +228,7 @@ function handleMouseleave() {
return; return;
} }
isLocked.value = false;
expandOnHovering.value = false; expandOnHovering.value = false;
collapse.value = true; collapse.value = true;
extraVisible.value = false; extraVisible.value = false;

View File

@ -242,7 +242,7 @@ const tabbarStyle = computed((): CSSProperties => {
let marginLeft = 0; let marginLeft = 0;
// tabbar 100% // tabbar 100%
if (!isMixedNav.value) { if (!isMixedNav.value || props.sidebarHidden) {
width = '100%'; width = '100%';
} else if (sidebarEnable.value) { } else if (sidebarEnable.value) {
// //

View File

@ -30,6 +30,8 @@ export class DrawerApi {
const defaultState: DrawerState = { const defaultState: DrawerState = {
cancelText: '取消', cancelText: '取消',
closable: true, closable: true,
closeOnClickModal: true,
closeOnPressEscape: true,
confirmLoading: false, confirmLoading: false,
confirmText: '确定', confirmText: '确定',
footer: true, footer: true,

View File

@ -7,12 +7,21 @@ export interface DrawerProps {
* *
*/ */
cancelText?: string; cancelText?: string;
/** /**
* *
* @default true * @default true
*/ */
closable?: boolean; closable?: boolean;
/**
*
* @default true
*/
closeOnClickModal?: boolean;
/**
* ESC
* @default true
*/
closeOnPressEscape?: boolean;
/** /**
* loading * loading
* @default false * @default false

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { DrawerProps, ExtendedDrawerApi } from './drawer'; import type { DrawerProps, ExtendedDrawerApi } from './drawer';
import { usePriorityValue } from '@vben-core/composables'; import { useIsMobile, usePriorityValue } from '@vben-core/composables';
import { Info, X } from '@vben-core/icons'; import { Info, X } from '@vben-core/icons';
import { import {
Sheet, Sheet,
@ -31,6 +31,7 @@ const props = withDefaults(defineProps<Props>(), {
drawerApi: undefined, drawerApi: undefined,
}); });
const { isMobile } = useIsMobile();
const state = props.drawerApi?.useStore?.(); const state = props.drawerApi?.useStore?.();
const title = usePriorityValue('title', props, state); const title = usePriorityValue('title', props, state);
@ -43,6 +44,27 @@ const modal = usePriorityValue('modal', props, state);
const confirmLoading = usePriorityValue('confirmLoading', props, state); const confirmLoading = usePriorityValue('confirmLoading', props, state);
const cancelText = usePriorityValue('cancelText', props, state); const cancelText = usePriorityValue('cancelText', props, state);
const confirmText = usePriorityValue('confirmText', props, state); const confirmText = usePriorityValue('confirmText', props, state);
const closeOnClickModal = usePriorityValue('closeOnClickModal', props, state);
const closeOnPressEscape = usePriorityValue('closeOnPressEscape', props, state);
function interactOutside(e: Event) {
if (!closeOnClickModal.value) {
e.preventDefault();
}
}
function escapeKeyDown(e: KeyboardEvent) {
if (!closeOnPressEscape.value) {
e.preventDefault();
}
}
// pointer-down-outside
function pointerDownOutside(e: Event) {
const target = e.target as HTMLElement;
const isDismissableModal = !!target?.dataset.dismissableModal;
if (!closeOnClickModal.value || !isDismissableModal) {
e.preventDefault();
}
}
</script> </script>
<template> <template>
<Sheet <Sheet
@ -50,7 +72,16 @@ const confirmText = usePriorityValue('confirmText', props, state);
:open="state?.isOpen" :open="state?.isOpen"
@update:open="() => drawerApi?.close()" @update:open="() => drawerApi?.close()"
> >
<SheetContent :class="cn('flex w-[520px] flex-col', props.class, {})"> <SheetContent
:class="
cn('flex w-[520px] flex-col', props.class, {
'!w-full': isMobile,
})
"
@escape-key-down="escapeKeyDown"
@interact-outside="interactOutside"
@pointer-down-outside="pointerDownOutside"
>
<SheetHeader <SheetHeader
:class=" :class="
cn('!flex flex-row items-center justify-between border-b px-6 py-5', { cn('!flex flex-row items-center justify-between border-b px-6 py-5', {
@ -59,7 +90,7 @@ const confirmText = usePriorityValue('confirmText', props, state);
" "
> >
<div> <div>
<SheetTitle v-if="title"> <SheetTitle v-if="title" class="text-left">
<slot name="title"> <slot name="title">
{{ title }} {{ title }}
@ -111,22 +142,17 @@ const confirmText = usePriorityValue('confirmText', props, state);
<SheetFooter <SheetFooter
v-if="showFooter" v-if="showFooter"
class="w-full items-center border-t p-2 px-3" class="w-full flex-row items-center justify-end border-t p-2 px-3"
> >
<slot name="prepend-footer"></slot> <slot name="prepend-footer"></slot>
<slot name="footer"> <slot name="footer">
<VbenButton <VbenButton variant="ghost" @click="() => drawerApi?.onCancel()">
size="sm"
variant="ghost"
@click="() => drawerApi?.onCancel()"
>
<slot name="cancelText"> <slot name="cancelText">
{{ cancelText }} {{ cancelText }}
</slot> </slot>
</VbenButton> </VbenButton>
<VbenButton <VbenButton
:loading="confirmLoading" :loading="confirmLoading"
size="sm"
@click="() => drawerApi?.onConfirm()" @click="() => drawerApi?.onConfirm()"
> >
<slot name="confirmText"> <slot name="confirmText">

View File

@ -3,7 +3,7 @@ import type { ExtendedModalApi, ModalProps } from './modal';
import { computed, nextTick, ref, watch } from 'vue'; import { computed, nextTick, ref, watch } from 'vue';
import { usePriorityValue } from '@vben-core/composables'; import { useIsMobile, usePriorityValue } from '@vben-core/composables';
import { Expand, Info, Shrink } from '@vben-core/icons'; import { Expand, Info, Shrink } from '@vben-core/icons';
import { import {
Dialog, Dialog,
@ -46,6 +46,7 @@ const dialogRef = ref();
const headerRef = ref(); const headerRef = ref();
const footerRef = ref(); const footerRef = ref();
const { isMobile } = useIsMobile();
// const { height: headerHeight } = useElementSize(headerRef); // const { height: headerHeight } = useElementSize(headerRef);
// const { height: footerHeight } = useElementSize(footerRef); // const { height: footerHeight } = useElementSize(footerRef);
const state = props.modalApi?.useStore?.(); const state = props.modalApi?.useStore?.();
@ -66,7 +67,11 @@ const draggable = usePriorityValue('draggable', props, state);
const fullscreenButton = usePriorityValue('fullscreenButton', props, state); const fullscreenButton = usePriorityValue('fullscreenButton', props, state);
const closeOnClickModal = usePriorityValue('closeOnClickModal', props, state); const closeOnClickModal = usePriorityValue('closeOnClickModal', props, state);
const closeOnPressEscape = usePriorityValue('closeOnPressEscape', props, state); const closeOnPressEscape = usePriorityValue('closeOnPressEscape', props, state);
const shouldDraggable = computed(() => draggable.value && !fullscreen.value);
const shouldFullscreen = computed(() => fullscreen.value || isMobile.value);
const shouldDraggable = computed(
() => draggable.value && !shouldFullscreen.value,
);
const { dragging } = useModalDraggable(dialogRef, headerRef, shouldDraggable); const { dragging } = useModalDraggable(dialogRef, headerRef, shouldDraggable);
@ -114,6 +119,14 @@ function escapeKeyDown(e: KeyboardEvent) {
e.preventDefault(); e.preventDefault();
} }
} }
// pointer-down-outside
function pointerDownOutside(e: Event) {
const target = e.target as HTMLElement;
const isDismissableModal = !!target?.dataset.dismissableModal;
if (!closeOnClickModal.value || !isDismissableModal) {
e.preventDefault();
}
}
</script> </script>
<template> <template>
<Dialog <Dialog
@ -133,8 +146,8 @@ function escapeKeyDown(e: KeyboardEvent) {
props.class, props.class,
{ {
'left-0 top-0 size-full max-h-full !translate-x-0 !translate-y-0': 'left-0 top-0 size-full max-h-full !translate-x-0 !translate-y-0':
fullscreen, shouldFullscreen,
'top-1/2 -translate-y-1/2': centered && !fullscreen, 'top-1/2 -translate-y-1/2': centered && !shouldFullscreen,
'duration-300': !dragging, 'duration-300': !dragging,
}, },
) )
@ -143,6 +156,7 @@ function escapeKeyDown(e: KeyboardEvent) {
close-class="top-4" close-class="top-4"
@escape-key-down="escapeKeyDown" @escape-key-down="escapeKeyDown"
@interact-outside="interactOutside" @interact-outside="interactOutside"
@pointer-down-outside="pointerDownOutside"
> >
<DialogHeader <DialogHeader
ref="headerRef" ref="headerRef"
@ -156,7 +170,7 @@ function escapeKeyDown(e: KeyboardEvent) {
) )
" "
> >
<DialogTitle v-if="title"> <DialogTitle v-if="title" class="text-left">
<slot name="title"> <slot name="title">
{{ title }} {{ title }}
@ -191,7 +205,7 @@ function escapeKeyDown(e: KeyboardEvent) {
<VbenIconButton <VbenIconButton
v-if="fullscreenButton" v-if="fullscreenButton"
class="hover:bg-accent hover:text-accent-foreground text-foreground/80 flex-center absolute right-10 top-4 size-6 rounded-full px-1 text-lg opacity-70 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none" class="hover:bg-accent hover:text-accent-foreground text-foreground/80 flex-center absolute right-10 top-4 hidden size-6 rounded-full px-1 text-lg opacity-70 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none sm:block"
@click="handleFullscreen" @click="handleFullscreen"
> >
<Shrink v-if="fullscreen" class="size-3.5" /> <Shrink v-if="fullscreen" class="size-3.5" />
@ -201,22 +215,22 @@ function escapeKeyDown(e: KeyboardEvent) {
<DialogFooter <DialogFooter
v-if="showFooter" v-if="showFooter"
ref="footerRef" ref="footerRef"
:class="cn('items-center border-t p-2', props.footerClass)" :class="
cn(
'flex-row items-center justify-end border-t p-2',
props.footerClass,
)
"
> >
<slot name="prepend-footer"></slot> <slot name="prepend-footer"></slot>
<slot name="footer"> <slot name="footer">
<VbenButton <VbenButton variant="ghost" @click="() => modalApi?.onCancel()">
size="sm"
variant="ghost"
@click="() => modalApi?.onCancel()"
>
<slot name="cancelText"> <slot name="cancelText">
{{ cancelText }} {{ cancelText }}
</slot> </slot>
</VbenButton> </VbenButton>
<VbenButton <VbenButton
:loading="confirmLoading" :loading="confirmLoading"
size="sm"
@click="() => modalApi?.onConfirm()" @click="() => modalApi?.onConfirm()"
> >
<slot name="confirmText"> <slot name="confirmText">

View File

@ -94,7 +94,7 @@ async function checkProps(api: ExtendedModalApi, attrs: Record<string, any>) {
if (stateKeys.has(attr)) { if (stateKeys.has(attr)) {
// connectedComponent存在时不要传入Modal的props会造成复杂度提升如果你需要修改Modal的props请使用 useModal 或者api // connectedComponent存在时不要传入Modal的props会造成复杂度提升如果你需要修改Modal的props请使用 useModal 或者api
console.warn( console.warn(
`[Vben Modal]: When 'connectedComponent' exists, do not set props or slots '${attr}', which will increase complexity. If you need to modify the props of Modal, please use useModal or api.`, `[Vben Modal]: When 'connectedComponent' exists, do not set props or slots '${attr}', which will increase complexity. If you need to modify the props of Modal, please use useVbenModal or api.`,
); );
} }
} }

View File

@ -45,6 +45,7 @@ defineExpose({
<DialogPortal> <DialogPortal>
<DialogOverlay <DialogOverlay
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 bg-overlay fixed inset-0 z-[1000] backdrop-blur-sm" class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 bg-overlay fixed inset-0 z-[1000] backdrop-blur-sm"
data-dismissable-modal="true"
@click="() => emits('close')" @click="() => emits('close')"
/> />
<DialogContent <DialogContent

View File

@ -40,6 +40,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
<DialogPortal> <DialogPortal>
<DialogOverlay <DialogOverlay
class="bg-overlay data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-[1000]" class="bg-overlay data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-[1000]"
data-dismissable-modal="true"
/> />
<DialogContent <DialogContent
:class="cn(sheetVariants({ side }), 'z-[1000]', props.class)" :class="cn(sheetVariants({ side }), 'z-[1000]', props.class)"

View File

@ -73,6 +73,7 @@ export function useTabsViewScroll(props: TabsProps) {
resizeObserver = new ResizeObserver( resizeObserver = new ResizeObserver(
useDebounceFn((_entries: ResizeObserverEntry[]) => { useDebounceFn((_entries: ResizeObserverEntry[]) => {
calcShowScrollbarButton(); calcShowScrollbarButton();
scrollToActiveIntoView();
}, 100), }, 100),
); );
resizeObserver.observe(viewportEl); resizeObserver.observe(viewportEl);

View File

@ -1 +0,0 @@
export * from './echarts';

View File

@ -28,7 +28,7 @@ withDefaults(defineProps<Props>(), {
}); });
const accessStore = useAccessStore(); const accessStore = useAccessStore();
const { globalSearchShortcutKey } = usePreferences(); const { globalSearchShortcutKey, preferencesButtonPosition } = usePreferences();
const slots = useSlots(); const slots = useSlots();
const rightSlots = computed(() => { const rightSlots = computed(() => {
const list = [{ index: 100, name: 'user-dropdown' }]; const list = [{ index: 100, name: 'user-dropdown' }];
@ -39,10 +39,7 @@ const rightSlots = computed(() => {
}); });
} }
if ( if (preferencesButtonPosition.value.header) {
preferences.app.enablePreferences &&
preferences.app.preferencesButtonPosition === 'header'
) {
list.push({ list.push({
index: 10, index: 10,
name: 'preferences', name: 'preferences',
@ -121,7 +118,7 @@ const leftSlots = computed(() => {
<GlobalSearch <GlobalSearch
:enable-shortcut-key="globalSearchShortcutKey" :enable-shortcut-key="globalSearchShortcutKey"
:menus="accessStore.accessMenus" :menus="accessStore.accessMenus"
class="mr-4" class="mr-1 sm:mr-4"
/> />
</template> </template>

View File

@ -40,6 +40,7 @@ const {
isMobile, isMobile,
isSideMixedNav, isSideMixedNav,
layout, layout,
preferencesButtonPosition,
sidebarCollapsed, sidebarCollapsed,
theme, theme,
} = usePreferences(); } = usePreferences();
@ -326,12 +327,7 @@ const headerSlots = computed(() => {
<slot v-if="lockStore.isLockScreen" name="lock-screen"></slot> <slot v-if="lockStore.isLockScreen" name="lock-screen"></slot>
</Transition> </Transition>
<template <template v-if="preferencesButtonPosition.fixed">
v-if="
preferences.app.enablePreferences &&
preferences.app.preferencesButtonPosition === 'fixed'
"
>
<Preferences <Preferences
class="z-100 fixed bottom-20 right-0" class="z-100 fixed bottom-20 right-0"
@clear-preferences-and-logout="clearPreferencesAndLogout" @clear-preferences-and-logout="clearPreferencesAndLogout"

View File

@ -127,7 +127,7 @@ onMounted(() => {
@click="toggleOpen()" @click="toggleOpen()"
> >
<Search <Search
class="text-muted-foreground group-hover:text-foreground size-3 group-hover:opacity-100" class="text-muted-foreground group-hover:text-foreground size-4 group-hover:opacity-100"
/> />
<span <span
class="text-muted-foreground group-hover:text-foreground hidden text-xs duration-300 md:block" class="text-muted-foreground group-hover:text-foreground hidden text-xs duration-300 md:block"

View File

@ -159,7 +159,7 @@ function toggleUnlockForm() {
</transition> </transition>
<div <div
class="enter-y absolute bottom-5 w-full text-center text-gray-300 xl:text-xl 2xl:text-3xl" class="enter-y absolute bottom-5 w-full text-center xl:text-xl 2xl:text-3xl"
> >
<div v-if="showUnlockForm" class="enter-x mb-2 text-3xl"> <div v-if="showUnlockForm" class="enter-x mb-2 text-3xl">
{{ hour }}:{{ minute }} <span class="text-lg">{{ meridiem }}</span> {{ hour }}:{{ minute }} <span class="text-lg">{{ meridiem }}</span>

View File

@ -24,6 +24,10 @@ const appPreferencesButtonPosition = defineModel<string>(
); );
const positionItems = computed((): SelectOption[] => [ const positionItems = computed((): SelectOption[] => [
{
label: $t('preferences.position.auto'),
value: 'auto',
},
{ {
label: $t('preferences.position.header'), label: $t('preferences.position.header'),
value: 'header', value: 'header',

View File

@ -55,7 +55,7 @@ const listen = computed(() => {
</script> </script>
<template> <template>
<div> <div>
<Drawer v-bind="attrs" v-on="listen" /> <Drawer v-bind="{ ...$attrs, ...attrs }" v-on="listen" />
<div @click="() => drawerApi.open()"> <div @click="() => drawerApi.open()">
<slot> <slot>

View File

@ -0,0 +1,28 @@
# @vben/plugins
该目录用于存放项目中集成的第三方库及其相关插件。每个插件都包含了可重用的逻辑、配置和组件,方便在项目中进行统一管理和调用。
## 注意
所有的第三方插件都必须以 `subpath` 形式引入,例:
`echarts` 为例,引入方式如下:
**packages.json**
```json
"exports": {
"./echarts": {
"types": "./src/echarts/index.ts",
"default": "./src/echarts/index.ts"
}
}
```
**使用方式**
```ts
import { useEcharts } from '@vben/plugins/echarts';
```
这样做的好处是,应用可以自行选择是否使用插件,而不会因为插件的引入及副作用而导致打包体积增大,只引入需要的插件即可。

View File

@ -1,12 +1,12 @@
{ {
"name": "@vben/chart-ui", "name": "@vben/plugins",
"version": "5.1.2", "version": "5.1.2",
"homepage": "https://github.com/vbenjs/vue-vben-admin", "homepage": "https://github.com/vbenjs/vue-vben-admin",
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues", "bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/vbenjs/vue-vben-admin.git", "url": "git+https://github.com/vbenjs/vue-vben-admin.git",
"directory": "packages/effects/chart-ui" "directory": "packages/effects/plugins"
}, },
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",
@ -14,9 +14,9 @@
"**/*.css" "**/*.css"
], ],
"exports": { "exports": {
".": { "./echarts": {
"types": "./src/index.ts", "types": "./src/echarts/index.ts",
"default": "./src/index.ts" "default": "./src/echarts/index.ts"
} }
}, },
"dependencies": { "dependencies": {

View File

@ -5,11 +5,12 @@ import type EchartsUI from './echarts-ui.vue';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { computed, nextTick, watch } from 'vue'; import { computed, nextTick, watch } from 'vue';
import { preferences, usePreferences } from '@vben/preferences'; import { usePreferences } from '@vben/preferences';
import { import {
tryOnUnmounted, tryOnUnmounted,
useDebounceFn, useDebounceFn,
useResizeObserver,
useTimeoutFn, useTimeoutFn,
useWindowSize, useWindowSize,
} from '@vueuse/core'; } from '@vueuse/core';
@ -86,6 +87,8 @@ function useEcharts(chartRef: Ref<EchartsUIType>) {
resizeHandler?.(); resizeHandler?.();
}); });
useResizeObserver(chartRef as never, resizeHandler);
watch(isDark, () => { watch(isDark, () => {
if (chartInstance) { if (chartInstance) {
chartInstance.dispose(); chartInstance.dispose();
@ -95,21 +98,6 @@ function useEcharts(chartRef: Ref<EchartsUIType>) {
} }
}); });
watch(
[
() => preferences.sidebar.collapsed,
() => preferences.sidebar.extraCollapse,
() => preferences.sidebar.hidden,
() => preferences.app.contentCompact,
],
() => {
// 折叠动画200ms
setTimeout(() => {
resize();
}, 200);
},
);
tryOnUnmounted(() => { tryOnUnmounted(() => {
// 销毁实例,释放资源 // 销毁实例,释放资源
chartInstance?.dispose(); chartInstance?.dispose();

View File

@ -182,6 +182,7 @@
"position": { "position": {
"title": "Preferences Postion", "title": "Preferences Postion",
"header": "Header", "header": "Header",
"auto": "Auto",
"fixed": "Fixed" "fixed": "Fixed"
}, },
"sidebar": { "sidebar": {

View File

@ -182,6 +182,7 @@
"position": { "position": {
"title": "偏好设置位置", "title": "偏好设置位置",
"header": "顶栏", "header": "顶栏",
"auto": "自动",
"fixed": "固定" "fixed": "固定"
}, },
"sidebar": { "sidebar": {

View File

@ -0,0 +1,6 @@
/**
* Returns the parent node of the given element or the document body if the element is not provided.it
*/
export function getPopupContainer(node?: HTMLElement): HTMLElement {
return (node?.parentNode as HTMLElement) ?? document.body;
}

View File

@ -2,6 +2,7 @@ export * from './find-menu-by-path';
export * from './generate-menus'; export * from './generate-menus';
export * from './generate-routes-backend'; export * from './generate-routes-backend';
export * from './generate-routes-frontend'; export * from './generate-routes-frontend';
export * from './get-popup-container';
export * from './merge-route-modules'; export * from './merge-route-modules';
export * from './reset-routes'; export * from './reset-routes';
export * from './unmount-global-loading'; export * from './unmount-global-loading';

View File

@ -27,13 +27,13 @@
}, },
"dependencies": { "dependencies": {
"@vben/access": "workspace:*", "@vben/access": "workspace:*",
"@vben/chart-ui": "workspace:*",
"@vben/common-ui": "workspace:*", "@vben/common-ui": "workspace:*",
"@vben/constants": "workspace:*", "@vben/constants": "workspace:*",
"@vben/hooks": "workspace:*", "@vben/hooks": "workspace:*",
"@vben/icons": "workspace:*", "@vben/icons": "workspace:*",
"@vben/layouts": "workspace:*", "@vben/layouts": "workspace:*",
"@vben/locales": "workspace:*", "@vben/locales": "workspace:*",
"@vben/plugins": "workspace:*",
"@vben/preferences": "workspace:*", "@vben/preferences": "workspace:*",
"@vben/request": "workspace:*", "@vben/request": "workspace:*",
"@vben/stores": "workspace:*", "@vben/stores": "workspace:*",

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -1,7 +1,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
import { EchartsUI, type EchartsUIType, useEcharts } from '@vben/chart-ui'; import {
EchartsUI,
type EchartsUIType,
useEcharts,
} from '@vben/plugins/echarts';
const chartRef = ref<EchartsUIType>(); const chartRef = ref<EchartsUIType>();
const { renderEcharts } = useEcharts(chartRef); const { renderEcharts } = useEcharts(chartRef);

View File

@ -138,9 +138,6 @@ importers:
'@vben/access': '@vben/access':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/effects/access version: link:../../packages/effects/access
'@vben/chart-ui':
specifier: workspace:*
version: link:../../packages/effects/chart-ui
'@vben/common-ui': '@vben/common-ui':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/effects/common-ui version: link:../../packages/effects/common-ui
@ -159,6 +156,9 @@ importers:
'@vben/locales': '@vben/locales':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/locales version: link:../../packages/locales
'@vben/plugins':
specifier: workspace:*
version: link:../../packages/effects/plugins
'@vben/preferences': '@vben/preferences':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/preferences version: link:../../packages/preferences
@ -226,9 +226,6 @@ importers:
'@vben/access': '@vben/access':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/effects/access version: link:../../packages/effects/access
'@vben/chart-ui':
specifier: workspace:*
version: link:../../packages/effects/chart-ui
'@vben/common-ui': '@vben/common-ui':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/effects/common-ui version: link:../../packages/effects/common-ui
@ -247,6 +244,9 @@ importers:
'@vben/locales': '@vben/locales':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/locales version: link:../../packages/locales
'@vben/plugins':
specifier: workspace:*
version: link:../../packages/effects/plugins
'@vben/preferences': '@vben/preferences':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/preferences version: link:../../packages/preferences
@ -293,9 +293,6 @@ importers:
'@vben/access': '@vben/access':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/effects/access version: link:../../packages/effects/access
'@vben/chart-ui':
specifier: workspace:*
version: link:../../packages/effects/chart-ui
'@vben/common-ui': '@vben/common-ui':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/effects/common-ui version: link:../../packages/effects/common-ui
@ -314,6 +311,9 @@ importers:
'@vben/locales': '@vben/locales':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/locales version: link:../../packages/locales
'@vben/plugins':
specifier: workspace:*
version: link:../../packages/effects/plugins
'@vben/preferences': '@vben/preferences':
specifier: workspace:* specifier: workspace:*
version: link:../../packages/preferences version: link:../../packages/preferences
@ -928,21 +928,6 @@ importers:
specifier: 3.4.38 specifier: 3.4.38
version: 3.4.38(typescript@5.5.4) version: 3.4.38(typescript@5.5.4)
packages/effects/chart-ui:
dependencies:
'@vben/preferences':
specifier: workspace:*
version: link:../../preferences
'@vueuse/core':
specifier: ^11.0.1
version: 11.0.1(vue@3.4.38(typescript@5.5.4))
echarts:
specifier: ^5.5.1
version: 5.5.1
vue:
specifier: 3.4.38
version: 3.4.38(typescript@5.5.4)
packages/effects/common-ui: packages/effects/common-ui:
dependencies: dependencies:
'@vben-core/popup-ui': '@vben-core/popup-ui':
@ -1061,6 +1046,21 @@ importers:
specifier: ^4.4.3 specifier: ^4.4.3
version: 4.4.3(vue@3.4.38(typescript@5.5.4)) version: 4.4.3(vue@3.4.38(typescript@5.5.4))
packages/effects/plugins:
dependencies:
'@vben/preferences':
specifier: workspace:*
version: link:../../preferences
'@vueuse/core':
specifier: ^11.0.1
version: 11.0.1(vue@3.4.38(typescript@5.5.4))
echarts:
specifier: ^5.5.1
version: 5.5.1
vue:
specifier: 3.4.38
version: 3.4.38(typescript@5.5.4)
packages/effects/request: packages/effects/request:
dependencies: dependencies:
'@vben/locales': '@vben/locales':
@ -1160,9 +1160,6 @@ importers:
'@vben/access': '@vben/access':
specifier: workspace:* specifier: workspace:*
version: link:../packages/effects/access version: link:../packages/effects/access
'@vben/chart-ui':
specifier: workspace:*
version: link:../packages/effects/chart-ui
'@vben/common-ui': '@vben/common-ui':
specifier: workspace:* specifier: workspace:*
version: link:../packages/effects/common-ui version: link:../packages/effects/common-ui
@ -1181,6 +1178,9 @@ importers:
'@vben/locales': '@vben/locales':
specifier: workspace:* specifier: workspace:*
version: link:../packages/locales version: link:../packages/locales
'@vben/plugins':
specifier: workspace:*
version: link:../packages/effects/plugins
'@vben/preferences': '@vben/preferences':
specifier: workspace:* specifier: workspace:*
version: link:../packages/preferences version: link:../packages/preferences

View File

@ -104,10 +104,6 @@
"name": "@vben/access", "name": "@vben/access",
"path": "packages/effects/access", "path": "packages/effects/access",
}, },
{
"name": "@vben/chart-ui",
"path": "packages/effects/chart-ui",
},
{ {
"name": "@vben/common-ui", "name": "@vben/common-ui",
"path": "packages/effects/common-ui", "path": "packages/effects/common-ui",
@ -120,6 +116,10 @@
"name": "@vben/layouts", "name": "@vben/layouts",
"path": "packages/effects/layouts", "path": "packages/effects/layouts",
}, },
{
"name": "@vben/plugins",
"path": "packages/effects/plugins",
},
{ {
"name": "@vben/request", "name": "@vben/request",
"path": "packages/effects/request", "path": "packages/effects/request",

3
vitest.workspace.ts Normal file
View File

@ -0,0 +1,3 @@
import { defineWorkspace } from 'vitest/config';
export default defineWorkspace(['vitest.config.ts']);