diff --git a/package.json b/package.json index eff99c19..1428a303 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,8 @@ ], "dependencies": { "@halo-dev/admin-api": "^1.1.0", - "@halo-dev/components": "workspace:*", "@halo-dev/admin-shared": "workspace:*", + "@halo-dev/components": "workspace:*", "@vueuse/core": "^8.6.0", "filepond": "^4.30.4", "filepond-plugin-image-preview": "^4.6.11", @@ -72,6 +72,7 @@ "vite": "^2.9.12", "vite-compression-plugin": "^0.0.4", "vite-plugin-pwa": "^0.12.0", + "vite-plugin-vue-setup-extend": "^0.4.0", "vitest": "^0.15.1", "vue-tsc": "^0.34.17" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 54484163..d134c54a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,6 +45,7 @@ importers: vite: ^2.9.12 vite-compression-plugin: ^0.0.4 vite-plugin-pwa: ^0.12.0 + vite-plugin-vue-setup-extend: ^0.4.0 vitest: ^0.15.1 vue: ^3.2.37 vue-filepond: ^7.0.3 @@ -98,6 +99,7 @@ importers: vite: 2.9.12_sass@1.52.3 vite-compression-plugin: 0.0.4 vite-plugin-pwa: 0.12.0_vite@2.9.12 + vite-plugin-vue-setup-extend: 0.4.0_vite@2.9.12 vitest: 0.15.1_fiumxgyk2tfafw3c4rsaverrnm vue-tsc: 0.34.17_typescript@4.7.3 @@ -6783,6 +6785,16 @@ packages: - supports-color dev: true + /vite-plugin-vue-setup-extend/0.4.0_vite@2.9.12: + resolution: {integrity: sha512-WMbjPCui75fboFoUTHhdbXzu4Y/bJMv5N9QT9a7do3wNMNHHqrk+Tn2jrSJU0LS5fGl/EG+FEDBYVUeWIkDqXQ==} + peerDependencies: + vite: '>=2.0.0' + dependencies: + '@vue/compiler-sfc': 3.2.37 + magic-string: 0.25.9 + vite: 2.9.12_sass@1.52.3 + dev: true + /vite/2.9.12: resolution: {integrity: sha512-suxC36dQo9Rq1qMB2qiRorNJtJAdxguu5TMvBHOc/F370KvqAe9t48vYp+/TbPKRNrMh/J55tOUmkuIqstZaew==} engines: {node: '>=12.2.0'} diff --git a/src/main.ts b/src/main.ts index 35b236cb..530f9a71 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,16 +2,78 @@ import { createApp } from "vue"; import { createPinia } from "pinia"; import App from "./App.vue"; import router from "./router"; - -const app = createApp(App); - +import type { Plugin } from "@halo-dev/admin-shared"; // setup import "./setup/setupStyles"; import { setupComponents } from "./setup/setupComponents"; +import { registerMenu } from "@/router/menus.config"; + +// modules +import dashboardModule from "./modules/dashboard/module"; +import postModule from "./modules/contents/posts/module"; +import sheetModule from "./modules/contents/sheets/module"; +import commentModule from "./modules/contents/comments/module"; +import attachmentModule from "./modules/contents/attachments/module"; +import themeModule from "./modules/interface/themes/module"; +import menuModule from "./modules/interface/menus/module"; +import pluginModule from "./modules/system/plugins/module"; +import userModule from "./modules/system/users/module"; +import roleModule from "./modules/system/roles/module"; +import settingModule from "./modules/system/settings/module"; + +const app = createApp(App); setupComponents(app); app.use(createPinia()); -app.use(router); -app.mount("#app"); +async function registerModule(pluginModule: Plugin) { + if (pluginModule.components) { + for (const component of pluginModule.components) { + component.name && app.component(component.name, component); + } + } + + if (pluginModule.routes) { + for (const route of pluginModule.routes) { + router.addRoute(route); + } + } + + if (pluginModule.menus) { + for (const group of pluginModule.menus) { + for (const menu of group.items) { + registerMenu(group.name, menu); + } + } + } +} + +function loadCoreModules() { + [ + dashboardModule, + postModule, + sheetModule, + commentModule, + attachmentModule, + themeModule, + menuModule, + pluginModule, + userModule, + roleModule, + settingModule, + ].forEach(registerModule); +} + +function loadPluginModules() { + // TODO: load plugin modules +} + +initApp(); + +async function initApp() { + loadCoreModules(); + loadPluginModules(); + app.use(router); + app.mount("#app"); +} diff --git a/src/modules/contents/attachments/module.ts b/src/modules/contents/attachments/module.ts new file mode 100644 index 00000000..2e9c1bee --- /dev/null +++ b/src/modules/contents/attachments/module.ts @@ -0,0 +1,36 @@ +import type { Plugin } from "@halo-dev/admin-shared"; +import { BasicLayout } from "@/layouts"; +import AttachmentList from "./AttachmentList.vue"; +import { IconFolder } from "@halo-dev/components"; + +const attachmentModule: Plugin = { + name: "attachmentModule", + components: [], + routes: [ + { + path: "/attachments", + component: BasicLayout, + children: [ + { + path: "", + name: "Attachments", + component: AttachmentList, + }, + ], + }, + ], + menus: [ + { + name: "内容", + items: [ + { + name: "附件", + path: "/attachments", + icon: IconFolder, + }, + ], + }, + ], +}; + +export default attachmentModule; diff --git a/src/modules/contents/comments/module.ts b/src/modules/contents/comments/module.ts new file mode 100644 index 00000000..c70d8a29 --- /dev/null +++ b/src/modules/contents/comments/module.ts @@ -0,0 +1,36 @@ +import type { Plugin } from "@halo-dev/admin-shared"; +import { BasicLayout } from "@/layouts"; +import { IconMessage } from "@halo-dev/components"; +import CommentList from "./CommentList.vue"; + +const commentModule: Plugin = { + name: "commentModule", + components: [], + routes: [ + { + path: "/comments", + component: BasicLayout, + children: [ + { + path: "", + name: "Comments", + component: CommentList, + }, + ], + }, + ], + menus: [ + { + name: "内容", + items: [ + { + name: "评论", + path: "/comments", + icon: IconMessage, + }, + ], + }, + ], +}; + +export default commentModule; diff --git a/src/modules/contents/posts/module.ts b/src/modules/contents/posts/module.ts new file mode 100644 index 00000000..7ddf8f48 --- /dev/null +++ b/src/modules/contents/posts/module.ts @@ -0,0 +1,66 @@ +import type { Plugin } from "@halo-dev/admin-shared"; +import { BasicLayout, BlankLayout } from "@/layouts"; +import { IconBookRead } from "@halo-dev/components"; +import PostList from "./PostList.vue"; +import PostEditor from "./PostEditor.vue"; +import CategoryList from "./categories/CategoryList.vue"; +import TagList from "./tags/TagList.vue"; + +const postModule: Plugin = { + name: "postModule", + components: [], + routes: [ + { + path: "/posts", + component: BasicLayout, + children: [ + { + path: "", + name: "Posts", + component: PostList, + }, + { + path: "editor", + name: "PostEditor", + component: PostEditor, + }, + { + path: "categories", + component: BlankLayout, + children: [ + { + path: "", + name: "Categories", + component: CategoryList, + }, + ], + }, + { + path: "tags", + component: BlankLayout, + children: [ + { + path: "", + name: "Tags", + component: TagList, + }, + ], + }, + ], + }, + ], + menus: [ + { + name: "内容", + items: [ + { + name: "文章", + path: "/posts", + icon: IconBookRead, + }, + ], + }, + ], +}; + +export default postModule; diff --git a/src/modules/contents/sheets/module.ts b/src/modules/contents/sheets/module.ts new file mode 100644 index 00000000..36eb1e42 --- /dev/null +++ b/src/modules/contents/sheets/module.ts @@ -0,0 +1,36 @@ +import type { Plugin } from "@halo-dev/admin-shared"; +import { BasicLayout } from "@/layouts"; +import SheetList from "./SheetList.vue"; +import { IconPages } from "@halo-dev/components"; + +const sheetModule: Plugin = { + name: "sheetModule", + components: [], + routes: [ + { + path: "/sheets", + component: BasicLayout, + children: [ + { + path: "", + name: "Sheets", + component: SheetList, + }, + ], + }, + ], + menus: [ + { + name: "内容", + items: [ + { + name: "页面", + path: "/sheets", + icon: IconPages, + }, + ], + }, + ], +}; + +export default sheetModule; diff --git a/src/modules/dashboard/module.ts b/src/modules/dashboard/module.ts new file mode 100644 index 00000000..f48ac183 --- /dev/null +++ b/src/modules/dashboard/module.ts @@ -0,0 +1,55 @@ +import type { Plugin } from "@halo-dev/admin-shared"; +import { BasicLayout } from "@/layouts"; +import Dashboard from "./Dashboard.vue"; +import { IconDashboard } from "@halo-dev/components"; + +import CommentStatsWidget from "./widgets/CommentStatsWidget.vue"; +import JournalPublishWidget from "./widgets/JournalPublishWidget.vue"; +import PostStatsWidget from "./widgets/PostStatsWidget.vue"; +import QuickLinkWidget from "./widgets/QuickLinkWidget.vue"; +import RecentLoginWidget from "./widgets/RecentLoginWidget.vue"; +import RecentPublishedWidget from "./widgets/RecentPublishedWidget.vue"; +import UserStatsWidget from "./widgets/UserStatsWidget.vue"; +import ViewsStatsWidget from "./widgets/ViewsStatsWidget.vue"; + +const dashboardModule: Plugin = { + name: "dashboardModule", + components: [ + CommentStatsWidget, + JournalPublishWidget, + PostStatsWidget, + QuickLinkWidget, + RecentLoginWidget, + RecentPublishedWidget, + UserStatsWidget, + ViewsStatsWidget, + ], + routes: [ + { + path: "/", + component: BasicLayout, + redirect: "/dashboard", + children: [ + { + path: "dashboard", + name: "Dashboard", + component: Dashboard, + }, + ], + }, + ], + menus: [ + { + name: "", + items: [ + { + name: "仪表盘", + path: "/dashboard", + icon: IconDashboard, + }, + ], + }, + ], +}; + +export default dashboardModule; diff --git a/src/modules/dashboard/widgets/CommentStatsWidget.vue b/src/modules/dashboard/widgets/CommentStatsWidget.vue index 8e920b14..634aac41 100644 --- a/src/modules/dashboard/widgets/CommentStatsWidget.vue +++ b/src/modules/dashboard/widgets/CommentStatsWidget.vue @@ -1,4 +1,4 @@ -