refactor: improve project structure for user center feature (#4839)

#### What type of PR is this?

/area console
/kind improvement
/milestone 2.11.x

#### What this PR does / why we need it:

重构前端的目录结构,以便在后续让个人中心的代码支持复用部分代码。

https://github.com/ruibaby/halo/tree/refactor/file-structure/console/docs/project-structure

#### Special notes for your reviewer:

测试方式:

1. 启动最新的 Halo 后端。
2. 在 Console 目录运行 `pnpm dev`,可以观察到同时监听了 3000 和 4000 端口,分别代理原本的 /console 和新加的 /uc
3. 访问 /console 和 /uc。
4. 测试 `pnpm build`,完成之后检查主项目的 `src/main/resources` 目录是否包含 console 和 uc 目录。
5. 使用生产环境运行 Halo,访问 /console 和 /uc

#### Does this PR introduce a user-facing change?

```release-note
None
```
pull/4851/head
Ryan Wang 2023-11-09 14:56:06 +08:00 committed by GitHub
parent 8c56093ee2
commit 52d064381f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
205 changed files with 304 additions and 222 deletions

View File

@ -3,7 +3,7 @@ import { RouterView, useRoute } from "vue-router";
import { computed, watch, reactive, onMounted, inject } from "vue";
import { useTitle } from "@vueuse/core";
import { useFavicon } from "@vueuse/core";
import { useSystemConfigMapStore } from "./stores/system-configmap";
import { useSystemConfigMapStore } from "@console/stores/system-configmap";
import { storeToRefs } from "pinia";
import { useI18n } from "vue-i18n";
import {
@ -11,9 +11,9 @@ import {
type UseOverlayScrollbarsParams,
} from "overlayscrollbars-vue";
import type { FormKitConfig } from "@formkit/core";
import { i18n } from "./locales";
import { AppName } from "./constants/app";
import { useGlobalInfoStore } from "./stores/global-info";
import { i18n } from "@/locales";
import { AppName } from "@/constants/app";
import { useGlobalInfoStore } from "@/stores/global-info";
const { t } = useI18n();

View File

@ -2,7 +2,7 @@
// types
import { computed, watch, type ComputedRef, type Ref } from "vue";
import { ref } from "vue";
import { apiClient } from "../utils/api-client";
import { apiClient } from "@/utils/api-client";
// libs
import cloneDeep from "lodash.clonedeep";

View File

@ -22,7 +22,7 @@ import { onMounted, onUnmounted, reactive, ref } from "vue";
import axios from "axios";
import GlobalSearchModal from "@/components/global-search/GlobalSearchModal.vue";
import LoginModal from "@/components/login/LoginModal.vue";
import { coreMenuGroups } from "@/router/routes.config";
import { coreMenuGroups } from "@console/router/routes.config";
import sortBy from "lodash.sortby";
import { useRoleStore } from "@/stores/role";
import { hasPermission } from "@/utils/permission";

View File

@ -0,0 +1,113 @@
import { createApp } from "vue";
import type { DirectiveBinding } from "vue";
import { createPinia } from "pinia";
import App from "./App.vue";
import router from "./router";
import { apiClient } from "@/utils/api-client";
// setup
import "@/setup/setupStyles";
import { setupComponents } from "@/setup/setupComponents";
import { setupI18n, i18n, getBrowserLanguage } from "@/locales";
// core modules
import { hasPermission } from "@/utils/permission";
import { useRoleStore } from "@/stores/role";
import { useThemeStore } from "@console/stores/theme";
import { useUserStore } from "@/stores/user";
import { useSystemConfigMapStore } from "@console/stores/system-configmap";
import { setupVueQuery } from "@/setup/setupVueQuery";
import { useGlobalInfoStore } from "@/stores/global-info";
import { setupCoreModules, setupPluginModules } from "@/setup/setupModules";
const app = createApp(App);
setupComponents(app);
setupI18n(app);
setupVueQuery(app);
app.use(createPinia());
async function loadUserPermissions() {
const { data: currentPermissions } = await apiClient.user.getPermissions({
name: "-",
});
const roleStore = useRoleStore();
roleStore.$patch({
permissions: currentPermissions,
});
app.directive(
"permission",
(el: HTMLElement, binding: DirectiveBinding<string[]>) => {
const uiPermissions = Array.from<string>(
currentPermissions.uiPermissions
);
const { value } = binding;
const { any, enable } = binding.modifiers;
if (hasPermission(uiPermissions, value, any)) {
return;
}
if (enable) {
//TODO
return;
}
el?.remove?.();
}
);
}
async function loadActivatedTheme() {
const themeStore = useThemeStore();
await themeStore.fetchActivatedTheme();
}
(async function () {
await initApp();
})();
async function initApp() {
// TODO 实验性
const theme = localStorage.getItem("theme");
if (theme) {
document.body.classList.add(theme);
}
try {
setupCoreModules(app);
const userStore = useUserStore();
await userStore.fetchCurrentUser();
// set locale
i18n.global.locale.value =
localStorage.getItem("locale") || getBrowserLanguage();
const globalInfoStore = useGlobalInfoStore();
await globalInfoStore.fetchGlobalInfo();
if (userStore.isAnonymous) {
return;
}
await loadUserPermissions();
try {
await setupPluginModules(app);
} catch (e) {
console.error("Failed to load plugins", e);
}
// load system configMap
const systemConfigMapStore = useSystemConfigMapStore();
await systemConfigMapStore.fetchSystemConfigMap();
if (globalInfoStore.globalInfo?.userInitialized) {
await loadActivatedTheme();
}
} catch (e) {
console.error(e);
} finally {
app.use(router);
app.mount("#app");
}
}

View File

@ -19,7 +19,7 @@ import { useI18n } from "vue-i18n";
import { inject } from "vue";
import type { Ref } from "vue";
import { useQueryClient } from "@tanstack/vue-query";
import { useOperationItemExtensionPoint } from "@/composables/use-operation-extension-points";
import { useOperationItemExtensionPoint } from "@console/composables/use-operation-extension-points";
import { toRefs } from "vue";
import type { OperationItem } from "@halo-dev/console-shared";
import EntityDropdownItems from "@/components/entity/EntityDropdownItems.vue";

View File

@ -4,7 +4,7 @@ import SubmitButton from "@/components/button/SubmitButton.vue";
import type { Policy, PolicyTemplate } from "@halo-dev/api-client";
import cloneDeep from "lodash.clonedeep";
import { computed, ref, toRaw, watch, watchEffect } from "vue";
import { useSettingForm } from "@/composables/use-setting-form";
import { useSettingForm } from "@console/composables/use-setting-form";
import { apiClient } from "@/utils/api-client";
import {
reset,

View File

@ -1,5 +1,5 @@
import { definePlugin } from "@halo-dev/console-shared";
import BasicLayout from "@/layouts/BasicLayout.vue";
import BasicLayout from "@console/layouts/BasicLayout.vue";
import AttachmentList from "./AttachmentList.vue";
import AttachmentSelectorModal from "./components/AttachmentSelectorModal.vue";
import { IconFolder } from "@halo-dev/components";

View File

@ -1,5 +1,5 @@
import { definePlugin } from "@halo-dev/console-shared";
import BasicLayout from "@/layouts/BasicLayout.vue";
import BasicLayout from "@console/layouts/BasicLayout.vue";
import { IconMessage } from "@halo-dev/components";
import CommentList from "./CommentList.vue";
import CommentStatsWidget from "./widgets/CommentStatsWidget.vue";

View File

@ -27,8 +27,8 @@ import { useRouteQuery } from "@vueuse/router";
import cloneDeep from "lodash.clonedeep";
import { useRouter } from "vue-router";
import { randomUUID } from "@/utils/id";
import { useContentCache } from "@/composables/use-content-cache";
import { useEditorExtensionPoints } from "@/composables/use-editor-extension-points";
import { useContentCache } from "@console/composables/use-content-cache";
import { useEditorExtensionPoints } from "@console/composables/use-editor-extension-points";
import type { EditorProvider } from "@halo-dev/console-shared";
import { useLocalStorage } from "@vueuse/core";
import EditorProviderSelector from "@/components/dropdown-selector/EditorProviderSelector.vue";
@ -36,9 +36,9 @@ import { useI18n } from "vue-i18n";
import UrlPreviewModal from "@/components/preview/UrlPreviewModal.vue";
import { contentAnnotations } from "@/constants/annotations";
import { usePageUpdateMutate } from "./composables/use-page-update-mutate";
import { useAutoSaveContent } from "@/composables/use-auto-save-content";
import { useContentSnapshot } from "@/composables/use-content-snapshot";
import { useSaveKeybinding } from "@/composables/use-save-keybinding";
import { useAutoSaveContent } from "@console/composables/use-auto-save-content";
import { useContentSnapshot } from "@console/composables/use-content-snapshot";
import { useSaveKeybinding } from "@console/composables/use-save-keybinding";
const router = useRouter();
const { t } = useI18n();

View File

@ -10,13 +10,13 @@ import { computed, nextTick, ref, watchEffect } from "vue";
import type { SinglePage } from "@halo-dev/api-client";
import cloneDeep from "lodash.clonedeep";
import { apiClient } from "@/utils/api-client";
import { useThemeCustomTemplates } from "@/modules/interface/themes/composables/use-theme";
import { useThemeCustomTemplates } from "@console/modules/interface/themes/composables/use-theme";
import { singlePageLabels } from "@/constants/labels";
import { randomUUID } from "@/utils/id";
import { toDatetimeLocal, toISOString } from "@/utils/date";
import { submitForm } from "@formkit/core";
import AnnotationsForm from "@/components/form/AnnotationsForm.vue";
import useSlugify from "@/composables/use-slugify";
import useSlugify from "@console/composables/use-slugify";
import { useI18n } from "vue-i18n";
import { usePageUpdateMutate } from "../composables/use-page-update-mutate";
import { FormType } from "@/types/slug";

View File

@ -1,5 +1,5 @@
import { definePlugin } from "@halo-dev/console-shared";
import BasicLayout from "@/layouts/BasicLayout.vue";
import BasicLayout from "@console/layouts/BasicLayout.vue";
import SinglePageList from "./SinglePageList.vue";
import DeletedSinglePageList from "./DeletedSinglePageList.vue";
import SinglePageEditor from "./SinglePageEditor.vue";

View File

@ -27,8 +27,8 @@ import { apiClient } from "@/utils/api-client";
import { useRouteQuery } from "@vueuse/router";
import { useRouter } from "vue-router";
import { randomUUID } from "@/utils/id";
import { useContentCache } from "@/composables/use-content-cache";
import { useEditorExtensionPoints } from "@/composables/use-editor-extension-points";
import { useContentCache } from "@console/composables/use-content-cache";
import { useEditorExtensionPoints } from "@console/composables/use-editor-extension-points";
import type { EditorProvider } from "@halo-dev/console-shared";
import { useLocalStorage } from "@vueuse/core";
import EditorProviderSelector from "@/components/dropdown-selector/EditorProviderSelector.vue";
@ -36,9 +36,9 @@ import { useI18n } from "vue-i18n";
import UrlPreviewModal from "@/components/preview/UrlPreviewModal.vue";
import { usePostUpdateMutate } from "./composables/use-post-update-mutate";
import { contentAnnotations } from "@/constants/annotations";
import { useAutoSaveContent } from "@/composables/use-auto-save-content";
import { useContentSnapshot } from "@/composables/use-content-snapshot";
import { useSaveKeybinding } from "@/composables/use-save-keybinding";
import { useAutoSaveContent } from "@console/composables/use-auto-save-content";
import { useContentSnapshot } from "@console/composables/use-content-snapshot";
import { useSaveKeybinding } from "@console/composables/use-save-keybinding";
const router = useRouter();
const { t } = useI18n();

View File

@ -20,9 +20,9 @@ import type { Category } from "@halo-dev/api-client";
import cloneDeep from "lodash.clonedeep";
import { reset } from "@formkit/core";
import { setFocus } from "@/formkit/utils/focus";
import { useThemeCustomTemplates } from "@/modules/interface/themes/composables/use-theme";
import { useThemeCustomTemplates } from "@console/modules/interface/themes/composables/use-theme";
import AnnotationsForm from "@/components/form/AnnotationsForm.vue";
import useSlugify from "@/composables/use-slugify";
import useSlugify from "@console/composables/use-slugify";
import { useI18n } from "vue-i18n";
import { FormType } from "@/types/slug";

View File

@ -2,8 +2,8 @@ import { apiClient } from "@/utils/api-client";
import type { Category } from "@halo-dev/api-client";
import type { Ref } from "vue";
import { ref } from "vue";
import type { CategoryTree } from "@/modules/contents/posts/categories/utils";
import { buildCategoriesTree } from "@/modules/contents/posts/categories/utils";
import type { CategoryTree } from "@console/modules/contents/posts/categories/utils";
import { buildCategoriesTree } from "@console/modules/contents/posts/categories/utils";
import { Dialog, Toast } from "@halo-dev/components";
import { useQuery } from "@tanstack/vue-query";
import { useI18n } from "vue-i18n";

View File

@ -16,8 +16,8 @@ import { useQueryClient } from "@tanstack/vue-query";
import type { Ref } from "vue";
import { computed, toRefs, markRaw, ref, inject } from "vue";
import { useRouter } from "vue-router";
import { useEntityFieldItemExtensionPoint } from "@/composables/use-entity-extension-points";
import { useOperationItemExtensionPoint } from "@/composables/use-operation-extension-points";
import { useEntityFieldItemExtensionPoint } from "@console/composables/use-entity-extension-points";
import { useOperationItemExtensionPoint } from "@console/composables/use-operation-extension-points";
import EntityDropdownItems from "@/components/entity/EntityDropdownItems.vue";
import type { EntityFieldItem, OperationItem } from "@halo-dev/console-shared";
import TitleField from "./entity-fields/TitleField.vue";

View File

@ -10,13 +10,13 @@ import { computed, nextTick, ref, watchEffect } from "vue";
import type { Post } from "@halo-dev/api-client";
import cloneDeep from "lodash.clonedeep";
import { apiClient } from "@/utils/api-client";
import { useThemeCustomTemplates } from "@/modules/interface/themes/composables/use-theme";
import { useThemeCustomTemplates } from "@console/modules/interface/themes/composables/use-theme";
import { postLabels } from "@/constants/labels";
import { randomUUID } from "@/utils/id";
import { toDatetimeLocal, toISOString } from "@/utils/date";
import AnnotationsForm from "@/components/form/AnnotationsForm.vue";
import { submitForm } from "@formkit/core";
import useSlugify from "@/composables/use-slugify";
import useSlugify from "@console/composables/use-slugify";
import { useI18n } from "vue-i18n";
import { usePostUpdateMutate } from "../composables/use-post-update-mutate";
import { FormType } from "@/types/slug";

View File

@ -1,7 +1,7 @@
<script lang="ts" setup>
import { VEntityField } from "@halo-dev/components";
import type { ListedPost } from "@halo-dev/api-client";
import ContributorList from "@/modules/contents/_components/ContributorList.vue";
import ContributorList from "@console/modules/contents/_components/ContributorList.vue";
withDefaults(
defineProps<{

View File

@ -1,6 +1,6 @@
import { definePlugin } from "@halo-dev/console-shared";
import BasicLayout from "@/layouts/BasicLayout.vue";
import BlankLayout from "@/layouts/BlankLayout.vue";
import BasicLayout from "@console/layouts/BasicLayout.vue";
import BlankLayout from "@console/layouts/BlankLayout.vue";
import { IconBookRead } from "@halo-dev/components";
import PostList from "./PostList.vue";
import DeletedPostList from "./DeletedPostList.vue";

View File

@ -23,7 +23,7 @@ import cloneDeep from "lodash.clonedeep";
import { reset } from "@formkit/core";
import { setFocus } from "@/formkit/utils/focus";
import AnnotationsForm from "@/components/form/AnnotationsForm.vue";
import useSlugify from "@/composables/use-slugify";
import useSlugify from "@console/composables/use-slugify";
import { useI18n } from "vue-i18n";
import { FormType } from "@/types/slug";

View File

@ -1,5 +1,5 @@
import { definePlugin } from "@halo-dev/console-shared";
import BasicLayout from "@/layouts/BasicLayout.vue";
import BasicLayout from "@console/layouts/BasicLayout.vue";
import Dashboard from "./Dashboard.vue";
import { IconDashboard } from "@halo-dev/components";

View File

@ -17,7 +17,7 @@ import {
} from "@halo-dev/components";
import { markRaw, ref, type Component } from "vue";
import { useRouter } from "vue-router";
import ThemePreviewModal from "@/modules/interface/themes/components/preview/ThemePreviewModal.vue";
import ThemePreviewModal from "@console/modules/interface/themes/components/preview/ThemePreviewModal.vue";
import { apiClient } from "@/utils/api-client";
import { useI18n } from "vue-i18n";
import { OverlayScrollbarsComponent } from "overlayscrollbars-vue";

View File

@ -9,7 +9,7 @@ import {
} from "@halo-dev/components";
import Draggable from "vuedraggable";
import { ref } from "vue";
import type { MenuTreeItem } from "@/modules/interface/menus/utils";
import type { MenuTreeItem } from "@console/modules/interface/menus/utils";
import { usePermission } from "@/utils/permission";
import { useI18n } from "vue-i18n";

View File

@ -1,5 +1,5 @@
import { definePlugin } from "@halo-dev/console-shared";
import BasicLayout from "@/layouts/BasicLayout.vue";
import BasicLayout from "@console/layouts/BasicLayout.vue";
import Menus from "./Menus.vue";
import { IconListSettings } from "@halo-dev/components";
import { markRaw } from "vue";

View File

@ -11,7 +11,7 @@ import type { ConfigMap, Setting, Theme } from "@halo-dev/api-client";
// hooks
import { apiClient } from "@/utils/api-client";
import { useSettingFormConvert } from "@/composables/use-setting-form";
import { useSettingFormConvert } from "@console/composables/use-setting-form";
import { useI18n } from "vue-i18n";
import { useQuery, useQueryClient } from "@tanstack/vue-query";

View File

@ -16,7 +16,7 @@ import { useThemeLifeCycle } from "../composables/use-theme";
import { usePermission } from "@/utils/permission";
import { useI18n } from "vue-i18n";
import { useQueryClient } from "@tanstack/vue-query";
import { useOperationItemExtensionPoint } from "@/composables/use-operation-extension-points";
import { useOperationItemExtensionPoint } from "@console/composables/use-operation-extension-points";
import { markRaw } from "vue";
import UninstallOperationItem from "./operation/UninstallOperationItem.vue";
import { computed } from "vue";

View File

@ -12,7 +12,7 @@ import { ref, inject, type Ref } from "vue";
import type { Theme } from "@halo-dev/api-client";
import { apiClient } from "@/utils/api-client";
import { useQuery } from "@tanstack/vue-query";
import { useThemeStore } from "@/stores/theme";
import { useThemeStore } from "@console/stores/theme";
const themeStore = useThemeStore();

View File

@ -10,7 +10,7 @@ import { useI18n } from "vue-i18n";
import { inject } from "vue";
import type { Ref } from "vue";
import { ref } from "vue";
import { useThemeStore } from "@/stores/theme";
import { useThemeStore } from "@console/stores/theme";
import { apiClient } from "@/utils/api-client";
import UppyUpload from "@/components/upload/UppyUpload.vue";

View File

@ -7,7 +7,7 @@ import { inject } from "vue";
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import type { ThemeInstallationErrorResponse } from "../../types";
import { useThemeStore } from "@/stores/theme";
import { useThemeStore } from "@console/stores/theme";
import { THEME_ALREADY_EXISTS_TYPE } from "../../constants";
import { useRouteQuery } from "@vueuse/router";
import { onMounted } from "vue";

View File

@ -1,7 +1,7 @@
<script lang="ts" setup>
import ThemePreviewListItem from "./ThemePreviewListItem.vue";
import { useSettingFormConvert } from "@/composables/use-setting-form";
import { useThemeStore } from "@/stores/theme";
import { useSettingFormConvert } from "@console/composables/use-setting-form";
import { useThemeStore } from "@console/stores/theme";
import { apiClient } from "@/utils/api-client";
import type {
ConfigMap,

View File

@ -3,7 +3,7 @@ import { computed, ref } from "vue";
import type { Theme } from "@halo-dev/api-client";
import { apiClient } from "@/utils/api-client";
import { Dialog, Toast } from "@halo-dev/components";
import { useThemeStore } from "@/stores/theme";
import { useThemeStore } from "@console/stores/theme";
import { storeToRefs } from "pinia";
import { useI18n } from "vue-i18n";

View File

@ -10,7 +10,7 @@ import cloneDeep from "lodash.clonedeep";
// hooks
import { useThemeLifeCycle } from "../composables/use-theme";
// types
import BasicLayout from "@/layouts/BasicLayout.vue";
import BasicLayout from "@console/layouts/BasicLayout.vue";
// components
import {
@ -31,7 +31,7 @@ import ThemeListModal from "../components/ThemeListModal.vue";
import ThemePreviewModal from "../components/preview/ThemePreviewModal.vue";
import type { Setting, SettingForm, Theme } from "@halo-dev/api-client";
import { usePermission } from "@/utils/permission";
import { useThemeStore } from "@/stores/theme";
import { useThemeStore } from "@console/stores/theme";
import { storeToRefs } from "pinia";
import { apiClient } from "@/utils/api-client";
import { useI18n } from "vue-i18n";

Some files were not shown because too many files have changed in this diff Show More