From c58c0797baac9312f8f60eecdf68bc36d4f46198 Mon Sep 17 00:00:00 2001 From: vben Date: Sun, 23 Jun 2024 23:18:55 +0800 Subject: [PATCH] feat: add dashboard page --- apps/web-antd/mock/user.ts | 4 +- apps/web-antd/package.json | 2 +- .../src/router/routes/modules/dashboard.ts | 39 ++ .../src/router/routes/modules/fallback.ts | 2 +- .../src/router/routes/modules/home.ts | 30 - .../src/router/routes/modules/nested.ts | 2 +- .../src/router/routes/modules/outside.ts | 2 +- apps/web-antd/src/store/index.ts | 4 +- .../dashboard/analytics/analytics-trends.vue | 80 +++ .../analytics/analytics-visits-data.vue | 82 +++ .../analytics/analytics-visits-sales.vue | 46 ++ .../analytics/analytics-visits-source.vue | 65 ++ .../dashboard/analytics/analytics-visits.vue | 55 ++ .../src/views/dashboard/analytics/index.vue | 92 +++ apps/web-antd/src/views/dashboard/index.vue | 250 -------- .../src/views/dashboard/workspace/index.vue | 125 ++++ cspell.json | 1 + internal/node-utils/package.json | 2 +- internal/tailwind-config/package.json | 2 +- internal/tsconfig/package.json | 2 +- internal/tsconfig/web.json | 2 +- internal/vite-config/package.json | 4 +- internal/vite-config/src/typing.ts | 2 +- package.json | 4 +- .../@core/forward/preferences/package.json | 2 +- .../@core/forward/preferences/src/config.ts | 2 +- .../preferences/src/preferences.test.ts | 2 +- packages/@core/forward/stores/package.json | 2 +- .../@core/forward/stores/src/modules/tabs.ts | 2 +- packages/@core/shared/iconify/package.json | 2 +- packages/@core/shared/iconify/src/factory.ts | 1 + packages/@core/shared/toolkit/package.json | 2 +- packages/@core/shared/toolkit/src/dom.test.ts | 140 +++++ packages/@core/shared/toolkit/src/dom.ts | 24 + packages/@core/shared/toolkit/src/index.ts | 2 + .../@core/shared/toolkit/src/inference.ts | 9 + .../@core/shared/toolkit/src/unique.test.ts | 61 ++ packages/@core/shared/toolkit/src/unique.ts | 15 + packages/@core/shared/typings/package.json | 2 +- packages/@core/ui-kit/layout-ui/package.json | 3 +- .../src/components/layout-content.vue | 8 +- packages/@core/ui-kit/menu-ui/package.json | 2 +- packages/@core/ui-kit/shadcn-ui/package.json | 2 +- .../src/components/back-top/back-top.vue | 4 +- .../count-to-animator/count-to-animator.vue | 111 ++++ .../src/components/count-to-animator/index.ts | 1 + .../ui-kit/shadcn-ui/src/components/index.ts | 1 + packages/@core/ui-kit/tabs-ui/package.json | 2 +- packages/business/chart-ui/package.json | 5 +- packages/business/chart-ui/src/chart.vue | 37 -- .../chart-ui/src/echarts/echarts-ui.vue | 15 + .../business/chart-ui/src/echarts/echarts.ts | 59 ++ .../business/chart-ui/src/echarts/index.ts | 3 + .../chart-ui/src/echarts/use-echarts.ts | 108 ++++ packages/business/chart-ui/src/index.ts | 60 +- packages/business/layouts/package.json | 3 +- .../layouts/src/basic/content/content.vue | 2 +- .../layouts/src/basic/widgets/breadcrumb.vue | 1 + packages/business/universal-ui/package.json | 3 +- .../business/universal-ui/src/about/about.vue | 15 +- .../analysis/analysis-chart-card.vue | 24 + .../analysis/analysis-charts-tabs.vue | 42 ++ .../dashboard/analysis/analysis-overview.vue | 59 ++ .../src/dashboard/analysis/index.ts | 3 + .../universal-ui/src/dashboard/card.vue | 45 -- .../universal-ui/src/dashboard/chartCard.vue | 24 - .../universal-ui/src/dashboard/chartTab.vue | 41 -- .../universal-ui/src/dashboard/dashboard.vue | 156 ----- .../universal-ui/src/dashboard/index.ts | 6 +- .../universal-ui/src/dashboard/layout.vue | 7 - .../universal-ui/src/dashboard/typing.ts | 29 + .../universal-ui/src/dashboard/typings.ts | 17 - .../src/dashboard/workbench/index.ts | 3 + .../dashboard/workbench/workbench-header.vue | 46 ++ .../dashboard/workbench/workbench-project.vue | 56 ++ .../workbench/workbench-quick-nav.vue | 47 ++ packages/business/universal-ui/tsconfig.json | 3 + packages/business/widgets/package.json | 2 +- .../widgets/src/coze-assistant/assistant.vue | 4 +- .../src/global-search/search-panel.vue | 7 +- .../preferences/blocks/layout/breadcrumb.vue | 12 +- .../widgets/src/preferences/preferences.vue | 12 +- .../widgets/src/preferences/trigger.vue | 2 +- .../src/user-dropdown/user-dropdown.vue | 2 +- packages/constants/src/_essentials.ts | 2 +- packages/hooks/package.json | 2 +- packages/icons/src/svg/icons/bell.svg | 1 + packages/icons/src/svg/icons/cake.svg | 1 + packages/icons/src/svg/icons/card.svg | 1 + packages/icons/src/svg/icons/download.svg | 1 + packages/icons/src/svg/index.ts | 12 +- packages/icons/src/svg/load.ts | 7 +- packages/locales/package.json | 2 +- packages/locales/src/langs/en-US.yaml | 37 +- packages/locales/src/langs/zh-CN.yaml | 36 +- packages/types/package.json | 2 +- packages/types/src/ui.d.ts | 7 +- packages/types/window.d.ts | 30 +- pnpm-lock.yaml | 585 +++++++++--------- website/package.json | 2 +- 100 files changed, 1908 insertions(+), 1081 deletions(-) create mode 100644 apps/web-antd/src/router/routes/modules/dashboard.ts delete mode 100644 apps/web-antd/src/router/routes/modules/home.ts create mode 100644 apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue create mode 100644 apps/web-antd/src/views/dashboard/analytics/analytics-visits-data.vue create mode 100644 apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue create mode 100644 apps/web-antd/src/views/dashboard/analytics/analytics-visits-source.vue create mode 100644 apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue create mode 100644 apps/web-antd/src/views/dashboard/analytics/index.vue delete mode 100644 apps/web-antd/src/views/dashboard/index.vue create mode 100644 apps/web-antd/src/views/dashboard/workspace/index.vue create mode 100644 packages/@core/shared/toolkit/src/dom.test.ts create mode 100644 packages/@core/shared/toolkit/src/dom.ts create mode 100644 packages/@core/shared/toolkit/src/unique.test.ts create mode 100644 packages/@core/shared/toolkit/src/unique.ts create mode 100644 packages/@core/ui-kit/shadcn-ui/src/components/count-to-animator/count-to-animator.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/components/count-to-animator/index.ts delete mode 100644 packages/business/chart-ui/src/chart.vue create mode 100644 packages/business/chart-ui/src/echarts/echarts-ui.vue create mode 100644 packages/business/chart-ui/src/echarts/echarts.ts create mode 100644 packages/business/chart-ui/src/echarts/index.ts create mode 100644 packages/business/chart-ui/src/echarts/use-echarts.ts create mode 100644 packages/business/universal-ui/src/dashboard/analysis/analysis-chart-card.vue create mode 100644 packages/business/universal-ui/src/dashboard/analysis/analysis-charts-tabs.vue create mode 100644 packages/business/universal-ui/src/dashboard/analysis/analysis-overview.vue create mode 100644 packages/business/universal-ui/src/dashboard/analysis/index.ts delete mode 100644 packages/business/universal-ui/src/dashboard/card.vue delete mode 100644 packages/business/universal-ui/src/dashboard/chartCard.vue delete mode 100644 packages/business/universal-ui/src/dashboard/chartTab.vue delete mode 100644 packages/business/universal-ui/src/dashboard/dashboard.vue delete mode 100644 packages/business/universal-ui/src/dashboard/layout.vue create mode 100644 packages/business/universal-ui/src/dashboard/typing.ts delete mode 100644 packages/business/universal-ui/src/dashboard/typings.ts create mode 100644 packages/business/universal-ui/src/dashboard/workbench/index.ts create mode 100644 packages/business/universal-ui/src/dashboard/workbench/workbench-header.vue create mode 100644 packages/business/universal-ui/src/dashboard/workbench/workbench-project.vue create mode 100644 packages/business/universal-ui/src/dashboard/workbench/workbench-quick-nav.vue create mode 100644 packages/icons/src/svg/icons/bell.svg create mode 100644 packages/icons/src/svg/icons/cake.svg create mode 100644 packages/icons/src/svg/icons/card.svg create mode 100644 packages/icons/src/svg/icons/download.svg diff --git a/apps/web-antd/mock/user.ts b/apps/web-antd/mock/user.ts index e6aa33ac..aa363774 100644 --- a/apps/web-antd/mock/user.ts +++ b/apps/web-antd/mock/user.ts @@ -5,7 +5,7 @@ const fakeUserList = [ accessToken: 'fakeAdminToken', avatar: '', desc: 'manager', - homePath: '/welcome', + homePath: '/', password: '123456', realName: 'Vben Admin', roles: [ @@ -21,7 +21,7 @@ const fakeUserList = [ accessToken: 'fakeTestToken', avatar: '', desc: 'tester', - homePath: '/welcome', + homePath: '/', password: '123456', realName: 'test user', roles: [ diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json index 38e695a1..daab1b9d 100644 --- a/apps/web-antd/package.json +++ b/apps/web-antd/package.json @@ -44,7 +44,7 @@ "ant-design-vue": "^4.2.3", "dayjs": "^1.11.11", "pinia": "2.1.7", - "vue": "^3.4.30", + "vue": "^3.4.31", "vue-router": "^4.4.0" }, "devDependencies": { diff --git a/apps/web-antd/src/router/routes/modules/dashboard.ts b/apps/web-antd/src/router/routes/modules/dashboard.ts new file mode 100644 index 00000000..cea314a3 --- /dev/null +++ b/apps/web-antd/src/router/routes/modules/dashboard.ts @@ -0,0 +1,39 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { $t } from '@vben/locales/helper'; + +import { BasicLayout } from '#/layouts'; + +const routes: RouteRecordRaw[] = [ + { + component: BasicLayout, + meta: { + order: -1, + title: $t('page.dashboard.title'), + }, + name: 'Dashboard', + path: '/', + redirect: '/analytics', + children: [ + { + name: 'Analytics', + path: '/analytics', + component: () => import('#/views/dashboard/analytics/index.vue'), + meta: { + affixTab: true, + title: $t('page.dashboard.analytics'), + }, + }, + { + name: 'Workspace', + path: '/workspace', + component: () => import('#/views/dashboard/workspace/index.vue'), + meta: { + title: $t('page.dashboard.workspace'), + }, + }, + ], + }, +]; + +export default routes; diff --git a/apps/web-antd/src/router/routes/modules/fallback.ts b/apps/web-antd/src/router/routes/modules/fallback.ts index 3ca720cb..58b42b35 100644 --- a/apps/web-antd/src/router/routes/modules/fallback.ts +++ b/apps/web-antd/src/router/routes/modules/fallback.ts @@ -9,7 +9,7 @@ const routes: RouteRecordRaw[] = [ component: BasicLayout, meta: { icon: 'mdi:lightbulb-error-outline', - title: $t('page.fallback.page'), + title: $t('page.fallback.title'), }, name: 'FallbackLayout', path: '/fallback', diff --git a/apps/web-antd/src/router/routes/modules/home.ts b/apps/web-antd/src/router/routes/modules/home.ts deleted file mode 100644 index d509563e..00000000 --- a/apps/web-antd/src/router/routes/modules/home.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { RouteRecordRaw } from 'vue-router'; - -import { BasicLayout } from '#/layouts'; - -const routes: RouteRecordRaw[] = [ - { - component: BasicLayout, - meta: { - hideChildrenInMenu: true, - order: -1, - title: '首页', - }, - name: 'Home', - path: '/', - redirect: '/welcome', - children: [ - { - name: 'Welcome', - path: '/welcome', - component: () => import('#/views/dashboard/index.vue'), - meta: { - affixTab: true, - title: 'Welcome', - }, - }, - ], - }, -]; - -export default routes; diff --git a/apps/web-antd/src/router/routes/modules/nested.ts b/apps/web-antd/src/router/routes/modules/nested.ts index 1b139c55..508f76d7 100644 --- a/apps/web-antd/src/router/routes/modules/nested.ts +++ b/apps/web-antd/src/router/routes/modules/nested.ts @@ -11,7 +11,7 @@ const routes: RouteRecordRaw[] = [ icon: 'ic:round-menu', keepAlive: true, order: 1000, - title: $t('page.nested.page'), + title: $t('page.nested.title'), }, name: 'Nested', path: '/nested', diff --git a/apps/web-antd/src/router/routes/modules/outside.ts b/apps/web-antd/src/router/routes/modules/outside.ts index 95fbe5a7..de4906cd 100644 --- a/apps/web-antd/src/router/routes/modules/outside.ts +++ b/apps/web-antd/src/router/routes/modules/outside.ts @@ -9,7 +9,7 @@ const routes: RouteRecordRaw[] = [ component: BasicLayout, meta: { icon: 'ic:round-settings-input-composite', - title: $t('page.outside.page'), + title: $t('page.outside.title'), }, name: 'Outside', path: '/outside', diff --git a/apps/web-antd/src/store/index.ts b/apps/web-antd/src/store/index.ts index dc80b39f..da5990b1 100644 --- a/apps/web-antd/src/store/index.ts +++ b/apps/web-antd/src/store/index.ts @@ -2,7 +2,7 @@ import type { InitStoreOptions } from '@vben-core/stores'; import type { App } from 'vue'; -import { initStore } from '@vben-core/stores'; +import { initStore, useAccessStore, useTabsStore } from '@vben-core/stores'; /** * @zh_CN 初始化pinia @@ -13,4 +13,4 @@ async function setupStore(app: App, options: InitStoreOptions) { app.use(pinia); } -export { setupStore }; +export { setupStore, useAccessStore, useTabsStore }; diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue new file mode 100644 index 00000000..ab2147fe --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue @@ -0,0 +1,80 @@ + + + diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-visits-data.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-data.vue new file mode 100644 index 00000000..43674a84 --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-data.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue new file mode 100644 index 00000000..9be875c8 --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue @@ -0,0 +1,46 @@ + + + diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-visits-source.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-source.vue new file mode 100644 index 00000000..8cf14cc1 --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-visits-source.vue @@ -0,0 +1,65 @@ + + + diff --git a/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue b/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue new file mode 100644 index 00000000..27d2024b --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue @@ -0,0 +1,55 @@ + + + diff --git a/apps/web-antd/src/views/dashboard/analytics/index.vue b/apps/web-antd/src/views/dashboard/analytics/index.vue new file mode 100644 index 00000000..b088219d --- /dev/null +++ b/apps/web-antd/src/views/dashboard/analytics/index.vue @@ -0,0 +1,92 @@ + + + diff --git a/apps/web-antd/src/views/dashboard/index.vue b/apps/web-antd/src/views/dashboard/index.vue deleted file mode 100644 index 2d4e650a..00000000 --- a/apps/web-antd/src/views/dashboard/index.vue +++ /dev/null @@ -1,250 +0,0 @@ - - - diff --git a/apps/web-antd/src/views/dashboard/workspace/index.vue b/apps/web-antd/src/views/dashboard/workspace/index.vue new file mode 100644 index 00000000..b381d8f4 --- /dev/null +++ b/apps/web-antd/src/views/dashboard/workspace/index.vue @@ -0,0 +1,125 @@ + + + diff --git a/cspell.json b/cspell.json index bc0cf9be..13147587 100644 --- a/cspell.json +++ b/cspell.json @@ -5,6 +5,7 @@ "words": [ "clsx", "esno", + "unref", "taze", "acmr", "antd", diff --git a/internal/node-utils/package.json b/internal/node-utils/package.json index 9daa2bcc..86285f3f 100644 --- a/internal/node-utils/package.json +++ b/internal/node-utils/package.json @@ -29,7 +29,7 @@ }, "dependencies": { "@changesets/git": "^3.0.0", - "@manypkg/get-packages": "^2.2.1", + "@manypkg/get-packages": "^2.2.2", "consola": "^3.2.3", "dayjs": "^1.11.11", "find-up": "^7.0.0", diff --git a/internal/tailwind-config/package.json b/internal/tailwind-config/package.json index a44d4f9b..30736f97 100644 --- a/internal/tailwind-config/package.json +++ b/internal/tailwind-config/package.json @@ -45,7 +45,7 @@ "tailwindcss": "^3.4.3" }, "dependencies": { - "@iconify/json": "^2.2.222", + "@iconify/json": "^2.2.223", "@iconify/tailwind": "^1.1.1", "@tailwindcss/forms": "^0.5.7", "@tailwindcss/nesting": "0.0.0-insiders.565cd3e", diff --git a/internal/tsconfig/package.json b/internal/tsconfig/package.json index e1d626ff..721458b9 100644 --- a/internal/tsconfig/package.json +++ b/internal/tsconfig/package.json @@ -20,6 +20,6 @@ ], "dependencies": { "@vben/types": "workspace:*", - "vite": "^5.3.1" + "vite": "^5.3.2" } } diff --git a/internal/tsconfig/web.json b/internal/tsconfig/web.json index a0f74e19..a4b60cec 100644 --- a/internal/tsconfig/web.json +++ b/internal/tsconfig/web.json @@ -8,7 +8,7 @@ "lib": ["ESNext", "DOM", "DOM.Iterable"], "useDefineForClassFields": true, "moduleResolution": "bundler", - "types": ["vite/client", "@vben/types/window"], + "types": ["vite/client"], "declaration": false } } diff --git a/internal/vite-config/package.json b/internal/vite-config/package.json index 89c794ae..083d92e7 100644 --- a/internal/vite-config/package.json +++ b/internal/vite-config/package.json @@ -46,8 +46,8 @@ "rollup": "^4.18.0", "rollup-plugin-visualizer": "^5.12.0", "sass": "^1.77.6", - "unplugin-turbo-console": "^1.8.8-beta.1", - "vite": "^5.3.1", + "unplugin-turbo-console": "^1.8.9", + "vite": "^5.3.2", "vite-plugin-compression": "^0.5.1", "vite-plugin-dts": "^3.9.1", "vite-plugin-html": "^3.2.2", diff --git a/internal/vite-config/src/typing.ts b/internal/vite-config/src/typing.ts index 89e4d58e..428a56c2 100644 --- a/internal/vite-config/src/typing.ts +++ b/internal/vite-config/src/typing.ts @@ -43,7 +43,7 @@ interface CommonPluginOptions { /** 环境变量 */ env?: Record; /** 是否开启注入metadata */ - injectMetadata: boolean; + injectMetadata?: boolean; /** 是否构建模式 */ isBuild?: boolean; /** 构建模式 */ diff --git a/package.json b/package.json index 428ec383..043021c3 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "turbo": "^2.0.5", "typescript": "^5.5.2", "unbuild": "^2.0.0", - "vite": "^5.3.1", + "vite": "^5.3.2", "vitest": "^2.0.0-beta.10", "vue-tsc": "^2.0.22" }, @@ -86,7 +86,7 @@ "@ant-design/colors": "^7.0.2", "@ctrl/tinycolor": "^4.1.0", "clsx": "^2.1.1", - "vue": "^3.4.30" + "vue": "^3.4.31" }, "neverBuiltDependencies": [ "canvas", diff --git a/packages/@core/forward/preferences/package.json b/packages/@core/forward/preferences/package.json index 729a074d..f8b9604b 100644 --- a/packages/@core/forward/preferences/package.json +++ b/packages/@core/forward/preferences/package.json @@ -34,6 +34,6 @@ "@vben-core/toolkit": "workspace:*", "@vben-core/typings": "workspace:*", "@vueuse/core": "^10.11.0", - "vue": "^3.4.30" + "vue": "^3.4.31" } } diff --git a/packages/@core/forward/preferences/src/config.ts b/packages/@core/forward/preferences/src/config.ts index bd62e18c..e110c655 100644 --- a/packages/@core/forward/preferences/src/config.ts +++ b/packages/@core/forward/preferences/src/config.ts @@ -27,7 +27,7 @@ const defaultPreferences: Preferences = { styleType: 'normal', }, footer: { - enable: true, + enable: false, fixed: true, }, header: { diff --git a/packages/@core/forward/preferences/src/preferences.test.ts b/packages/@core/forward/preferences/src/preferences.test.ts index 885840f3..b2628808 100644 --- a/packages/@core/forward/preferences/src/preferences.test.ts +++ b/packages/@core/forward/preferences/src/preferences.test.ts @@ -36,7 +36,7 @@ describe('preferences', () => { }); it('initPreferences should initialize preferences with overrides and namespace', async () => { - const overrides = { theme: { colorPrimary: 'hsl(211 91% 39%)' } }; + const overrides = { theme: { colorPrimary: 'hsl(245 82% 67%)' } }; const namespace = 'testNamespace'; await preferenceManager.initPreferences({ namespace, overrides }); diff --git a/packages/@core/forward/stores/package.json b/packages/@core/forward/stores/package.json index b9e75d78..d17e362e 100644 --- a/packages/@core/forward/stores/package.json +++ b/packages/@core/forward/stores/package.json @@ -42,7 +42,7 @@ "@vben-core/typings": "workspace:*", "pinia": "2.1.7", "pinia-plugin-persistedstate": "^3.2.1", - "vue": "^3.4.30", + "vue": "^3.4.31", "vue-router": "^4.4.0" } } diff --git a/packages/@core/forward/stores/src/modules/tabs.ts b/packages/@core/forward/stores/src/modules/tabs.ts index d9e962f8..eb0eda12 100644 --- a/packages/@core/forward/stores/src/modules/tabs.ts +++ b/packages/@core/forward/stores/src/modules/tabs.ts @@ -1,9 +1,9 @@ +import type { TabItem } from '@vben-core/typings'; import type { RouteRecordNormalized, Router } from 'vue-router'; import { toRaw } from 'vue'; import { startProgress, stopProgress } from '@vben-core/toolkit'; -import { TabItem } from '@vben-core/typings'; import { acceptHMRUpdate, defineStore } from 'pinia'; diff --git a/packages/@core/shared/iconify/package.json b/packages/@core/shared/iconify/package.json index 6955650a..9717f9ed 100644 --- a/packages/@core/shared/iconify/package.json +++ b/packages/@core/shared/iconify/package.json @@ -22,6 +22,6 @@ }, "dependencies": { "@iconify/vue": "^4.1.2", - "vue": "^3.4.30" + "vue": "^3.4.31" } } diff --git a/packages/@core/shared/iconify/src/factory.ts b/packages/@core/shared/iconify/src/factory.ts index b4323cd3..2b34d6f7 100644 --- a/packages/@core/shared/iconify/src/factory.ts +++ b/packages/@core/shared/iconify/src/factory.ts @@ -4,6 +4,7 @@ import { Icon } from '@iconify/vue'; function createIcon(name: string) { return defineComponent({ + name: `SvgIcon-${name}`, setup(props, { attrs }) { return () => h(Icon, { icon: name, ...props, ...attrs }); }, diff --git a/packages/@core/shared/toolkit/package.json b/packages/@core/shared/toolkit/package.json index f28f5217..f871efcc 100644 --- a/packages/@core/shared/toolkit/package.json +++ b/packages/@core/shared/toolkit/package.json @@ -36,7 +36,7 @@ } }, "dependencies": { - "@vue/shared": "^3.4.30", + "@vue/shared": "^3.4.31", "clsx": "2.1.1", "defu": "^6.1.4", "nprogress": "^0.2.0", diff --git a/packages/@core/shared/toolkit/src/dom.test.ts b/packages/@core/shared/toolkit/src/dom.test.ts new file mode 100644 index 00000000..c95ec4c7 --- /dev/null +++ b/packages/@core/shared/toolkit/src/dom.test.ts @@ -0,0 +1,140 @@ +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; + +import { getElementVisibleHeight } from './dom'; // 假设函数位于 utils.ts 中 + +describe('getElementVisibleHeight', () => { + // Mocking the getBoundingClientRect method + const mockGetBoundingClientRect = vi.fn(); + const originalGetBoundingClientRect = Element.prototype.getBoundingClientRect; + + beforeAll(() => { + // Mock getBoundingClientRect method + Element.prototype.getBoundingClientRect = mockGetBoundingClientRect; + }); + + afterAll(() => { + // Restore original getBoundingClientRect method + Element.prototype.getBoundingClientRect = originalGetBoundingClientRect; + }); + + it('should return 0 if the element is null or undefined', () => { + expect(getElementVisibleHeight(null)).toBe(0); + expect(getElementVisibleHeight()).toBe(0); + }); + + it('should return the visible height of the element', () => { + // Mock the getBoundingClientRect return value + mockGetBoundingClientRect.mockReturnValue({ + bottom: 500, + height: 400, + left: 0, + right: 0, + toJSON: () => ({}), + top: 100, + width: 0, + x: 0, + y: 0, + }); + + const mockElement = document.createElement('div'); + document.body.append(mockElement); + + // Mocking window.innerHeight and document.documentElement.clientHeight + const originalInnerHeight = window.innerHeight; + const originalClientHeight = document.documentElement.clientHeight; + + Object.defineProperty(window, 'innerHeight', { + value: 600, + writable: true, + }); + + Object.defineProperty(document.documentElement, 'clientHeight', { + value: 600, + writable: true, + }); + + expect(getElementVisibleHeight(mockElement)).toBe(400); + + // Restore original values + Object.defineProperty(window, 'innerHeight', { + value: originalInnerHeight, + writable: true, + }); + + Object.defineProperty(document.documentElement, 'clientHeight', { + value: originalClientHeight, + writable: true, + }); + + mockElement.remove(); + }); + + it('should return the visible height when element is partially out of viewport', () => { + // Mock the getBoundingClientRect return value + mockGetBoundingClientRect.mockReturnValue({ + bottom: 300, + height: 400, + left: 0, + right: 0, + toJSON: () => ({}), + top: -100, + width: 0, + x: 0, + y: 0, + }); + + const mockElement = document.createElement('div'); + document.body.append(mockElement); + + // Mocking window.innerHeight and document.documentElement.clientHeight + const originalInnerHeight = window.innerHeight; + const originalClientHeight = document.documentElement.clientHeight; + + Object.defineProperty(window, 'innerHeight', { + value: 600, + writable: true, + }); + + Object.defineProperty(document.documentElement, 'clientHeight', { + value: 600, + writable: true, + }); + + expect(getElementVisibleHeight(mockElement)).toBe(300); + + // Restore original values + Object.defineProperty(window, 'innerHeight', { + value: originalInnerHeight, + writable: true, + }); + + Object.defineProperty(document.documentElement, 'clientHeight', { + value: originalClientHeight, + writable: true, + }); + + mockElement.remove(); + }); + + it('should return 0 if the element is completely out of viewport', () => { + // Mock the getBoundingClientRect return value + mockGetBoundingClientRect.mockReturnValue({ + bottom: -100, + height: 400, + left: 0, + right: 0, + toJSON: () => ({}), + top: -500, + width: 0, + x: 0, + y: 0, + }); + + const mockElement = document.createElement('div'); + document.body.append(mockElement); + + expect(getElementVisibleHeight(mockElement)).toBe(0); + + mockElement.remove(); + }); +}); diff --git a/packages/@core/shared/toolkit/src/dom.ts b/packages/@core/shared/toolkit/src/dom.ts new file mode 100644 index 00000000..f7321d46 --- /dev/null +++ b/packages/@core/shared/toolkit/src/dom.ts @@ -0,0 +1,24 @@ +/** + * 获取元素可见高度 + * @param element + * @returns + */ +function getElementVisibleHeight( + element?: HTMLElement | null | undefined, +): number { + if (!element) { + return 0; + } + const rect = element.getBoundingClientRect(); + const viewHeight = Math.max( + document.documentElement.clientHeight, + window.innerHeight, + ); + + const top = Math.max(rect.top, 0); + const bottom = Math.min(rect.bottom, viewHeight); + + return Math.max(0, bottom - top); +} + +export { getElementVisibleHeight }; diff --git a/packages/@core/shared/toolkit/src/index.ts b/packages/@core/shared/toolkit/src/index.ts index 137d2c2f..f49beebc 100644 --- a/packages/@core/shared/toolkit/src/index.ts +++ b/packages/@core/shared/toolkit/src/index.ts @@ -1,5 +1,6 @@ export * from './cn'; export * from './diff'; +export * from './dom'; export * from './hash'; export * from './inference'; export * from './letter'; @@ -7,5 +8,6 @@ export * from './merge'; export * from './namespace'; export * from './nprogress'; export * from './tree'; +export * from './unique'; export * from './update-css-variables'; export * from './window'; diff --git a/packages/@core/shared/toolkit/src/inference.ts b/packages/@core/shared/toolkit/src/inference.ts index 0e805776..4e33ba2c 100644 --- a/packages/@core/shared/toolkit/src/inference.ts +++ b/packages/@core/shared/toolkit/src/inference.ts @@ -97,11 +97,20 @@ function isWindowsOs(): boolean { return windowsRegex.test(navigator.userAgent); } +/** + * 检查传入的值是否为数字 + * @param value + */ +function isNumber(value: any): value is number { + return typeof value === 'number' && Number.isFinite(value); +} + export { isEmpty, isFunction, isHttpUrl, isMacOs, + isNumber, isObject, isString, isUndefined, diff --git a/packages/@core/shared/toolkit/src/unique.test.ts b/packages/@core/shared/toolkit/src/unique.test.ts new file mode 100644 index 00000000..50f48bbf --- /dev/null +++ b/packages/@core/shared/toolkit/src/unique.test.ts @@ -0,0 +1,61 @@ +import { describe, expect, it } from 'vitest'; + +import { uniqueByField } from './unique'; + +describe('uniqueByField', () => { + it('should return an array with unique items based on id field', () => { + const items = [ + { id: 1, name: 'Item 1' }, + { id: 2, name: 'Item 2' }, + { id: 3, name: 'Item 3' }, + { id: 1, name: 'Duplicate Item' }, + ]; + + const uniqueItems = uniqueByField(items, 'id'); + + // Assert expected results + expect(uniqueItems).toHaveLength(3); // After deduplication, there should be three objects left + expect(uniqueItems).toEqual([ + { id: 1, name: 'Item 1' }, + { id: 2, name: 'Item 2' }, + { id: 3, name: 'Item 3' }, + ]); + }); + + it('should return an empty array when input array is empty', () => { + const items: any[] = []; // Empty array + + const uniqueItems = uniqueByField(items, 'id'); + + // Assert expected results + expect(uniqueItems).toEqual([]); + }); + + it('should handle arrays with only one item correctly', () => { + const items = [{ id: 1, name: 'Item 1' }]; + + const uniqueItems = uniqueByField(items, 'id'); + + // Assert expected results + expect(uniqueItems).toHaveLength(1); + expect(uniqueItems).toEqual([{ id: 1, name: 'Item 1' }]); + }); + + it('should preserve the order of the first occurrence of each item', () => { + const items = [ + { id: 2, name: 'Item 2' }, + { id: 1, name: 'Item 1' }, + { id: 3, name: 'Item 3' }, + { id: 1, name: 'Duplicate Item' }, + ]; + + const uniqueItems = uniqueByField(items, 'id'); + + // Assert expected results (order of first occurrences preserved) + expect(uniqueItems).toEqual([ + { id: 2, name: 'Item 2' }, + { id: 1, name: 'Item 1' }, + { id: 3, name: 'Item 3' }, + ]); + }); +}); diff --git a/packages/@core/shared/toolkit/src/unique.ts b/packages/@core/shared/toolkit/src/unique.ts new file mode 100644 index 00000000..e81f972c --- /dev/null +++ b/packages/@core/shared/toolkit/src/unique.ts @@ -0,0 +1,15 @@ +/** + * 根据指定字段对对象数组进行去重 + * @param arr 要去重的对象数组 + * @param key 去重依据的字段名 + * @returns 去重后的对象数组 + */ +function uniqueByField(arr: T[], key: keyof T): T[] { + const seen = new Map(); + return arr.filter((item) => { + const value = item[key]; + return seen.has(value) ? false : (seen.set(value, item), true); + }); +} + +export { uniqueByField }; diff --git a/packages/@core/shared/typings/package.json b/packages/@core/shared/typings/package.json index 0c63d3c6..367376fc 100644 --- a/packages/@core/shared/typings/package.json +++ b/packages/@core/shared/typings/package.json @@ -39,7 +39,7 @@ } }, "dependencies": { - "vue": "^3.4.30", + "vue": "^3.4.31", "vue-router": "^4.4.0" } } diff --git a/packages/@core/ui-kit/layout-ui/package.json b/packages/@core/ui-kit/layout-ui/package.json index 08bb3891..c5e993d0 100644 --- a/packages/@core/ui-kit/layout-ui/package.json +++ b/packages/@core/ui-kit/layout-ui/package.json @@ -39,8 +39,9 @@ "dependencies": { "@vben-core/iconify": "workspace:*", "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/toolkit": "workspace:*", "@vben-core/typings": "workspace:*", "@vueuse/core": "^10.11.0", - "vue": "^3.4.30" + "vue": "^3.4.31" } } diff --git a/packages/@core/ui-kit/layout-ui/src/components/layout-content.vue b/packages/@core/ui-kit/layout-ui/src/components/layout-content.vue index 533faab7..37f37b3e 100644 --- a/packages/@core/ui-kit/layout-ui/src/components/layout-content.vue +++ b/packages/@core/ui-kit/layout-ui/src/components/layout-content.vue @@ -4,6 +4,8 @@ import type { ContentCompactType } from '@vben-core/typings'; import type { CSSProperties } from 'vue'; import { computed, onMounted, ref, watch } from 'vue'; +import { getElementVisibleHeight } from '@vben-core/toolkit'; + import { useCssVar, useDebounceFn, useWindowSize } from '@vueuse/core'; interface Props { @@ -54,12 +56,12 @@ const props = withDefaults(defineProps(), { paddingTop: 16, }); -const domElement = ref(); +const contentElement = ref(); const { height, width } = useWindowSize(); const contentClientHeight = useCssVar('--vben-content-client-height'); const debouncedCalcHeight = useDebounceFn(() => { - contentClientHeight.value = `${domElement.value?.clientHeight ?? window.innerHeight}px`; + contentClientHeight.value = `${getElementVisibleHeight(contentElement.value)}px`; }, 200); const style = computed((): CSSProperties => { @@ -97,7 +99,7 @@ onMounted(() => { diff --git a/packages/@core/ui-kit/menu-ui/package.json b/packages/@core/ui-kit/menu-ui/package.json index dd049d18..6421e71e 100644 --- a/packages/@core/ui-kit/menu-ui/package.json +++ b/packages/@core/ui-kit/menu-ui/package.json @@ -43,6 +43,6 @@ "@vben-core/toolkit": "workspace:*", "@vben-core/typings": "workspace:*", "@vueuse/core": "^10.11.0", - "vue": "^3.4.30" + "vue": "^3.4.31" } } diff --git a/packages/@core/ui-kit/shadcn-ui/package.json b/packages/@core/ui-kit/shadcn-ui/package.json index bd653b67..c29090f6 100644 --- a/packages/@core/ui-kit/shadcn-ui/package.json +++ b/packages/@core/ui-kit/shadcn-ui/package.json @@ -50,7 +50,7 @@ "@vueuse/core": "^10.11.0", "class-variance-authority": "^0.7.0", "radix-vue": "^1.8.5", - "vue": "^3.4.30", + "vue": "^3.4.31", "vue-sonner": "^1.1.3" } } diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/back-top/back-top.vue b/packages/@core/ui-kit/shadcn-ui/src/components/back-top/back-top.vue index d6472ea7..5a162f31 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/components/back-top/back-top.vue +++ b/packages/@core/ui-kit/shadcn-ui/src/components/back-top/back-top.vue @@ -13,7 +13,7 @@ interface Props extends BacktopProps {} defineOptions({ name: 'BackTop' }); const props = withDefaults(defineProps(), { - bottom: 40, + bottom: 24, isGroup: false, right: 40, target: '', @@ -32,7 +32,7 @@ const { handleClick, visible } = useBackTop(props); +import { computed, onMounted, ref, unref, watch, watchEffect } from 'vue'; + +import { isNumber } from '@vben-core/toolkit'; + +import { TransitionPresets, useTransition } from '@vueuse/core'; + +interface Props { + autoplay?: boolean; + color?: string; + decimal?: string; + decimals?: number; + duration?: number; + endVal?: number; + prefix?: string; + separator?: string; + startVal?: number; + suffix?: string; + transition?: keyof typeof TransitionPresets; + useEasing?: boolean; +} + +defineOptions({ name: 'CountToAnimator' }); + +const props = withDefaults(defineProps(), { + autoplay: true, + color: '', + decimal: '.', + decimals: 0, + duration: 1500, + endVal: 2021, + prefix: '', + separator: ',', + startVal: 0, + suffix: '', + transition: 'linear', + useEasing: true, +}); + +const emit = defineEmits(['onStarted', 'onFinished']); + +const source = ref(props.startVal); +const disabled = ref(false); +let outputValue = useTransition(source); + +const value = computed(() => formatNumber(unref(outputValue))); + +watchEffect(() => { + source.value = props.startVal; +}); + +watch([() => props.startVal, () => props.endVal], () => { + if (props.autoplay) { + start(); + } +}); + +onMounted(() => { + props.autoplay && start(); +}); + +function start() { + run(); + source.value = props.endVal; +} + +function reset() { + source.value = props.startVal; + run(); +} + +function run() { + outputValue = useTransition(source, { + disabled, + duration: props.duration, + onFinished: () => emit('onFinished'), + onStarted: () => emit('onStarted'), + ...(props.useEasing + ? { transition: TransitionPresets[props.transition] } + : {}), + }); +} + +function formatNumber(num: number | string) { + if (!num && num !== 0) { + return ''; + } + const { decimal, decimals, prefix, separator, suffix } = props; + num = Number(num).toFixed(decimals); + num += ''; + + const x = num.split('.'); + let x1 = x[0]; + const x2 = x.length > 1 ? decimal + x[1] : ''; + + const rgx = /(\d+)(\d{3})/; + if (separator && !isNumber(separator)) { + while (rgx.test(x1)) { + x1 = x1.replace(rgx, `$1${separator}$2`); + } + } + return prefix + x1 + x2 + suffix; +} + +defineExpose({ reset }); + + diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/count-to-animator/index.ts b/packages/@core/ui-kit/shadcn-ui/src/components/count-to-animator/index.ts new file mode 100644 index 00000000..a97bafbf --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/components/count-to-animator/index.ts @@ -0,0 +1 @@ +export { default as VbenCountToAnimator } from './count-to-animator.vue'; diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/index.ts b/packages/@core/ui-kit/shadcn-ui/src/components/index.ts index a7ca8509..dc04c2ba 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/components/index.ts +++ b/packages/@core/ui-kit/shadcn-ui/src/components/index.ts @@ -6,6 +6,7 @@ export * from './breadcrumb'; export * from './button'; export * from './checkbox'; export * from './context-menu'; +export * from './count-to-animator'; export * from './dropdown-menu'; export * from './floating-button-group'; export * from './full-screen'; diff --git a/packages/@core/ui-kit/tabs-ui/package.json b/packages/@core/ui-kit/tabs-ui/package.json index de596177..c137f443 100644 --- a/packages/@core/ui-kit/tabs-ui/package.json +++ b/packages/@core/ui-kit/tabs-ui/package.json @@ -42,6 +42,6 @@ "@vben-core/shadcn-ui": "workspace:*", "@vben-core/toolkit": "workspace:*", "@vben-core/typings": "workspace:*", - "vue": "^3.4.30" + "vue": "^3.4.31" } } diff --git a/packages/business/chart-ui/package.json b/packages/business/chart-ui/package.json index d44f3221..5f546420 100644 --- a/packages/business/chart-ui/package.json +++ b/packages/business/chart-ui/package.json @@ -41,7 +41,8 @@ }, "dependencies": { "@vben-core/preferences": "workspace:*", - "echarts": "^5.5.0", - "vue": "^3.4.30" + "@vueuse/core": "^10.11.0", + "echarts": "^5.5.1", + "vue": "^3.4.31" } } diff --git a/packages/business/chart-ui/src/chart.vue b/packages/business/chart-ui/src/chart.vue deleted file mode 100644 index d7134c97..00000000 --- a/packages/business/chart-ui/src/chart.vue +++ /dev/null @@ -1,37 +0,0 @@ - - - diff --git a/packages/business/chart-ui/src/echarts/echarts-ui.vue b/packages/business/chart-ui/src/echarts/echarts-ui.vue new file mode 100644 index 00000000..70d1f20b --- /dev/null +++ b/packages/business/chart-ui/src/echarts/echarts-ui.vue @@ -0,0 +1,15 @@ + + + diff --git a/packages/business/chart-ui/src/echarts/echarts.ts b/packages/business/chart-ui/src/echarts/echarts.ts new file mode 100644 index 00000000..042ac4eb --- /dev/null +++ b/packages/business/chart-ui/src/echarts/echarts.ts @@ -0,0 +1,59 @@ +import type { + // 系列类型的定义后缀都为 SeriesOption + BarSeriesOption, + LineSeriesOption, +} from 'echarts/charts'; +import type { + DatasetComponentOption, + GridComponentOption, + // 组件类型的定义后缀都为 ComponentOption + TitleComponentOption, + TooltipComponentOption, +} from 'echarts/components'; +import type { ComposeOption } from 'echarts/core'; + +import { BarChart, LineChart, PieChart, RadarChart } from 'echarts/charts'; +import { + // 数据集组件 + DatasetComponent, + GridComponent, + LegendComponent, + TitleComponent, + ToolboxComponent, + TooltipComponent, + // 内置数据转换器组件 (filter, sort) + TransformComponent, +} from 'echarts/components'; +import * as echarts from 'echarts/core'; +import { LabelLayout, UniversalTransition } from 'echarts/features'; +import { CanvasRenderer } from 'echarts/renderers'; + +// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型 +export type ECOption = ComposeOption< + | BarSeriesOption + | DatasetComponentOption + | GridComponentOption + | LineSeriesOption + | TitleComponentOption + | TooltipComponentOption +>; + +// 注册必须的组件 +echarts.use([ + TitleComponent, + PieChart, + RadarChart, + TooltipComponent, + GridComponent, + DatasetComponent, + TransformComponent, + BarChart, + LineChart, + LabelLayout, + UniversalTransition, + CanvasRenderer, + LegendComponent, + ToolboxComponent, +]); + +export { echarts }; diff --git a/packages/business/chart-ui/src/echarts/index.ts b/packages/business/chart-ui/src/echarts/index.ts new file mode 100644 index 00000000..80f36a13 --- /dev/null +++ b/packages/business/chart-ui/src/echarts/index.ts @@ -0,0 +1,3 @@ +export * from './echarts'; +export { default as EchartsUI } from './echarts-ui.vue'; +export * from './use-echarts'; diff --git a/packages/business/chart-ui/src/echarts/use-echarts.ts b/packages/business/chart-ui/src/echarts/use-echarts.ts new file mode 100644 index 00000000..7e781592 --- /dev/null +++ b/packages/business/chart-ui/src/echarts/use-echarts.ts @@ -0,0 +1,108 @@ +import type { EChartsOption } from 'echarts'; + +import type EchartsUI from './echarts-ui.vue'; + +import type { Ref } from 'vue'; +import { computed, nextTick, watch } from 'vue'; + +import { usePreferences } from '@vben-core/preferences'; + +import { + tryOnUnmounted, + useDebounceFn, + useTimeoutFn, + useWindowSize, +} from '@vueuse/core'; + +import { echarts } from './echarts'; + +type EchartsUIType = typeof EchartsUI | undefined; + +type EchartsThemeType = 'dark' | 'light' | null; + +function useEcharts(chartRef: Ref) { + let chartInstance: echarts.ECharts | null = null; + let cacheOptions: EChartsOption = {}; + + const { isDark } = usePreferences(); + const { height, width } = useWindowSize(); + const resizeHandler: () => void = useDebounceFn(resize, 200); + + const getOptions = computed((): EChartsOption => { + if (!isDark.value) { + return cacheOptions; + } + + return { + backgroundColor: 'transparent', + ...cacheOptions, + }; + }); + + const initCharts = (t?: EchartsThemeType) => { + const el = chartRef?.value?.$el; + if (!el) { + return; + } + chartInstance = echarts.init(el, t || isDark.value ? 'dark' : null); + + return chartInstance; + }; + + const renderEcharts = (options: EChartsOption, clear = true) => { + cacheOptions = options; + return new Promise((resolve) => { + if (chartRef.value?.offsetHeight === 0) { + useTimeoutFn(() => { + renderEcharts(getOptions.value); + resolve(null); + }, 30); + return; + } + nextTick(() => { + useTimeoutFn(() => { + if (!chartInstance) { + const instance = initCharts(); + if (!instance) return; + } + clear && chartInstance?.clear(); + chartInstance?.setOption(getOptions.value); + resolve(null); + }, 30); + }); + }); + }; + + function resize() { + chartInstance?.resize({ + animation: { + duration: 300, + easing: 'quadraticIn', + }, + }); + } + + watch([width, height], () => { + resizeHandler?.(); + }); + + watch(isDark, () => { + if (chartInstance) { + chartInstance.dispose(); + initCharts(); + renderEcharts(cacheOptions); + } + }); + + tryOnUnmounted(() => { + // 销毁实例,释放资源 + chartInstance?.dispose(); + }); + return { + renderEcharts, + }; +} + +export { useEcharts }; + +export type { EchartsUIType }; diff --git a/packages/business/chart-ui/src/index.ts b/packages/business/chart-ui/src/index.ts index a2ab1e05..2aaf5c8c 100644 --- a/packages/business/chart-ui/src/index.ts +++ b/packages/business/chart-ui/src/index.ts @@ -1,59 +1 @@ -import * as echarts from 'echarts/core'; -import { BarChart, LineChart, PieChart, RadarChart } from 'echarts/charts'; -import { - TitleComponent, - TooltipComponent, - GridComponent, - - // 数据集组件 - DatasetComponent, - // 内置数据转换器组件 (filter, sort) - TransformComponent, - LegendComponent, - ToolboxComponent, -} from 'echarts/components'; -import { LabelLayout, UniversalTransition } from 'echarts/features'; -import { CanvasRenderer } from 'echarts/renderers'; -import type { - // 系列类型的定义后缀都为 SeriesOption - BarSeriesOption, - LineSeriesOption, -} from 'echarts/charts'; -import type { - // 组件类型的定义后缀都为 ComponentOption - TitleComponentOption, - TooltipComponentOption, - GridComponentOption, - DatasetComponentOption, -} from 'echarts/components'; -import type { ComposeOption } from 'echarts/core'; - -// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型 -export type ECOption = ComposeOption< - | BarSeriesOption - | LineSeriesOption - | TitleComponentOption - | TooltipComponentOption - | GridComponentOption - | DatasetComponentOption ->; - -// 注册必须的组件 -echarts.use([ - TitleComponent, - PieChart, - RadarChart, - TooltipComponent, - GridComponent, - DatasetComponent, - TransformComponent, - BarChart, - LineChart, - LabelLayout, - UniversalTransition, - CanvasRenderer, - LegendComponent, - ToolboxComponent, -]); -export const echartsInstance = echarts; -export { default as chart } from './chart.vue'; +export * from './echarts'; diff --git a/packages/business/layouts/package.json b/packages/business/layouts/package.json index f68bd4fb..1fa865f7 100644 --- a/packages/business/layouts/package.json +++ b/packages/business/layouts/package.json @@ -46,9 +46,10 @@ "@vben-core/stores": "workspace:*", "@vben-core/tabs-ui": "workspace:*", "@vben-core/toolkit": "workspace:*", + "@vben/constants": "workspace:*", "@vben/locales": "workspace:*", "@vben/widgets": "workspace:*", - "vue": "^3.4.30", + "vue": "^3.4.31", "vue-router": "^4.4.0" }, "devDependencies": { diff --git a/packages/business/layouts/src/basic/content/content.vue b/packages/business/layouts/src/basic/content/content.vue index 2e279d36..4dd00e24 100644 --- a/packages/business/layouts/src/basic/content/content.vue +++ b/packages/business/layouts/src/basic/content/content.vue @@ -10,8 +10,8 @@ import { useContentSpinner } from './use-content-spinner'; defineOptions({ name: 'LayoutContent' }); -const { keepAlive } = usePreferences(); const tabsStore = useTabsStore(); +const { keepAlive } = usePreferences(); const { spinning } = useContentSpinner(); const { getCacheTabs, getExcludeTabs, renderRouteView } = diff --git a/packages/business/layouts/src/basic/widgets/breadcrumb.vue b/packages/business/layouts/src/basic/widgets/breadcrumb.vue index bc4d5215..7a43d08a 100644 --- a/packages/business/layouts/src/basic/widgets/breadcrumb.vue +++ b/packages/business/layouts/src/basic/widgets/breadcrumb.vue @@ -64,6 +64,7 @@ const breadcrumbs = computed((): IBreadcrumb[] => { if (props.hideWhenOnlyOne && resultBreadcrumb.length === 1) { return []; } + return resultBreadcrumb; }); diff --git a/packages/business/universal-ui/package.json b/packages/business/universal-ui/package.json index 500fd082..4a2eee9d 100644 --- a/packages/business/universal-ui/package.json +++ b/packages/business/universal-ui/package.json @@ -46,9 +46,10 @@ "@vben-core/shadcn-ui": "workspace:*", "@vben/chart-ui": "workspace:*", "@vben/locales": "workspace:*", + "@vben/types": "workspace:*", "@vueuse/integrations": "^10.11.0", "qrcode": "^1.5.3", - "vue": "^3.4.30", + "vue": "^3.4.31", "vue-router": "^4.4.0" }, "devDependencies": { diff --git a/packages/business/universal-ui/src/about/about.vue b/packages/business/universal-ui/src/about/about.vue index 51c4dfd8..dcb962dc 100644 --- a/packages/business/universal-ui/src/about/about.vue +++ b/packages/business/universal-ui/src/about/about.vue @@ -13,9 +13,9 @@ defineOptions({ withDefaults(defineProps(), { description: - '是一个基于Vue3.0、Vite 、TypeScript 等前沿技术的后台解决方案,目标是为服务中大型项目开发,提供现成的开箱解决方案及丰富的示例。', + '是一个现代化开箱即用的中后台解决方案,采用最新的技术栈,包括 Vue 3.0、Vite、TailwindCSS 和 TypeScript 等前沿技术,代码规范严谨,提供丰富的配置选项,旨在为中大型项目的开发提供现成的开箱即用解决方案及丰富的示例,同时,它也是学习和深入前端技术的一个极佳示例。', name: 'Vben Admin Pro', - title: '关于我们', + title: '关于项目', }); const { @@ -29,7 +29,9 @@ const { license, repositoryUrl, version, -} = window.__VBEN_ADMIN_METADATA__ || {}; + // vite inject-metadata 插件注入的全局变量 + // eslint-disable-next-line no-undef +} = __VBEN_ADMIN_METADATA__ || {}; const vbenDescriptionItems: DescriptionItem[] = [ { @@ -105,7 +107,7 @@ const devDependenciesItems = Object.keys(devDependencies).map((key) => ({