diff --git a/package.json b/package.json index d0ad1a5c..74793627 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "@iconify-json/vscode-icons": "^1.1.16", "@rushstack/eslint-patch": "^1.2.0", "@tailwindcss/aspect-ratio": "^0.4.2", + "@tailwindcss/container-queries": "^0.1.0", "@types/jsdom": "^20.0.1", "@types/lodash.clonedeep": "4.5.7", "@types/lodash.debounce": "^4.0.7", @@ -118,7 +119,6 @@ "vite-plugin-html": "^3.2.0", "vite-plugin-pwa": "^0.13.3", "vite-plugin-static-copy": "^0.11.1", - "vite-plugin-vue-setup-extend": "^0.4.0", "vitest": "^0.25.3", "vue-tsc": "^1.0.9" } diff --git a/packages/shared/src/types/plugin.ts b/packages/shared/src/types/plugin.ts index 30a9e621..fc317ea4 100644 --- a/packages/shared/src/types/plugin.ts +++ b/packages/shared/src/types/plugin.ts @@ -20,7 +20,7 @@ export interface Plugin { /** * These components will be registered when plugin is activated. */ - components?: Component[]; + components?: Record; /** * Activate hook will be called when plugin is activated. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee5bdc73..18611eb3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,7 @@ importers: '@iconify-json/vscode-icons': ^1.1.16 '@rushstack/eslint-patch': ^1.2.0 '@tailwindcss/aspect-ratio': ^0.4.2 + '@tailwindcss/container-queries': ^0.1.0 '@tiptap/extension-character-count': ^2.0.0-beta.202 '@types/jsdom': ^20.0.1 '@types/lodash.clonedeep': 4.5.7 @@ -90,7 +91,6 @@ importers: vite-plugin-html: ^3.2.0 vite-plugin-pwa: ^0.13.3 vite-plugin-static-copy: ^0.11.1 - vite-plugin-vue-setup-extend: ^0.4.0 vitest: ^0.25.3 vue: ^3.2.45 vue-grid-layout: 3.0.0-beta1 @@ -150,6 +150,7 @@ importers: '@iconify-json/vscode-icons': 1.1.16 '@rushstack/eslint-patch': 1.2.0 '@tailwindcss/aspect-ratio': 0.4.2_tailwindcss@3.2.4 + '@tailwindcss/container-queries': 0.1.0_tailwindcss@3.2.4 '@types/jsdom': 20.0.1 '@types/lodash.clonedeep': 4.5.7 '@types/lodash.debounce': 4.0.7 @@ -180,7 +181,7 @@ importers: randomstring: 1.2.3 sass: 1.56.1 start-server-and-test: 1.14.0 - tailwindcss: 3.2.4 + tailwindcss: 3.2.4_postcss@8.4.19 tailwindcss-safe-area: 0.2.2 tailwindcss-themer: 2.0.2_tailwindcss@3.2.4 typescript: 4.7.4 @@ -191,7 +192,6 @@ importers: vite-plugin-html: 3.2.0_vite@3.2.4 vite-plugin-pwa: 0.13.3_vite@3.2.4 vite-plugin-static-copy: 0.11.1_vite@3.2.4 - vite-plugin-vue-setup-extend: 0.4.0_vite@3.2.4 vitest: 0.25.3_wqgykbafw5ps5nhnqfbxcvabhu vue-tsc: 1.0.9_typescript@4.7.4 @@ -1931,7 +1931,7 @@ packages: optional: true dependencies: '@formkit/core': 1.0.0-beta.12-e579559 - tailwindcss: 3.2.4 + tailwindcss: 3.2.4_postcss@8.4.19 dev: false /@formkit/utils/1.0.0-beta.12-e579559: @@ -2675,7 +2675,15 @@ packages: peerDependencies: tailwindcss: '>=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1' dependencies: - tailwindcss: 3.2.4 + tailwindcss: 3.2.4_postcss@8.4.19 + dev: true + + /@tailwindcss/container-queries/0.1.0_tailwindcss@3.2.4: + resolution: {integrity: sha512-t1GeJ9P8ual160BvKy6Y1sG7bjChArMaK6iRXm3ZYjZGN2FTzmqb5ztsTDb9AsTSJD4NMHtsnaI2ielrXEk+hw==} + peerDependencies: + tailwindcss: '>=3.2.0' + dependencies: + tailwindcss: 3.2.4_postcss@8.4.19 dev: true /@tiptap/core/2.0.0-beta.202: @@ -8532,13 +8540,15 @@ packages: just-unique: 4.1.1 lodash.merge: 4.6.2 lodash.mergewith: 4.6.2 - tailwindcss: 3.2.4 + tailwindcss: 3.2.4_postcss@8.4.19 dev: true - /tailwindcss/3.2.4: + /tailwindcss/3.2.4_postcss@8.4.19: resolution: {integrity: sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ==} engines: {node: '>=12.13.0'} hasBin: true + peerDependencies: + postcss: ^8.0.9 dependencies: arg: 5.0.2 chokidar: 3.5.3 @@ -9173,16 +9183,6 @@ packages: vite: 3.2.4_ajklay5k626t46b6fyghkbup3i dev: true - /vite-plugin-vue-setup-extend/0.4.0_vite@3.2.4: - resolution: {integrity: sha512-WMbjPCui75fboFoUTHhdbXzu4Y/bJMv5N9QT9a7do3wNMNHHqrk+Tn2jrSJU0LS5fGl/EG+FEDBYVUeWIkDqXQ==} - peerDependencies: - vite: '>=2.0.0' - dependencies: - '@vue/compiler-sfc': 3.2.45 - magic-string: 0.25.9 - vite: 3.2.4_ajklay5k626t46b6fyghkbup3i - dev: true - /vite/3.2.4: resolution: {integrity: sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==} engines: {node: ^14.18.0 || >=16.0.0} diff --git a/src/main.ts b/src/main.ts index f13149c7..3c721df9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -28,14 +28,12 @@ app.use(createPinia()); function registerModule(pluginModule: Plugin, core: boolean) { if (pluginModule.components) { - if (!Array.isArray(pluginModule.components)) { - console.error(`${pluginModule.name}: Plugin components must be an array`); - return; - } - - for (const component of pluginModule.components) { - component.name && app.component(component.name, component); - } + Object.keys(pluginModule.components).forEach((key) => { + const component = pluginModule.components?.[key]; + if (component) { + app.component(key, component); + } + }); } if (pluginModule.routes) { diff --git a/src/modules/contents/attachments/module.ts b/src/modules/contents/attachments/module.ts index ef981e7a..ea5522f9 100644 --- a/src/modules/contents/attachments/module.ts +++ b/src/modules/contents/attachments/module.ts @@ -7,7 +7,9 @@ import { markRaw } from "vue"; export default definePlugin({ name: "attachmentModule", - components: [AttachmentSelectorModal], + components: { + AttachmentSelectorModal, + }, routes: [ { path: "/attachments", diff --git a/src/modules/contents/comments/module.ts b/src/modules/contents/comments/module.ts index d05eca84..2d6c5237 100644 --- a/src/modules/contents/comments/module.ts +++ b/src/modules/contents/comments/module.ts @@ -2,11 +2,14 @@ import { definePlugin } from "@halo-dev/console-shared"; import BasicLayout from "@/layouts/BasicLayout.vue"; import { IconMessage } from "@halo-dev/components"; import CommentList from "./CommentList.vue"; +import CommentStatsWidget from "./widgets/CommentStatsWidget.vue"; import { markRaw } from "vue"; export default definePlugin({ name: "commentModule", - components: [], + components: { + CommentStatsWidget, + }, routes: [ { path: "/comments", diff --git a/src/modules/contents/comments/widgets/CommentStatsWidget.vue b/src/modules/contents/comments/widgets/CommentStatsWidget.vue new file mode 100644 index 00000000..443d26d8 --- /dev/null +++ b/src/modules/contents/comments/widgets/CommentStatsWidget.vue @@ -0,0 +1,27 @@ + + diff --git a/src/modules/contents/pages/module.ts b/src/modules/contents/pages/module.ts index 6ea57704..a235c33e 100644 --- a/src/modules/contents/pages/module.ts +++ b/src/modules/contents/pages/module.ts @@ -6,12 +6,15 @@ import FunctionalPageList from "./FunctionalPageList.vue"; import SinglePageList from "./SinglePageList.vue"; import DeletedSinglePageList from "./DeletedSinglePageList.vue"; import SinglePageEditor from "./SinglePageEditor.vue"; +import SinglePageStatsWidget from "./widgets/SinglePageStatsWidget.vue"; import { IconPages } from "@halo-dev/components"; import { markRaw } from "vue"; export default definePlugin({ name: "pageModule", - components: [], + components: { + SinglePageStatsWidget, + }, routes: [ { path: "/pages", diff --git a/src/modules/contents/pages/widgets/SinglePageStatsWidget.vue b/src/modules/contents/pages/widgets/SinglePageStatsWidget.vue new file mode 100644 index 00000000..ed2d1366 --- /dev/null +++ b/src/modules/contents/pages/widgets/SinglePageStatsWidget.vue @@ -0,0 +1,42 @@ + + diff --git a/src/modules/contents/posts/module.ts b/src/modules/contents/posts/module.ts index 6254703b..6415068d 100644 --- a/src/modules/contents/posts/module.ts +++ b/src/modules/contents/posts/module.ts @@ -7,11 +7,16 @@ import DeletedPostList from "./DeletedPostList.vue"; import PostEditor from "./PostEditor.vue"; import CategoryList from "./categories/CategoryList.vue"; import TagList from "./tags/TagList.vue"; +import PostStatsWidget from "./widgets/PostStatsWidget.vue"; +import RecentPublishedWidget from "./widgets/RecentPublishedWidget.vue"; import { markRaw } from "vue"; export default definePlugin({ name: "postModule", - components: [], + components: { + PostStatsWidget, + RecentPublishedWidget, + }, routes: [ { path: "/posts", diff --git a/src/modules/contents/posts/widgets/PostStatsWidget.vue b/src/modules/contents/posts/widgets/PostStatsWidget.vue new file mode 100644 index 00000000..80a0eeca --- /dev/null +++ b/src/modules/contents/posts/widgets/PostStatsWidget.vue @@ -0,0 +1,27 @@ + + diff --git a/src/modules/contents/posts/widgets/RecentPublishedWidget.vue b/src/modules/contents/posts/widgets/RecentPublishedWidget.vue new file mode 100644 index 00000000..f9beeb8d --- /dev/null +++ b/src/modules/contents/posts/widgets/RecentPublishedWidget.vue @@ -0,0 +1,90 @@ + + diff --git a/src/modules/dashboard/Dashboard.vue b/src/modules/dashboard/Dashboard.vue index af5bf932..0ae49147 100644 --- a/src/modules/dashboard/Dashboard.vue +++ b/src/modules/dashboard/Dashboard.vue @@ -114,9 +114,11 @@ import { VSpace, VTabbar, } from "@halo-dev/components"; -import { ref } from "vue"; +import { onMounted, provide, ref, type Ref } from "vue"; import { useStorage } from "@vueuse/core"; import cloneDeep from "lodash.clonedeep"; +import { apiClient } from "@/utils/api-client"; +import type { DashboardStats } from "@halo-dev/api-client/index"; const widgetsGroup = [ { @@ -127,6 +129,13 @@ const widgetsGroup = [ { x: 0, y: 0, w: 6, h: 10, i: 1, widget: "RecentPublishedWidget" }, ], }, + { + id: "page", + label: "页面", + widgets: [ + { x: 0, y: 0, w: 3, h: 3, i: 0, widget: "SinglePageStatsWidget" }, + ], + }, { id: "comment", label: "评论", @@ -137,7 +146,7 @@ const widgetsGroup = [ label: "用户", widgets: [ { x: 0, y: 0, w: 3, h: 3, i: 0, widget: "UserStatsWidget" }, - { x: 0, y: 0, w: 6, h: 10, i: 1, widget: "RecentLoginWidget" }, + { x: 0, y: 0, w: 3, h: 3, i: 1, widget: "UserProfileWidget" }, ], }, { @@ -159,14 +168,13 @@ const layout = useStorage("widgets", [ { x: 3, y: 0, w: 3, h: 3, i: 1, widget: "UserStatsWidget" }, { x: 6, y: 0, w: 3, h: 3, i: 2, widget: "CommentStatsWidget" }, { x: 9, y: 0, w: 3, h: 3, i: 3, widget: "ViewsStatsWidget" }, - { x: 8, y: 3, w: 4, h: 10, i: 4, widget: "RecentLoginWidget" }, - { x: 0, y: 3, w: 4, h: 10, i: 5, widget: "QuickLinkWidget" }, + { x: 0, y: 3, w: 4, h: 10, i: 4, widget: "QuickLinkWidget" }, { x: 4, y: 3, w: 4, h: 10, - i: 6, + i: 5, widget: "RecentPublishedWidget", }, ]); @@ -194,12 +202,32 @@ function handleRemove(item: any) { }; }); } + +// Dashboard basic stats + +const dashboardStats = ref({ + posts: 0, + comments: 0, + approvedComments: 0, + users: 0, + visits: 0, +}); + +provide>("dashboardStats", dashboardStats); + +const handleFetchStats = async () => { + const { data } = await apiClient.stats.getStats(); + dashboardStats.value = data; +}; + +onMounted(handleFetchStats);