From ca9430f82ce5773f877c07de21102b2ff6b844c8 Mon Sep 17 00:00:00 2001
From: zhouhongshuo <409581486@qq.com>
Date: Sun, 25 Aug 2024 23:46:05 +0800
Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.changeset/README.md | 8 +
.changeset/config.json | 11 +
.changeset/pre.json | 9 +
.editorconfig | 12 +
.env.development | 0
.env.production | 0
.eslintrc.cjs | 31 +
.gitignore | 32 +
.gitpod.yml | 15 +
.husky/pre-commit | 4 +
.npmignore | 4 +
.npmrc | 2 +
.prettierignore | 2 +
.vscode/extensions.json | 8 +
Makefile | 28 +
OWNERS | 15 +
README.md | 49 +
build.gradle | 74 +
console-src/App.vue | 7 +
.../composables/use-content-snapshot.ts | 33 +
.../composables/use-dashboard-stats.ts | 13 +
.../use-entity-extension-points.ts | 47 +
console-src/composables/use-global-info.ts | 20 +
.../use-operation-extension-points.ts | 36 +
.../composables/use-save-keybinding.ts | 21 +
console-src/composables/use-setting-form.ts | 92 +
console-src/composables/use-slugify.ts | 69 +
console-src/layouts/BasicLayout.vue | 352 +
console-src/layouts/BlankLayout.vue | 7 +
console-src/main.ts | 113 +
.../contents/attachments/AttachmentList.vue | 627 +
.../components/AttachmentDetailModal.vue | 192 +
.../components/AttachmentError.vue | 7 +
.../components/AttachmentGroupBadge.vue | 216 +
.../AttachmentGroupEditingModal.vue | 117 +
.../components/AttachmentGroupList.vue | 114 +
.../components/AttachmentListItem.vue | 223 +
.../components/AttachmentLoading.vue | 7 +
.../components/AttachmentPermalinkList.vue | 72 +
.../components/AttachmentPoliciesModal.vue | 215 +
.../components/AttachmentPolicyBadge.vue | 58 +
.../AttachmentPolicyEditingModal.vue | 238 +
.../components/AttachmentSelectorModal.vue | 171 +
.../components/AttachmentUploadModal.vue | 158 +
.../CoreSelectorProvider.vue | 250 +
.../composables/use-attachment-group.ts | 37 +
.../composables/use-attachment-policy.ts | 55 +
.../attachments/composables/use-attachment.ts | 307 +
.../modules/contents/attachments/module.ts | 37 +
.../modules/contents/comments/CommentList.vue | 413 +
.../comments/components/CommentListItem.vue | 508 +
.../components/ReplyCreationModal.vue | 158 +
.../comments/components/ReplyListItem.vue | 250 +
.../modules/contents/comments/module.ts | 38 +
.../comments/widgets/CommentStatsWidget.vue | 28 +
.../contents/pages/DeletedSinglePageList.vue | 402 +
.../contents/pages/SinglePageEditor.vue | 546 +
.../modules/contents/pages/SinglePageList.vue | 479 +
.../contents/pages/SinglePageSnapshots.vue | 182 +
.../pages/components/SinglePageListItem.vue | 258 +
.../components/SinglePageSettingModal.vue | 490 +
.../pages/components/SnapshotContent.vue | 114 +
.../pages/components/SnapshotListItem.vue | 142 +
.../composables/use-page-update-mutate.ts | 40 +
console-src/modules/contents/pages/module.ts | 72 +
.../pages/widgets/SinglePageStatsWidget.vue | 43 +
.../contents/posts/DeletedPostList.vue | 423 +
.../modules/contents/posts/PostEditor.vue | 582 +
.../modules/contents/posts/PostList.vue | 607 +
.../modules/contents/posts/PostSnapshots.vue | 181 +
.../posts/categories/CategoryList.vue | 143 +
.../components/CategoryEditingModal.vue | 381 +
.../components/CategoryListItem.vue | 206 +
.../__tests__/CategoryEditingModal.spec.ts | 28 +
.../composables/use-post-category.ts | 52 +
.../contents/posts/categories/utils/index.ts | 146 +
.../components/PostBatchSettingModal.vue | 338 +
.../posts/components/PostListItem.vue | 242 +
.../posts/components/PostSettingModal.vue | 520 +
.../posts/components/SnapshotContent.vue | 114 +
.../posts/components/SnapshotListItem.vue | 138 +
.../__tests__/PostSettingModal.spec.ts | 37 +
.../entity-fields/ContributorsField.vue | 23 +
.../entity-fields/PublishStatusField.vue | 40 +
.../entity-fields/PublishTimeField.vue | 33 +
.../components/entity-fields/TitleField.vue | 108 +
.../components/entity-fields/VisibleField.vue | 59 +
.../composables/use-post-update-mutate.ts | 40 +
console-src/modules/contents/posts/module.ts | 110 +
.../modules/contents/posts/tags/TagList.vue | 319 +
.../posts/tags/components/PostTag.vue | 57 +
.../posts/tags/components/TagEditingModal.vue | 290 +
.../posts/tags/components/TagListItem.vue | 98 +
.../posts/tags/composables/use-post-tag.ts | 138 +
.../posts/widgets/PostStatsWidget.vue | 28 +
.../posts/widgets/RecentPublishedWidget.vue | 105 +
console-src/modules/dashboard/Dashboard.vue | 288 +
console-src/modules/dashboard/module.ts | 41 +
.../dashboard/widgets/QuickLinkWidget.vue | 210 +
.../dashboard/widgets/ViewsStatsWidget.vue | 28 +
console-src/modules/index.ts | 10 +
console-src/modules/interface/menus/Menus.vue | 290 +
.../menus/components/MenuEditingModal.vue | 117 +
.../menus/components/MenuItemEditingModal.vue | 392 +
.../menus/components/MenuItemListItem.vue | 144 +
.../interface/menus/components/MenuList.vue | 266 +
console-src/modules/interface/menus/module.ts | 34 +
.../__snapshots__/index.spec.ts.snap | 342 +
.../menus/utils/__tests__/index.spec.ts | 226 +
.../modules/interface/menus/utils/index.ts | 265 +
.../modules/interface/themes/ThemeDetail.vue | 253 +
.../modules/interface/themes/ThemeSetting.vue | 105 +
.../themes/components/ThemeListItem.vue | 291 +
.../themes/components/ThemeListModal.vue | 161 +
.../components/list-tabs/InstalledThemes.vue | 114 +
.../components/list-tabs/LocalUpload.vue | 86 +
.../list-tabs/NotInstalledThemes.vue | 48 +
.../components/list-tabs/RemoteDownload.vue | 121 +
.../operation/MoreOperationItem.vue | 7 +
.../operation/UninstallOperationItem.vue | 86 +
.../preview/ThemePreviewListItem.vue | 91 +
.../components/preview/ThemePreviewModal.vue | 404 +
.../interface/themes/composables/use-theme.ts | 304 +
.../interface/themes/constants/index.ts | 2 +
.../interface/themes/layouts/ThemeLayout.vue | 289 +
.../modules/interface/themes/module.ts | 44 +
.../modules/interface/themes/types/index.ts | 10 +
.../auth-providers/AuthProviderDetail.vue | 250 +
.../system/auth-providers/AuthProviders.vue | 132 +
.../components/AuthProviderListItem.vue | 120 +
.../modules/system/auth-providers/module.ts | 32 +
console-src/modules/system/backup/Backups.vue | 95 +
.../backup/components/BackupListItem.vue | 199 +
.../system/backup/composables/use-backup.ts | 74 +
console-src/modules/system/backup/module.ts | 34 +
.../modules/system/backup/tabs/List.vue | 30 +
.../modules/system/backup/tabs/Restore.vue | 228 +
.../modules/system/overview/Overview.vue | 403 +
console-src/modules/system/overview/module.ts | 34 +
.../modules/system/plugins/PluginDetail.vue | 52 +
.../plugins/PluginExtensionPointSettings.vue | 138 +
.../modules/system/plugins/PluginList.vue | 348 +
.../components/PluginConditionsModal.vue | 93 +
.../plugins/components/PluginDetailModal.vue | 66 +
.../components/PluginInstallationModal.vue | 146 +
.../plugins/components/PluginListItem.vue | 288 +
.../components/entity-fields/AuthorField.vue | 24 +
.../components/entity-fields/LogoField.vue | 22 +
.../components/entity-fields/ReloadField.vue | 47 +
.../components/entity-fields/SwitchField.vue | 30 +
.../ExtensionDefinitionListItem.vue | 82 +
.../ExtensionDefinitionMultiInstanceView.vue | 49 +
.../ExtensionDefinitionSingletonView.vue | 153 +
.../installation-tabs/LocalUpload.vue | 122 +
.../installation-tabs/RemoteDownload.vue | 163 +
.../system/plugins/components/tabs/Detail.vue | 289 +
.../plugins/components/tabs/Setting.vue | 94 +
.../use-extension-definition-fetch.ts | 22 +
.../system/plugins/composables/use-plugin.ts | 380 +
.../modules/system/plugins/constants/index.ts | 2 +
console-src/modules/system/plugins/module.ts | 59 +
.../modules/system/plugins/types/index.ts | 10 +
.../modules/system/roles/RoleDetail.vue | 298 +
console-src/modules/system/roles/RoleList.vue | 325 +
.../roles/components/RoleEditingModal.vue | 280 +
console-src/modules/system/roles/module.ts | 35 +
.../system/settings/SystemSettings.vue | 104 +
console-src/modules/system/settings/module.ts | 33 +
.../settings/tabs/NotificationSetting.vue | 105 +
.../system/settings/tabs/Notifications.vue | 70 +
.../modules/system/settings/tabs/Setting.vue | 103 +
console-src/modules/system/tools/Tools.vue | 98 +
console-src/modules/system/tools/module.ts | 32 +
.../modules/system/users/UserDetail.vue | 180 +
console-src/modules/system/users/UserList.vue | 554 +
.../users/components/GrantPermissionModal.vue | 78 +
.../users/components/UserCreationModal.vue | 150 +
.../users/components/UserEditingModal.vue | 171 +
.../components/UserPasswordChangeModal.vue | 115 +
.../system/users/composables/use-user.ts | 45 +
console-src/modules/system/users/module.ts | 49 +
.../modules/system/users/tabs/Detail.vue | 89 +
.../users/widgets/NotificationWidget.vue | 107 +
.../users/widgets/RecentLoginWidget.vue | 46 +
.../system/users/widgets/UserStatsWidget.vue | 28 +
console-src/router/constant.ts | 29 +
console-src/router/guards/auth-check.ts | 82 +
console-src/router/guards/check-states.ts | 37 +
console-src/router/guards/permission.ts | 22 +
console-src/router/index.ts | 29 +
console-src/router/routes.config.ts | 101 +
console-src/setup/setupModules.ts | 145 +
console-src/stores/system-configmap.ts | 29 +
console-src/stores/theme.ts | 31 +
console-src/views/system/Binding.vue | 87 +
console-src/views/system/Login.vue | 85 +
console-src/views/system/Redirect.vue | 41 +
console-src/views/system/ResetPassword.vue | 81 +
console-src/views/system/Setup.vue | 124 +
console-src/views/system/SetupInitialData.vue | 165 +
.../views/system/setup-data/category.json | 16 +
.../views/system/setup-data/menu-items.json | 62 +
console-src/views/system/setup-data/menu.json | 14 +
console-src/views/system/setup-data/post.json | 35 +
.../views/system/setup-data/singlePage.json | 31 +
console-src/views/system/setup-data/tag.json | 13 +
cypress.json | 3 +
cypress/fixtures/example.json | 5 +
cypress/integration/example.spec.ts | 8 +
cypress/plugins/index.ts | 19 +
cypress/plugins/tsconfig.json | 14 +
cypress/support/commands.ts | 25 +
cypress/support/index.ts | 20 +
cypress/tsconfig.json | 18 +
docs/components/README.md | 54 +
docs/custom-formkit-input/README.md | 217 +
docs/extension-points/backup.md | 43 +
docs/extension-points/comment-subject-ref.md | 59 +
.../default-editor–extension.md | 19 +
docs/extension-points/editor.md | 74 +
.../extension-points/entity-listitem-field.md | 54 +
.../entity-listitem-operation.md | 72 +
.../plugin-installation-tabs.md | 54 +
docs/extension-points/plugin-self-tabs.md | 40 +
docs/extension-points/theme-list-tabs.md | 54 +
docs/project-structure/README.md | 61 +
docs/routes-generation/README.md | 126 +
env.d.ts | 133 +
index.html | 57 +
package.json | 164 +
packages/api-client/.eslintignore | 2 +
packages/api-client/.eslintrc.js | 3 +
packages/api-client/.gitignore | 69 +
packages/api-client/.openapi_config.yaml | 5 +
packages/api-client/.prettierrc.js | 3 +
packages/api-client/README.md | 69 +
packages/api-client/build.config.ts | 11 +
packages/api-client/entry/api-client.ts | 453 +
packages/api-client/entry/index.ts | 3 +
packages/api-client/openapitools.json | 7 +
packages/api-client/package.json | 57 +
packages/api-client/src/.gitignore | 4 +
packages/api-client/src/.npmignore | 1 +
.../api-client/src/.openapi-generator-ignore | 23 +
.../api-client/src/.openapi-generator/FILES | 334 +
.../api-client/src/.openapi-generator/VERSION | 1 +
packages/api-client/src/api.ts | 91 +
.../api/annotation-setting-v1alpha1-api.ts | 665 +
...tion-halo-run-v1alpha1-subscription-api.ts | 162 +
.../src/api/attachment-v1alpha1-api.ts | 665 +
.../api/attachment-v1alpha1-console-api.ts | 365 +
.../src/api/attachment-v1alpha1-uc-api.ts | 195 +
.../src/api/auth-provider-v1alpha1-api.ts | 665 +
.../api/auth-provider-v1alpha1-console-api.ts | 304 +
.../api-client/src/api/backup-v1alpha1-api.ts | 665 +
.../src/api/category-v1alpha1-api.ts | 665 +
.../src/api/category-v1alpha1-public-api.ts | 445 +
.../src/api/comment-v1alpha1-api.ts | 665 +
.../src/api/comment-v1alpha1-console-api.ts | 439 +
.../src/api/comment-v1alpha1-public-api.ts | 662 +
.../src/api/config-map-v1alpha1-api.ts | 665 +
.../src/api/counter-v1alpha1-api.ts | 665 +
.../api-client/src/api/device-v1alpha1-api.ts | 665 +
.../src/api/device-v1alpha1-uc-api.ts | 215 +
.../api/extension-definition-v1alpha1-api.ts | 665 +
...extension-point-definition-v1alpha1-api.ts | 665 +
.../api-client/src/api/group-v1alpha1-api.ts | 665 +
.../src/api/index-v1alpha1-public-api.ts | 284 +
.../src/api/indices-v1alpha1-console-api.ts | 196 +
packages/api-client/src/api/login-api.ts | 128 +
.../src/api/menu-item-v1alpha1-api.ts | 665 +
.../api-client/src/api/menu-v1alpha1-api.ts | 665 +
.../src/api/menu-v1alpha1-public-api.ts | 215 +
.../src/api/metrics-v1alpha1-public-api.ts | 331 +
.../src/api/migration-v1alpha1-console-api.ts | 358 +
.../api/notification-template-v1alpha1-api.ts | 665 +
.../src/api/notification-v1alpha1-api.ts | 665 +
.../src/api/notification-v1alpha1-uc-api.ts | 707 +
.../api/notifier-descriptor-v1alpha1-api.ts | 665 +
.../src/api/notifier-v1alpha1-console-api.ts | 339 +
.../src/api/notifier-v1alpha1-uc-api.ts | 248 +
.../api/personal-access-token-v1alpha1-api.ts | 665 +
.../personal-access-token-v1alpha1-uc-api.ts | 565 +
.../api-client/src/api/plugin-v1alpha1-api.ts | 665 +
.../src/api/plugin-v1alpha1-console-api.ts | 1444 +
.../src/api/plugin-v1alpha1-public-api.ts | 147 +
.../src/api/policy-template-v1alpha1-api.ts | 665 +
.../api-client/src/api/policy-v1alpha1-api.ts | 665 +
.../api-client/src/api/post-v1alpha1-api.ts | 665 +
.../src/api/post-v1alpha1-console-api.ts | 1407 +
.../src/api/post-v1alpha1-public-api.ts | 380 +
.../src/api/post-v1alpha1-uc-api.ts | 901 +
.../src/api/reason-type-v1alpha1-api.ts | 665 +
.../api-client/src/api/reason-v1alpha1-api.ts | 665 +
.../src/api/remember-me-token-v1alpha1-api.ts | 665 +
.../api-client/src/api/reply-v1alpha1-api.ts | 665 +
.../src/api/reply-v1alpha1-console-api.ts | 217 +
.../src/api/reverse-proxy-v1alpha1-api.ts | 665 +
.../src/api/role-binding-v1alpha1-api.ts | 665 +
.../api-client/src/api/role-v1alpha1-api.ts | 665 +
.../src/api/search-engine-v1alpha1-api.ts | 665 +
.../api-client/src/api/secret-v1alpha1-api.ts | 665 +
.../src/api/setting-v1alpha1-api.ts | 665 +
.../src/api/single-page-v1alpha1-api.ts | 665 +
.../api/single-page-v1alpha1-console-api.ts | 1231 +
.../api/single-page-v1alpha1-public-api.ts | 291 +
.../src/api/snapshot-v1alpha1-api.ts | 665 +
.../src/api/snapshot-v1alpha1-uc-api.ts | 177 +
.../src/api/subscription-v1alpha1-api.ts | 665 +
.../src/api/system-v1alpha1-console-api.ts | 217 +
.../src/api/system-v1alpha1-public-api.ts | 128 +
.../api-client/src/api/tag-v1alpha1-api.ts | 665 +
.../src/api/tag-v1alpha1-console-api.ts | 215 +
.../src/api/tag-v1alpha1-public-api.ts | 445 +
.../api-client/src/api/theme-v1alpha1-api.ts | 665 +
.../src/api/theme-v1alpha1-console-api.ts | 1263 +
.../api/two-factor-auth-v1alpha1-uc-api.ts | 548 +
.../src/api/user-connection-v1alpha1-api.ts | 665 +
.../api-client/src/api/user-v1alpha1-api.ts | 665 +
.../src/api/user-v1alpha1-console-api.ts | 1326 +
.../src/api/user-v1alpha1-public-api.ts | 438 +
packages/api-client/src/base.ts | 86 +
packages/api-client/src/common.ts | 150 +
packages/api-client/src/configuration.ts | 110 +
packages/api-client/src/git_push.sh | 57 +
packages/api-client/src/index.ts | 18 +
.../api-client/src/models/add-operation.ts | 49 +
.../src/models/annotation-setting-list.ts | 81 +
.../src/models/annotation-setting-spec.ts | 39 +
.../src/models/annotation-setting.ts | 54 +
.../api-client/src/models/attachment-list.ts | 81 +
.../api-client/src/models/attachment-spec.ts | 66 +
.../src/models/attachment-status.ts | 30 +
packages/api-client/src/models/attachment.ts | 63 +
.../src/models/auth-provider-list.ts | 81 +
.../src/models/auth-provider-spec.ts | 96 +
.../api-client/src/models/auth-provider.ts | 54 +
packages/api-client/src/models/author.ts | 36 +
packages/api-client/src/models/backup-file.ts | 42 +
packages/api-client/src/models/backup-list.ts | 81 +
packages/api-client/src/models/backup-spec.ts | 36 +
.../api-client/src/models/backup-status.ts | 76 +
packages/api-client/src/models/backup.ts | 63 +
.../api-client/src/models/category-list.ts | 81 +
.../api-client/src/models/category-spec.ts | 84 +
.../api-client/src/models/category-status.ts | 42 +
.../api-client/src/models/category-vo-list.ts | 81 +
packages/api-client/src/models/category-vo.ts | 57 +
packages/api-client/src/models/category.ts | 63 +
.../src/models/change-own-password-request.ts | 36 +
.../src/models/change-password-request.ts | 30 +
.../src/models/comment-email-owner.ts | 48 +
.../api-client/src/models/comment-list.ts | 81 +
.../api-client/src/models/comment-owner.ts | 48 +
.../api-client/src/models/comment-request.ts | 60 +
.../api-client/src/models/comment-spec.ts | 114 +
.../api-client/src/models/comment-stats-vo.ts | 30 +
.../api-client/src/models/comment-stats.ts | 30 +
.../api-client/src/models/comment-status.ts | 60 +
.../api-client/src/models/comment-vo-list.ts | 81 +
packages/api-client/src/models/comment-vo.ts | 69 +
.../src/models/comment-with-reply-vo-list.ts | 81 +
.../src/models/comment-with-reply-vo.ts | 78 +
packages/api-client/src/models/comment.ts | 63 +
packages/api-client/src/models/condition.ts | 63 +
.../api-client/src/models/config-map-list.ts | 81 +
.../api-client/src/models/config-map-ref.ts | 30 +
packages/api-client/src/models/config-map.ts | 51 +
.../src/models/content-update-param.ts | 48 +
packages/api-client/src/models/content-vo.ts | 36 +
.../api-client/src/models/content-wrapper.ts | 48 +
packages/api-client/src/models/content.ts | 42 +
.../api-client/src/models/contributor-vo.ts | 63 +
packages/api-client/src/models/contributor.ts | 42 +
.../api-client/src/models/copy-operation.ts | 49 +
.../api-client/src/models/counter-list.ts | 81 +
.../api-client/src/models/counter-request.ts | 66 +
packages/api-client/src/models/counter.ts | 75 +
.../src/models/create-user-request.ts | 78 +
.../api-client/src/models/custom-templates.ts | 45 +
.../api-client/src/models/dashboard-stats.ts | 60 +
.../api-client/src/models/detailed-user.ts | 42 +
packages/api-client/src/models/device-list.ts | 81 +
packages/api-client/src/models/device-spec.ts | 66 +
.../api-client/src/models/device-status.ts | 36 +
packages/api-client/src/models/device.ts | 63 +
.../models/email-config-validation-request.ts | 72 +
.../src/models/email-verify-request.ts | 30 +
packages/api-client/src/models/excerpt.ts | 36 +
.../src/models/extension-definition-list.ts | 81 +
.../src/models/extension-definition.ts | 54 +
.../models/extension-point-definition-list.ts | 81 +
.../src/models/extension-point-definition.ts | 54 +
.../src/models/extension-point-spec.ts | 62 +
.../api-client/src/models/extension-spec.ts | 54 +
packages/api-client/src/models/extension.ts | 45 +
.../src/models/file-reverse-proxy-provider.ts | 36 +
.../api-client/src/models/grant-request.ts | 30 +
packages/api-client/src/models/group-kind.ts | 36 +
packages/api-client/src/models/group-list.ts | 81 +
packages/api-client/src/models/group-spec.ts | 30 +
.../api-client/src/models/group-status.ts | 36 +
packages/api-client/src/models/group.ts | 63 +
.../api-client/src/models/halo-document.ts | 120 +
packages/api-client/src/models/index.ts | 250 +
.../src/models/install-from-uri-request.ts | 30 +
.../src/models/interest-reason-subject.ts | 42 +
.../api-client/src/models/interest-reason.ts | 45 +
.../api-client/src/models/json-patch-inner.ts | 41 +
packages/api-client/src/models/license.ts | 36 +
.../src/models/list-result-reply-vo.ts | 81 +
.../src/models/listed-auth-provider.ts | 102 +
.../src/models/listed-comment-list.ts | 81 +
.../api-client/src/models/listed-comment.ts | 60 +
.../api-client/src/models/listed-post-list.ts | 81 +
.../src/models/listed-post-vo-list.ts | 81 +
.../api-client/src/models/listed-post-vo.ts | 93 +
packages/api-client/src/models/listed-post.ts | 75 +
.../src/models/listed-reply-list.ts | 81 +
.../api-client/src/models/listed-reply.ts | 51 +
.../src/models/listed-single-page-list.ts | 81 +
.../src/models/listed-single-page-vo-list.ts | 81 +
.../src/models/listed-single-page-vo.ts | 75 +
.../src/models/listed-single-page.ts | 57 +
.../src/models/listed-snapshot-dto.ts | 42 +
.../src/models/listed-snapshot-spec.ts | 36 +
packages/api-client/src/models/listed-user.ts | 42 +
.../api-client/src/models/login-history.ts | 54 +
.../src/models/mark-specified-request.ts | 30 +
.../api-client/src/models/menu-item-list.ts | 81 +
.../api-client/src/models/menu-item-spec.ts | 73 +
.../api-client/src/models/menu-item-status.ts | 36 +
.../api-client/src/models/menu-item-vo.ts | 63 +
packages/api-client/src/models/menu-item.ts | 63 +
packages/api-client/src/models/menu-list.ts | 81 +
packages/api-client/src/models/menu-spec.ts | 36 +
packages/api-client/src/models/menu-vo.ts | 51 +
packages/api-client/src/models/menu.ts | 54 +
packages/api-client/src/models/metadata.ts | 72 +
.../api-client/src/models/move-operation.ts | 49 +
.../src/models/navigation-post-vo.ts | 45 +
.../src/models/notification-list.ts | 81 +
.../src/models/notification-spec.ts | 66 +
.../src/models/notification-template-list.ts | 81 +
.../src/models/notification-template-spec.ts | 42 +
.../src/models/notification-template.ts | 54 +
.../api-client/src/models/notification.ts | 54 +
.../src/models/notifier-descriptor-list.ts | 81 +
.../src/models/notifier-descriptor-spec.ts | 57 +
.../src/models/notifier-descriptor.ts | 54 +
.../api-client/src/models/notifier-info.ts | 42 +
.../src/models/notifier-setting-ref.ts | 36 +
packages/api-client/src/models/owner-info.ts | 54 +
.../api-client/src/models/password-request.ts | 30 +
.../models/password-reset-email-request.ts | 36 +
packages/api-client/src/models/pat-spec.ts | 84 +
.../src/models/personal-access-token-list.ts | 81 +
.../src/models/personal-access-token.ts | 54 +
.../api-client/src/models/plugin-author.ts | 36 +
packages/api-client/src/models/plugin-list.ts | 81 +
.../models/plugin-running-state-request.ts | 36 +
packages/api-client/src/models/plugin-spec.ts | 121 +
.../api-client/src/models/plugin-status.ts | 102 +
packages/api-client/src/models/plugin.ts | 63 +
packages/api-client/src/models/policy-list.ts | 81 +
packages/api-client/src/models/policy-rule.ts | 54 +
packages/api-client/src/models/policy-spec.ts | 42 +
.../src/models/policy-template-list.ts | 81 +
.../src/models/policy-template-spec.ts | 36 +
.../api-client/src/models/policy-template.ts | 54 +
packages/api-client/src/models/policy.ts | 54 +
packages/api-client/src/models/post-list.ts | 81 +
.../api-client/src/models/post-request.ts | 42 +
packages/api-client/src/models/post-spec.ts | 150 +
packages/api-client/src/models/post-status.ts | 87 +
packages/api-client/src/models/post-vo.ts | 102 +
packages/api-client/src/models/post.ts | 63 +
.../src/models/public-key-response.ts | 30 +
.../src/models/reason-attributes.ts | 30 +
packages/api-client/src/models/reason-list.ts | 81 +
.../api-client/src/models/reason-property.ts | 48 +
.../api-client/src/models/reason-selector.ts | 36 +
packages/api-client/src/models/reason-spec.ts | 54 +
.../api-client/src/models/reason-subject.ts | 54 +
.../api-client/src/models/reason-type-info.ts | 48 +
.../api-client/src/models/reason-type-list.ts | 81 +
...reason-type-notifier-collection-request.ts | 33 +
.../src/models/reason-type-notifier-matrix.ts | 48 +
.../models/reason-type-notifier-request.ts | 36 +
.../api-client/src/models/reason-type-spec.ts | 45 +
packages/api-client/src/models/reason-type.ts | 54 +
packages/api-client/src/models/reason.ts | 54 +
packages/api-client/src/models/ref.ts | 48 +
.../models/register-verify-email-request.ts | 30 +
.../src/models/remember-me-token-list.ts | 81 +
.../src/models/remember-me-token-spec.ts | 48 +
.../src/models/remember-me-token.ts | 54 +
.../api-client/src/models/remove-operation.ts | 43 +
.../src/models/replace-operation.ts | 49 +
packages/api-client/src/models/reply-list.ts | 81 +
.../api-client/src/models/reply-request.ts | 57 +
packages/api-client/src/models/reply-spec.ts | 111 +
.../api-client/src/models/reply-status.ts | 30 +
.../api-client/src/models/reply-vo-list.ts | 81 +
packages/api-client/src/models/reply-vo.ts | 60 +
packages/api-client/src/models/reply.ts | 63 +
.../src/models/reset-password-request.ts | 36 +
.../src/models/reverse-proxy-list.ts | 81 +
.../src/models/reverse-proxy-rule.ts | 39 +
.../api-client/src/models/reverse-proxy.ts | 54 +
.../models/revert-snapshot-for-post-param.ts | 30 +
.../revert-snapshot-for-single-param.ts | 30 +
.../src/models/role-binding-list.ts | 81 +
.../api-client/src/models/role-binding.ts | 63 +
packages/api-client/src/models/role-list.ts | 81 +
packages/api-client/src/models/role-ref.ts | 42 +
packages/api-client/src/models/role.ts | 54 +
.../src/models/search-engine-list.ts | 81 +
.../src/models/search-engine-spec.ts | 63 +
.../api-client/src/models/search-engine.ts | 54 +
.../api-client/src/models/search-option.ts | 96 +
.../api-client/src/models/search-result.ts | 57 +
packages/api-client/src/models/secret-list.ts | 81 +
packages/api-client/src/models/secret.ts | 63 +
.../api-client/src/models/setting-form.ts | 42 +
.../api-client/src/models/setting-list.ts | 81 +
packages/api-client/src/models/setting-ref.ts | 36 +
.../api-client/src/models/setting-spec.ts | 33 +
packages/api-client/src/models/setting.ts | 54 +
.../api-client/src/models/sign-up-request.ts | 45 +
.../api-client/src/models/single-page-list.ts | 81 +
.../src/models/single-page-request.ts | 42 +
.../api-client/src/models/single-page-spec.ts | 138 +
.../src/models/single-page-status.ts | 87 +
.../api-client/src/models/single-page-vo.ts | 84 +
packages/api-client/src/models/single-page.ts | 63 +
.../api-client/src/models/site-stats-vo.ts | 54 +
.../api-client/src/models/snap-shot-spec.ts | 75 +
.../api-client/src/models/snapshot-list.ts | 81 +
packages/api-client/src/models/snapshot.ts | 54 +
packages/api-client/src/models/stats-vo.ts | 42 +
packages/api-client/src/models/stats.ts | 48 +
packages/api-client/src/models/subject.ts | 42 +
.../src/models/subscription-list.ts | 81 +
.../src/models/subscription-spec.ts | 54 +
.../src/models/subscription-subscriber.ts | 30 +
.../api-client/src/models/subscription.ts | 54 +
.../models/system-initialization-request.ts | 48 +
packages/api-client/src/models/tag-list.ts | 81 +
packages/api-client/src/models/tag-spec.ts | 48 +
packages/api-client/src/models/tag-status.ts | 48 +
packages/api-client/src/models/tag-vo-list.ts | 81 +
packages/api-client/src/models/tag-vo.ts | 57 +
packages/api-client/src/models/tag.ts | 63 +
.../api-client/src/models/template-content.ts | 42 +
.../src/models/template-descriptor.ts | 48 +
.../api-client/src/models/test-operation.ts | 49 +
packages/api-client/src/models/theme-list.ts | 81 +
packages/api-client/src/models/theme-spec.ts | 125 +
.../api-client/src/models/theme-status.ts | 54 +
packages/api-client/src/models/theme.ts | 63 +
.../src/models/totp-auth-link-response.ts | 36 +
.../api-client/src/models/totp-request.ts | 42 +
.../src/models/two-factor-auth-settings.ts | 48 +
.../src/models/upgrade-from-uri-request.ts | 30 +
.../src/models/user-connection-list.ts | 81 +
.../src/models/user-connection-spec.ts | 84 +
.../api-client/src/models/user-connection.ts | 54 +
packages/api-client/src/models/user-device.ts | 45 +
.../models/user-endpoint-listed-user-list.ts | 81 +
packages/api-client/src/models/user-list.ts | 81 +
.../api-client/src/models/user-permission.ts | 45 +
packages/api-client/src/models/user-spec.ts | 96 +
packages/api-client/src/models/user-status.ts | 45 +
packages/api-client/src/models/user.ts | 63 +
.../src/models/verify-code-request.ts | 36 +
.../api-client/src/models/vote-request.ts | 42 +
packages/api-client/tsconfig.json | 15 +
packages/api-client/vite.config.ts | 30 +
packages/components/.eslintignore | 1 +
packages/components/.eslintrc.cjs | 3 +
packages/components/.storybook/main.ts | 27 +
packages/components/.storybook/preview.ts | 19 +
packages/components/env.d.ts | 12 +
packages/components/package.json | 77 +
packages/components/postcss.config.js | 6 +
packages/components/prettier.config.js | 3 +
packages/components/src/components.ts | 21 +
.../src/components/alert/Alert.stories.ts | 68 +
.../components/src/components/alert/Alert.vue | 203 +
.../components/alert/__tests__/Alert.spec.ts | 8 +
.../components/src/components/alert/index.ts | 1 +
.../src/components/alert/interface.ts | 1 +
.../src/components/avatar/Avatar.stories.ts | 47 +
.../src/components/avatar/Avatar.vue | 183 +
.../components/avatar/AvatarGroup.stories.ts | 41 +
.../src/components/avatar/AvatarGroup.vue | 30 +
.../components/src/components/avatar/index.ts | 2 +
.../src/components/avatar/interface.ts | 17 +
.../src/components/button/Button.stories.ts | 61 +
.../src/components/button/Button.vue | 212 +
.../button/__tests__/Button.spec.ts | 140 +
.../__snapshots__/Button.spec.ts.snap | 61 +
.../components/src/components/button/index.ts | 1 +
.../src/components/button/interface.ts | 2 +
.../src/components/card/Card.stories.ts | 629 +
.../components/src/components/card/Card.vue | 69 +
.../components/card/__tests__/Card.spec.ts | 8 +
.../components/src/components/card/index.ts | 1 +
.../components/description/Description.vue | 13 +
.../description/DescriptionItem.vue | 42 +
.../src/components/description/index.ts | 2 +
.../src/components/dialog/Dialog.stories.ts | 66 +
.../src/components/dialog/Dialog.vue | 136 +
.../dialog/__tests__/Dialog.spec.ts | 9 +
.../src/components/dialog/dialog-manager.ts | 75 +
.../components/src/components/dialog/index.ts | 2 +
.../src/components/dialog/interface.ts | 23 +
.../components/dropdown/Draopdown.stories.ts | 46 +
.../src/components/dropdown/Dropdown.vue | 66 +
.../components/dropdown/DropdownDivider.vue | 5 +
.../src/components/dropdown/DropdownItem.vue | 79 +
.../src/components/dropdown/index.ts | 6 +
.../src/components/dropdown/symbols.ts | 5 +
.../src/components/empty/Empty.stories.ts | 43 +
.../components/src/components/empty/Empty.svg | 90 +
.../components/src/components/empty/Empty.vue | 57 +
.../components/empty/__tests__/Empty.spec.ts | 99 +
.../__snapshots__/Empty.spec.ts.snap | 12 +
.../components/src/components/empty/index.ts | 1 +
.../src/components/entity/Entity.vue | 94 +
.../src/components/entity/EntityField.vue | 94 +
.../entity/__tests__/Entity.spec.ts | 9 +
.../entity/__tests__/EntityField.spec.ts | 9 +
.../components/src/components/entity/index.ts | 2 +
.../src/components/header/PageHeader.vue | 18 +
.../components/src/components/header/index.ts | 1 +
.../src/components/loading/Loading.stories.ts | 23 +
.../src/components/loading/Loading.vue | 26 +
.../src/components/loading/index.ts | 1 +
.../src/components/menu/Menu.stories.ts | 79 +
.../components/src/components/menu/Menu.vue | 17 +
.../src/components/menu/MenuItem.vue | 141 +
.../src/components/menu/MenuLabel.vue | 14 +
.../components/menu/__tests__/Menu.spec.tsx | 107 +
.../menu/__tests__/MenuLabel.spec.ts | 12 +
.../__snapshots__/Menu.spec.tsx.snap | 105 +
.../__snapshots__/MenuLabel.spec.ts.snap | 3 +
.../components/src/components/menu/index.ts | 3 +
.../src/components/modal/Modal.stories.ts | 67 +
.../components/src/components/modal/Modal.vue | 293 +
.../components/modal/__tests__/Modal.spec.ts | 8 +
.../components/src/components/modal/index.ts | 1 +
.../pagination/Pagination.stories.ts | 34 +
.../src/components/pagination/Pagination.vue | 176 +
.../pagination/__tests__/Pagination.spec.ts | 9 +
.../src/components/pagination/index.ts | 1 +
.../src/components/space/Space.stories.ts | 51 +
.../components/src/components/space/Space.vue | 62 +
.../components/space/__tests__/Space.spec.ts | 48 +
.../components/src/components/space/index.ts | 1 +
.../src/components/space/interface.ts | 9 +
.../components/status/StatusDot.stories.ts | 56 +
.../src/components/status/StatusDot.vue | 92 +
.../status/__tests__/StatusDot.spec.ts | 31 +
.../__snapshots__/StatusDot.spec.ts.snap | 8 +
.../components/src/components/status/index.ts | 1 +
.../src/components/status/interface.ts | 1 +
.../src/components/switch/Switch.stories.ts | 27 +
.../src/components/switch/Switch.vue | 116 +
.../switch/__tests__/Switch.spec.ts | 9 +
.../components/src/components/switch/index.ts | 1 +
.../src/components/tabs/TabItem.vue | 20 +
.../src/components/tabs/Tabbar.stories.ts | 71 +
.../components/src/components/tabs/Tabbar.vue | 432 +
.../src/components/tabs/Tabs.stories.ts | 61 +
.../components/src/components/tabs/Tabs.vue | 88 +
.../components/tabs/__tests__/TabItem.spec.ts | 8 +
.../components/tabs/__tests__/Tabbar.spec.ts | 8 +
.../components/tabs/__tests__/Tabs.spec.ts | 8 +
.../components/src/components/tabs/index.ts | 3 +
.../src/components/tabs/interface.ts | 6 +
.../src/components/tag/Tag.stories.ts | 64 +
.../src/components/tag/Tag.story.vue | 49 +
.../components/src/components/tag/Tag.vue | 87 +
.../src/components/tag/__tests__/Tag.spec.ts | 8 +
.../components/src/components/tag/index.ts | 1 +
.../src/components/tag/interface.ts | 1 +
.../src/components/toast/Toast.story.vue | 39 +
.../components/src/components/toast/Toast.vue | 172 +
.../components/src/components/toast/index.ts | 1 +
.../src/components/toast/interface.ts | 11 +
.../src/components/toast/toast-manager.ts | 131 +
.../src/components/tooltip/index.ts | 3 +
.../src/components/tooltip/style.css | 3 +
packages/components/src/icons/icons.ts | 155 +
packages/components/src/index.ts | 5 +
.../components/src/stories/Introduction.mdx | 213 +
.../src/stories/assets/code-brackets.svg | 1 +
.../components/src/stories/assets/colors.svg | 1 +
.../src/stories/assets/comments.svg | 1 +
.../src/stories/assets/direction.svg | 1 +
.../components/src/stories/assets/flow.svg | 1 +
.../components/src/stories/assets/plugin.svg | 1 +
.../components/src/stories/assets/repo.svg | 1 +
.../src/stories/assets/stackalt.svg | 1 +
packages/components/src/styles/tailwind.css | 3 +
packages/components/tailwind.config.js | 3 +
packages/components/tsconfig.app.json | 13 +
packages/components/tsconfig.json | 14 +
packages/components/tsconfig.node.json | 9 +
packages/components/tsconfig.vitest.json | 9 +
packages/components/vite.config.ts | 60 +
packages/editor/.eslintrc.cjs | 6 +
packages/editor/.gitignore | 28 +
packages/editor/.release-it.json | 8 +
packages/editor/docs/extension.md | 444 +
packages/editor/docs/extension.png | Bin 0 -> 143351 bytes
packages/editor/env.d.ts | 2 +
packages/editor/index.html | 13 +
packages/editor/package.json | 98 +
packages/editor/postcss.config.js | 6 +
packages/editor/prettier.config.js | 3 +
packages/editor/src/components/Editor.vue | 59 +
.../src/components/EditorBubbleMenu.vue | 102 +
.../editor/src/components/EditorHeader.vue | 125 +
.../components/block/BlockActionButton.vue | 36 +
.../src/components/block/BlockActionInput.vue | 38 +
.../components/block/BlockActionSeparator.vue | 10 +
.../editor/src/components/block/BlockCard.vue | 100 +
packages/editor/src/components/block/index.ts | 4 +
.../src/components/bubble/BubbleItem.vue | 86 +
.../src/components/bubble/BubbleMenu.vue | 78 +
.../src/components/bubble/BubbleMenuPlugin.ts | 357 +
.../editor/src/components/bubble/index.ts | 2 +
.../components/common/ColorPickerDropdown.vue | 118 +
.../icon/MdiDeleteForeverOutline.vue | 7 +
packages/editor/src/components/index.ts | 15 +
.../src/components/toolbar/ToolbarItem.vue | 43 +
.../src/components/toolbar/ToolbarSubItem.vue | 55 +
.../editor/src/components/toolbar/index.ts | 2 +
.../src/components/toolbox/ToolboxItem.vue | 47 +
.../editor/src/components/toolbox/index.ts | 1 +
packages/editor/src/dev/App.vue | 133 +
packages/editor/src/dev/main.ts | 6 +
.../editor/src/extensions/audio/AudioView.vue | 74 +
.../extensions/audio/BubbleItemAudioLink.vue | 37 +
packages/editor/src/extensions/audio/index.ts | 296 +
.../editor/src/extensions/blockquote/index.ts | 51 +
packages/editor/src/extensions/bold/index.ts | 33 +
.../src/extensions/bullet-list/index.ts | 64 +
.../src/extensions/clear-format/index.ts | 35 +
.../code-block/CodeBlockViewRenderer.vue | 111 +
.../src/extensions/code-block/code-block.ts | 363 +
.../editor/src/extensions/code-block/index.ts | 2 +
.../src/extensions/code-block/lowlight.ts | 8 +
packages/editor/src/extensions/code/index.ts | 32 +
.../src/extensions/color/ColorBubbleItem.vue | 48 +
.../src/extensions/color/ColorToolbarItem.vue | 58 +
packages/editor/src/extensions/color/index.ts | 34 +
.../editor/src/extensions/columns/column.ts | 47 +
.../editor/src/extensions/columns/columns.ts | 387 +
.../editor/src/extensions/columns/index.ts | 2 +
.../extensions/commands-menu/CommandsView.vue | 152 +
.../src/extensions/commands-menu/commands.ts | 123 +
.../src/extensions/commands-menu/index.ts | 1 +
.../editor/src/extensions/draggable/index.ts | 589 +
.../editor/src/extensions/font-size/index.ts | 117 +
.../src/extensions/format-brush/index.ts | 120 +
.../src/extensions/format-brush/util.ts | 119 +
.../gap-cursor/gap-cursor-selection.ts | 240 +
.../editor/src/extensions/gap-cursor/index.ts | 394 +
.../editor/src/extensions/heading/index.ts | 323 +
.../highlight/HighlightBubbleItem.vue | 63 +
.../highlight/HighlightToolbarItem.vue | 74 +
.../editor/src/extensions/highlight/index.ts | 30 +
.../editor/src/extensions/history/index.ts | 45 +
.../iframe/BubbleItemIframeLink.vue | 36 +
.../iframe/BubbleItemIframeSize.vue | 51 +
.../src/extensions/iframe/IframeView.vue | 83 +
.../editor/src/extensions/iframe/index.ts | 497 +
.../extensions/image/BubbleItemImageAlt.vue | 37 +
.../extensions/image/BubbleItemImageHref.vue | 56 +
.../extensions/image/BubbleItemImageLink.vue | 37 +
.../extensions/image/BubbleItemImageSize.vue | 106 +
.../editor/src/extensions/image/ImageView.vue | 140 +
packages/editor/src/extensions/image/index.ts | 284 +
.../editor/src/extensions/indent/index.ts | 266 +
packages/editor/src/extensions/index.ts | 161 +
.../editor/src/extensions/italic/index.ts | 31 +
.../src/extensions/link/LinkBubbleButton.vue | 116 +
packages/editor/src/extensions/link/index.ts | 23 +
.../src/extensions/list-keymap/index.ts | 47 +
.../src/extensions/node-selected/index.ts | 59 +
.../src/extensions/ordered-list/index.ts | 65 +
.../editor/src/extensions/paragraph/index.ts | 188 +
.../src/extensions/range-selection/index.ts | 160 +
.../range-selection/range-selection.ts | 190 +
.../search-and-replace/SearchAndReplace.vue | 358 +
.../SearchAndReplacePlugin.ts | 412 +
.../extensions/search-and-replace/index.ts | 295 +
.../editor/src/extensions/strike/index.ts | 31 +
.../editor/src/extensions/subscript/index.ts | 33 +
.../src/extensions/superscript/index.ts | 33 +
packages/editor/src/extensions/table/index.ts | 609 +
.../editor/src/extensions/table/table-cell.ts | 196 +
.../src/extensions/table/table-header.ts | 151 +
.../editor/src/extensions/table/table-row.ts | 17 +
packages/editor/src/extensions/table/util.ts | 379 +
.../editor/src/extensions/task-list/index.ts | 62 +
.../editor/src/extensions/text-align/index.ts | 101 +
.../editor/src/extensions/text-style/index.ts | 11 +
packages/editor/src/extensions/text/index.ts | 203 +
.../src/extensions/trailing-node/index.ts | 79 +
.../editor/src/extensions/underline/index.ts | 31 +
.../extensions/video/BubbleItemVideoLink.vue | 32 +
.../extensions/video/BubbleItemVideoSize.vue | 59 +
.../editor/src/extensions/video/VideoView.vue | 86 +
packages/editor/src/extensions/video/index.ts | 469 +
packages/editor/src/index.ts | 21 +
packages/editor/src/locales/en.yaml | 143 +
packages/editor/src/locales/index.ts | 21 +
packages/editor/src/locales/zh-CN.yaml | 143 +
packages/editor/src/styles/base.scss | 101 +
packages/editor/src/styles/columns.scss | 14 +
packages/editor/src/styles/draggable.scss | 27 +
packages/editor/src/styles/format-brush.scss | 9 +
packages/editor/src/styles/gap-cursor.scss | 43 +
packages/editor/src/styles/index.scss | 9 +
packages/editor/src/styles/node-select.scss | 9 +
.../editor/src/styles/range-selection.scss | 33 +
packages/editor/src/styles/search.scss | 11 +
packages/editor/src/styles/table.scss | 325 +
packages/editor/src/styles/tailwind.css | 26 +
packages/editor/src/tiptap/core/index.ts | 1 +
packages/editor/src/tiptap/index.ts | 25 +
packages/editor/src/tiptap/pm/index.ts | 20 +
packages/editor/src/tiptap/vue-3/index.ts | 1 +
packages/editor/src/types/index.ts | 135 +
packages/editor/src/utils/anchor.ts | 20 +
packages/editor/src/utils/delete-node.ts | 75 +
packages/editor/src/utils/index.ts | 2 +
packages/editor/src/utils/is-allowed-uri.ts | 12 +
packages/editor/src/utils/isListActive.ts | 42 +
packages/editor/src/utils/isNodeEmpty.ts | 19 +
packages/editor/tailwind.config.js | 3 +
packages/editor/tsconfig.app.json | 13 +
packages/editor/tsconfig.json | 14 +
packages/editor/tsconfig.node.json | 10 +
packages/editor/tsconfig.vitest.json | 9 +
packages/editor/vite.config.ts | 22 +
packages/editor/vite.lib.config.ts | 59 +
packages/shared/.eslintrc.cjs | 3 +
packages/shared/env.d.ts | 29 +
packages/shared/package.json | 50 +
packages/shared/prettier.config.js | 3 +
packages/shared/src/components/.gitkeep | 0
packages/shared/src/core/plugins.ts | 5 +
packages/shared/src/index.ts | 14 +
.../shared/src/states/attachment-selector.ts | 17 +
packages/shared/src/states/backup.ts | 8 +
.../shared/src/states/comment-subject-ref.ts | 15 +
packages/shared/src/states/editor.ts | 9 +
packages/shared/src/states/entity.ts | 10 +
packages/shared/src/states/operation.ts | 12 +
packages/shared/src/states/pages.ts | 7 +
.../src/states/plugin-installation-tabs.ts | 10 +
packages/shared/src/states/plugin-tab.ts | 8 +
packages/shared/src/states/theme-list-tabs.ts | 10 +
packages/shared/src/states/user-tab.ts | 17 +
packages/shared/src/types/menus.ts | 24 +
packages/shared/src/types/plugin.ts | 115 +
packages/shared/tsconfig.app.json | 12 +
packages/shared/tsconfig.json | 14 +
packages/shared/tsconfig.node.json | 9 +
packages/shared/tsconfig.vitest.json | 9 +
packages/shared/vite.config.ts | 46 +
packages/ui-plugin-bundler-kit/.eslintignore | 2 +
packages/ui-plugin-bundler-kit/.eslintrc.js | 3 +
.../ui-plugin-bundler-kit/.prettierrc.cjs | 3 +
.../ui-plugin-bundler-kit/build.config.ts | 12 +
packages/ui-plugin-bundler-kit/package.json | 45 +
packages/ui-plugin-bundler-kit/src/index.ts | 69 +
packages/ui-plugin-bundler-kit/tsconfig.json | 18 +
pnpm-lock.yaml | 22612 ++++++++++++++++
pnpm-workspace.yaml | 2 +
postcss.config.cjs | 7 +
prettier.config.cjs | 3 +
public/favicon.ico | Bin 0 -> 15086 bytes
src/assets/logo.png | Bin 0 -> 35203 bytes
src/assets/logo.svg | 1 +
src/components/base-app/BaseApp.vue | 106 +
src/components/button/SubmitButton.vue | 45 +
src/components/codemirror/Codemirror.vue | 120 +
src/components/common/AppDownloadAlert.vue | 41 +
src/components/common/LocaleChange.vue | 42 +
.../EditorProviderSelector.vue | 70 +
src/components/editor/DefaultEditor.vue | 680 +
.../editor/components/EditorLinkObtain.vue | 232 +
.../editor/components/InlineBlockBox.vue | 13 +
src/components/editor/components/index.ts | 1 +
.../editor/composables/use-attachment.ts | 26 +
.../editor/extensions/audio/AudioView.vue | 196 +
.../editor/extensions/audio/index.ts | 42 +
.../editor/extensions/image/ImageView.vue | 290 +
.../editor/extensions/image/index.ts | 74 +
src/components/editor/extensions/index.ts | 10 +
.../editor/extensions/upload/index.ts | 82 +
.../editor/extensions/video/VideoView.vue | 214 +
.../editor/extensions/video/index.ts | 42 +
src/components/editor/utils/attachment.ts | 95 +
src/components/editor/utils/upload.ts | 144 +
.../entity-fields/EntityFieldItems.vue | 26 +
.../entity-fields/StatusDotField.vue | 31 +
src/components/entity/EntityDropdownItems.vue | 74 +
.../filter/CategoryFilterDropdown.vue | 141 +
src/components/filter/FilterCleanButton.vue | 15 +
src/components/filter/FilterDropdown.vue | 67 +
src/components/filter/FilterTag.vue | 25 +
src/components/filter/TagFilterDropdown.vue | 141 +
src/components/filter/UserFilterDropdown.vue | 138 +
src/components/form/AnnotationsForm.vue | 299 +
.../global-search/GlobalSearchModal.vue | 443 +
.../icon/AttachmentFileTypeIcon.vue | 117 +
src/components/image/LazyImage.vue | 48 +
src/components/input/SearchInput.vue | 56 +
src/components/login/LoginForm.vue | 188 +
src/components/login/LoginModal.vue | 37 +
src/components/login/MfaForm.vue | 93 +
.../login/SocialAuthProviderItem.vue | 81 +
src/components/login/SocialAuthProviders.vue | 34 +
src/components/menu/RoutesMenu.tsx | 89 +
src/components/permission/HasPermission.vue | 16 +
src/components/preview/UrlPreviewModal.vue | 90 +
src/components/signup/SignupForm.vue | 260 +
src/components/sticky-block/StickyBlock.vue | 53 +
src/components/upload/UppyUpload.vue | 150 +
src/components/user-avatar/UserAvatar.vue | 223 +
.../user-avatar/UserAvatarCropper.vue | 265 +
src/components/user/PostContributorList.vue | 61 +
src/components/video/LazyVideo.vue | 47 +
src/composables/use-auto-save-content.ts | 41 +
src/composables/use-content-cache.ts | 105 +
.../use-editor-extension-points.ts | 57 +
src/composables/use-role.ts | 293 +
src/composables/use-route-menu-generator.ts | 168 +
src/composables/use-session-keep-alive.ts | 16 +
src/composables/use-title.ts | 24 +
src/constants/annotations.ts | 36 +
src/constants/app.ts | 1 +
src/constants/constants.ts | 1 +
src/constants/error-types.ts | 1 +
src/constants/labels.ts | 30 +
src/formkit/formkit.config.ts | 75 +
src/formkit/inputs/attachment-group-select.ts | 26 +
.../inputs/attachment-policy-select.ts | 23 +
.../inputs/attachment/AttachmentInput.vue | 64 +
src/formkit/inputs/attachment/index.ts | 10 +
src/formkit/inputs/category-checkbox.ts | 29 +
.../inputs/category-select/CategorySelect.vue | 335 +
.../components/CategoryListItem.vue | 68 +
.../components/CategoryTag.vue | 46 +
.../components/SearchResultListItem.vue | 67 +
src/formkit/inputs/category-select/index.ts | 38 +
.../inputs/category-select/sections/index.ts | 11 +
src/formkit/inputs/code/CodeInput.vue | 77 +
src/formkit/inputs/code/index.ts | 8 +
src/formkit/inputs/form.ts | 39 +
src/formkit/inputs/group.ts | 29 +
src/formkit/inputs/list/AddButton.vue | 37 +
src/formkit/inputs/list/features/lists.ts | 79 +
src/formkit/inputs/list/index.ts | 116 +
src/formkit/inputs/list/listSection.ts | 97 +
src/formkit/inputs/list/sections/index.ts | 137 +
src/formkit/inputs/menu-checkbox.ts | 27 +
src/formkit/inputs/menu-item-select.ts | 25 +
src/formkit/inputs/menu-radio.ts | 27 +
src/formkit/inputs/password/RevealButton.vue | 35 +
src/formkit/inputs/password/index.ts | 76 +
src/formkit/inputs/post-select.ts | 29 +
src/formkit/inputs/repeater/AddButton.vue | 37 +
.../inputs/repeater/features/repeats.ts | 69 +
src/formkit/inputs/repeater/index.ts | 113 +
.../inputs/repeater/repeaterSection.ts | 96 +
src/formkit/inputs/repeater/sections/index.ts | 134 +
src/formkit/inputs/role-select.ts | 40 +
src/formkit/inputs/secret/SecretSelect.vue | 316 +
.../secret/components/SecretCreationModal.vue | 81 +
.../secret/components/SecretEditModal.vue | 102 +
.../inputs/secret/components/SecretForm.vue | 59 +
.../secret/components/SecretListItem.vue | 84 +
.../secret/components/SecretListModal.vue | 51 +
.../secret/composables/use-secrets-fetch.ts | 20 +
src/formkit/inputs/secret/index.ts | 32 +
src/formkit/inputs/secret/sections/index.ts | 8 +
src/formkit/inputs/secret/types/index.ts | 4 +
src/formkit/inputs/singlePage-select.ts | 29 +
src/formkit/inputs/tag-checkbox.ts | 29 +
src/formkit/inputs/tag-select/TagSelect.vue | 317 +
src/formkit/inputs/tag-select/index.ts | 38 +
.../inputs/tag-select/sections/index.ts | 8 +
src/formkit/inputs/user-select.ts | 26 +
.../inputs/verify-form/VerificationButton.vue | 137 +
.../inputs/verify-form/features/index.ts | 58 +
src/formkit/inputs/verify-form/index.ts | 64 +
src/formkit/plugins/auto-scroll-to-errors.ts | 34 +
.../plugins/password-prevent-autocomplete.ts | 9 +
src/formkit/plugins/radio-alt.ts | 9 +
src/formkit/plugins/required-asterisk.ts | 38 +
.../plugins/stop-implicit-submission.ts | 81 +
src/formkit/theme.ts | 158 +
src/formkit/utils/focus.ts | 14 +
src/layouts/GatewayLayout.vue | 13 +
src/locales/en.yaml | 1955 ++
src/locales/es.yaml | 1409 +
src/locales/index.ts | 68 +
src/locales/zh-CN.yaml | 1810 ++
src/locales/zh-TW.yaml | 1803 ++
src/setup/setupApiClient.ts | 72 +
src/setup/setupComponents.ts | 49 +
src/setup/setupStyles.ts | 9 +
src/setup/setupVueQuery.ts | 25 +
src/stores/global-info.ts | 17 +
src/stores/plugin.ts | 17 +
src/stores/role.ts | 13 +
src/stores/user.ts | 31 +
src/styles/index.css | 23 +
src/styles/tailwind.css | 26 +
src/types/actuator.ts | 117 +
src/types/index.ts | 1 +
src/types/slug.ts | 7 +
src/utils/__tests__/date.spec.ts | 34 +
src/utils/__tests__/media-type.spec.ts | 44 +
src/utils/__tests__/permission.spec.ts | 36 +
src/utils/date.ts | 67 +
src/utils/device.ts | 1 +
src/utils/id.ts | 7 +
src/utils/image.ts | 16 +
src/utils/load-style.ts | 27 +
src/utils/media-type.ts | 9 +
src/utils/permission.ts | 60 +
src/utils/role.ts | 32 +
src/views/exceptions/Forbidden.vue | 6 +
src/views/exceptions/NotFound.vue | 6 +
.../exceptions/__tests__/NotFound.spec.ts | 7 +
src/views/exceptions/components/Exception.vue | 44 +
src/vite/config-builder.ts | 102 +
src/vite/library-external.ts | 134 +
tailwind.config.cjs | 57 +
tsconfig.app.json | 24 +
tsconfig.json | 14 +
tsconfig.node.json | 16 +
tsconfig.vitest.json | 9 +
uc-src/App.vue | 7 +
uc-src/layouts/BasicLayout.vue | 329 +
uc-src/main.ts | 92 +
uc-src/modules/contents/posts/PostEditor.vue | 546 +
uc-src/modules/contents/posts/PostList.vue | 217 +
.../posts/components/PostCreationModal.vue | 142 +
.../posts/components/PostListItem.vue | 256 +
.../posts/components/PostSettingEditModal.vue | 101 +
.../posts/components/PostSettingForm.vue | 222 +
.../composables/use-post-update-mutate.ts | 38 +
uc-src/modules/contents/posts/module.ts | 46 +
uc-src/modules/contents/posts/types/index.ts | 15 +
uc-src/modules/index.ts | 10 +
.../modules/notifications/Notifications.vue | 137 +
.../components/NotificationContent.vue | 43 +
.../components/NotificationListItem.vue | 125 +
uc-src/modules/notifications/module.ts | 33 +
uc-src/modules/profile/Profile.vue | 184 +
.../profile/components/EmailVerifyModal.vue | 172 +
.../components/PasswordChangeModal.vue | 117 +
.../PersonalAccessTokenCreationModal.vue | 260 +
.../PersonalAccessTokenListItem.vue | 151 +
.../components/ProfileEditingModal.vue | 171 +
.../modules/profile/composables/use-user.ts | 45 +
uc-src/modules/profile/module.ts | 34 +
uc-src/modules/profile/tabs/Detail.vue | 207 +
uc-src/modules/profile/tabs/Devices.vue | 36 +
.../profile/tabs/NotificationPreferences.vue | 163 +
.../profile/tabs/PersonalAccessTokens.vue | 85 +
uc-src/modules/profile/tabs/TwoFactor.vue | 148 +
.../tabs/components/DeviceDetailModal.vue | 92 +
.../tabs/components/DeviceListItem.vue | 119 +
.../components/PasswordValidationForm.vue | 31 +
.../tabs/components/TotpConfigureModal.vue | 146 +
.../tabs/components/TotpDeletionModal.vue | 63 +
.../tabs/components/TwoFactorDisableModal.vue | 63 +
.../tabs/components/TwoFactorEnableModal.vue | 63 +
.../tabs/composables/use-user-agent.ts | 22 +
.../tabs/composables/use-user-device.ts | 29 +
uc-src/router/constant.ts | 29 +
uc-src/router/guards/auth-check.ts | 24 +
uc-src/router/guards/permission.ts | 22 +
uc-src/router/index.ts | 27 +
uc-src/router/routes.config.ts | 41 +
uc-src/setup/setupModules.ts | 145 +
uc-src/views/ResetPassword.vue | 108 +
vite.config.ts | 19 +
vite.uc.config.ts | 19 +
vitest.config.ts | 29 +
1102 files changed, 156292 insertions(+)
create mode 100644 .changeset/README.md
create mode 100644 .changeset/config.json
create mode 100644 .changeset/pre.json
create mode 100644 .editorconfig
create mode 100644 .env.development
create mode 100644 .env.production
create mode 100644 .eslintrc.cjs
create mode 100644 .gitignore
create mode 100644 .gitpod.yml
create mode 100644 .husky/pre-commit
create mode 100644 .npmignore
create mode 100644 .npmrc
create mode 100644 .prettierignore
create mode 100644 .vscode/extensions.json
create mode 100644 Makefile
create mode 100644 OWNERS
create mode 100644 README.md
create mode 100644 build.gradle
create mode 100644 console-src/App.vue
create mode 100644 console-src/composables/use-content-snapshot.ts
create mode 100644 console-src/composables/use-dashboard-stats.ts
create mode 100644 console-src/composables/use-entity-extension-points.ts
create mode 100644 console-src/composables/use-global-info.ts
create mode 100644 console-src/composables/use-operation-extension-points.ts
create mode 100644 console-src/composables/use-save-keybinding.ts
create mode 100644 console-src/composables/use-setting-form.ts
create mode 100644 console-src/composables/use-slugify.ts
create mode 100644 console-src/layouts/BasicLayout.vue
create mode 100644 console-src/layouts/BlankLayout.vue
create mode 100644 console-src/main.ts
create mode 100644 console-src/modules/contents/attachments/AttachmentList.vue
create mode 100644 console-src/modules/contents/attachments/components/AttachmentDetailModal.vue
create mode 100644 console-src/modules/contents/attachments/components/AttachmentError.vue
create mode 100644 console-src/modules/contents/attachments/components/AttachmentGroupBadge.vue
create mode 100644 console-src/modules/contents/attachments/components/AttachmentGroupEditingModal.vue
create mode 100644 console-src/modules/contents/attachments/components/AttachmentGroupList.vue
create mode 100644 console-src/modules/contents/attachments/components/AttachmentListItem.vue
create mode 100644 console-src/modules/contents/attachments/components/AttachmentLoading.vue
create mode 100644 console-src/modules/contents/attachments/components/AttachmentPermalinkList.vue
create mode 100644 console-src/modules/contents/attachments/components/AttachmentPoliciesModal.vue
create mode 100644 console-src/modules/contents/attachments/components/AttachmentPolicyBadge.vue
create mode 100644 console-src/modules/contents/attachments/components/AttachmentPolicyEditingModal.vue
create mode 100644 console-src/modules/contents/attachments/components/AttachmentSelectorModal.vue
create mode 100644 console-src/modules/contents/attachments/components/AttachmentUploadModal.vue
create mode 100644 console-src/modules/contents/attachments/components/selector-providers/CoreSelectorProvider.vue
create mode 100644 console-src/modules/contents/attachments/composables/use-attachment-group.ts
create mode 100644 console-src/modules/contents/attachments/composables/use-attachment-policy.ts
create mode 100644 console-src/modules/contents/attachments/composables/use-attachment.ts
create mode 100644 console-src/modules/contents/attachments/module.ts
create mode 100644 console-src/modules/contents/comments/CommentList.vue
create mode 100644 console-src/modules/contents/comments/components/CommentListItem.vue
create mode 100644 console-src/modules/contents/comments/components/ReplyCreationModal.vue
create mode 100644 console-src/modules/contents/comments/components/ReplyListItem.vue
create mode 100644 console-src/modules/contents/comments/module.ts
create mode 100644 console-src/modules/contents/comments/widgets/CommentStatsWidget.vue
create mode 100644 console-src/modules/contents/pages/DeletedSinglePageList.vue
create mode 100644 console-src/modules/contents/pages/SinglePageEditor.vue
create mode 100644 console-src/modules/contents/pages/SinglePageList.vue
create mode 100644 console-src/modules/contents/pages/SinglePageSnapshots.vue
create mode 100644 console-src/modules/contents/pages/components/SinglePageListItem.vue
create mode 100644 console-src/modules/contents/pages/components/SinglePageSettingModal.vue
create mode 100644 console-src/modules/contents/pages/components/SnapshotContent.vue
create mode 100644 console-src/modules/contents/pages/components/SnapshotListItem.vue
create mode 100644 console-src/modules/contents/pages/composables/use-page-update-mutate.ts
create mode 100644 console-src/modules/contents/pages/module.ts
create mode 100644 console-src/modules/contents/pages/widgets/SinglePageStatsWidget.vue
create mode 100644 console-src/modules/contents/posts/DeletedPostList.vue
create mode 100644 console-src/modules/contents/posts/PostEditor.vue
create mode 100644 console-src/modules/contents/posts/PostList.vue
create mode 100644 console-src/modules/contents/posts/PostSnapshots.vue
create mode 100644 console-src/modules/contents/posts/categories/CategoryList.vue
create mode 100644 console-src/modules/contents/posts/categories/components/CategoryEditingModal.vue
create mode 100644 console-src/modules/contents/posts/categories/components/CategoryListItem.vue
create mode 100644 console-src/modules/contents/posts/categories/components/__tests__/CategoryEditingModal.spec.ts
create mode 100644 console-src/modules/contents/posts/categories/composables/use-post-category.ts
create mode 100644 console-src/modules/contents/posts/categories/utils/index.ts
create mode 100644 console-src/modules/contents/posts/components/PostBatchSettingModal.vue
create mode 100644 console-src/modules/contents/posts/components/PostListItem.vue
create mode 100644 console-src/modules/contents/posts/components/PostSettingModal.vue
create mode 100644 console-src/modules/contents/posts/components/SnapshotContent.vue
create mode 100644 console-src/modules/contents/posts/components/SnapshotListItem.vue
create mode 100644 console-src/modules/contents/posts/components/__tests__/PostSettingModal.spec.ts
create mode 100644 console-src/modules/contents/posts/components/entity-fields/ContributorsField.vue
create mode 100644 console-src/modules/contents/posts/components/entity-fields/PublishStatusField.vue
create mode 100644 console-src/modules/contents/posts/components/entity-fields/PublishTimeField.vue
create mode 100644 console-src/modules/contents/posts/components/entity-fields/TitleField.vue
create mode 100644 console-src/modules/contents/posts/components/entity-fields/VisibleField.vue
create mode 100644 console-src/modules/contents/posts/composables/use-post-update-mutate.ts
create mode 100644 console-src/modules/contents/posts/module.ts
create mode 100644 console-src/modules/contents/posts/tags/TagList.vue
create mode 100644 console-src/modules/contents/posts/tags/components/PostTag.vue
create mode 100644 console-src/modules/contents/posts/tags/components/TagEditingModal.vue
create mode 100644 console-src/modules/contents/posts/tags/components/TagListItem.vue
create mode 100644 console-src/modules/contents/posts/tags/composables/use-post-tag.ts
create mode 100644 console-src/modules/contents/posts/widgets/PostStatsWidget.vue
create mode 100644 console-src/modules/contents/posts/widgets/RecentPublishedWidget.vue
create mode 100644 console-src/modules/dashboard/Dashboard.vue
create mode 100644 console-src/modules/dashboard/module.ts
create mode 100644 console-src/modules/dashboard/widgets/QuickLinkWidget.vue
create mode 100644 console-src/modules/dashboard/widgets/ViewsStatsWidget.vue
create mode 100644 console-src/modules/index.ts
create mode 100644 console-src/modules/interface/menus/Menus.vue
create mode 100644 console-src/modules/interface/menus/components/MenuEditingModal.vue
create mode 100644 console-src/modules/interface/menus/components/MenuItemEditingModal.vue
create mode 100644 console-src/modules/interface/menus/components/MenuItemListItem.vue
create mode 100644 console-src/modules/interface/menus/components/MenuList.vue
create mode 100644 console-src/modules/interface/menus/module.ts
create mode 100644 console-src/modules/interface/menus/utils/__tests__/__snapshots__/index.spec.ts.snap
create mode 100644 console-src/modules/interface/menus/utils/__tests__/index.spec.ts
create mode 100644 console-src/modules/interface/menus/utils/index.ts
create mode 100644 console-src/modules/interface/themes/ThemeDetail.vue
create mode 100644 console-src/modules/interface/themes/ThemeSetting.vue
create mode 100644 console-src/modules/interface/themes/components/ThemeListItem.vue
create mode 100644 console-src/modules/interface/themes/components/ThemeListModal.vue
create mode 100644 console-src/modules/interface/themes/components/list-tabs/InstalledThemes.vue
create mode 100644 console-src/modules/interface/themes/components/list-tabs/LocalUpload.vue
create mode 100644 console-src/modules/interface/themes/components/list-tabs/NotInstalledThemes.vue
create mode 100644 console-src/modules/interface/themes/components/list-tabs/RemoteDownload.vue
create mode 100644 console-src/modules/interface/themes/components/operation/MoreOperationItem.vue
create mode 100644 console-src/modules/interface/themes/components/operation/UninstallOperationItem.vue
create mode 100644 console-src/modules/interface/themes/components/preview/ThemePreviewListItem.vue
create mode 100644 console-src/modules/interface/themes/components/preview/ThemePreviewModal.vue
create mode 100644 console-src/modules/interface/themes/composables/use-theme.ts
create mode 100644 console-src/modules/interface/themes/constants/index.ts
create mode 100644 console-src/modules/interface/themes/layouts/ThemeLayout.vue
create mode 100644 console-src/modules/interface/themes/module.ts
create mode 100644 console-src/modules/interface/themes/types/index.ts
create mode 100644 console-src/modules/system/auth-providers/AuthProviderDetail.vue
create mode 100644 console-src/modules/system/auth-providers/AuthProviders.vue
create mode 100644 console-src/modules/system/auth-providers/components/AuthProviderListItem.vue
create mode 100644 console-src/modules/system/auth-providers/module.ts
create mode 100644 console-src/modules/system/backup/Backups.vue
create mode 100644 console-src/modules/system/backup/components/BackupListItem.vue
create mode 100644 console-src/modules/system/backup/composables/use-backup.ts
create mode 100644 console-src/modules/system/backup/module.ts
create mode 100644 console-src/modules/system/backup/tabs/List.vue
create mode 100644 console-src/modules/system/backup/tabs/Restore.vue
create mode 100644 console-src/modules/system/overview/Overview.vue
create mode 100644 console-src/modules/system/overview/module.ts
create mode 100644 console-src/modules/system/plugins/PluginDetail.vue
create mode 100644 console-src/modules/system/plugins/PluginExtensionPointSettings.vue
create mode 100644 console-src/modules/system/plugins/PluginList.vue
create mode 100644 console-src/modules/system/plugins/components/PluginConditionsModal.vue
create mode 100644 console-src/modules/system/plugins/components/PluginDetailModal.vue
create mode 100644 console-src/modules/system/plugins/components/PluginInstallationModal.vue
create mode 100644 console-src/modules/system/plugins/components/PluginListItem.vue
create mode 100644 console-src/modules/system/plugins/components/entity-fields/AuthorField.vue
create mode 100644 console-src/modules/system/plugins/components/entity-fields/LogoField.vue
create mode 100644 console-src/modules/system/plugins/components/entity-fields/ReloadField.vue
create mode 100644 console-src/modules/system/plugins/components/entity-fields/SwitchField.vue
create mode 100644 console-src/modules/system/plugins/components/extension-points/ExtensionDefinitionListItem.vue
create mode 100644 console-src/modules/system/plugins/components/extension-points/ExtensionDefinitionMultiInstanceView.vue
create mode 100644 console-src/modules/system/plugins/components/extension-points/ExtensionDefinitionSingletonView.vue
create mode 100644 console-src/modules/system/plugins/components/installation-tabs/LocalUpload.vue
create mode 100644 console-src/modules/system/plugins/components/installation-tabs/RemoteDownload.vue
create mode 100644 console-src/modules/system/plugins/components/tabs/Detail.vue
create mode 100644 console-src/modules/system/plugins/components/tabs/Setting.vue
create mode 100644 console-src/modules/system/plugins/composables/use-extension-definition-fetch.ts
create mode 100644 console-src/modules/system/plugins/composables/use-plugin.ts
create mode 100644 console-src/modules/system/plugins/constants/index.ts
create mode 100644 console-src/modules/system/plugins/module.ts
create mode 100644 console-src/modules/system/plugins/types/index.ts
create mode 100644 console-src/modules/system/roles/RoleDetail.vue
create mode 100644 console-src/modules/system/roles/RoleList.vue
create mode 100644 console-src/modules/system/roles/components/RoleEditingModal.vue
create mode 100644 console-src/modules/system/roles/module.ts
create mode 100644 console-src/modules/system/settings/SystemSettings.vue
create mode 100644 console-src/modules/system/settings/module.ts
create mode 100644 console-src/modules/system/settings/tabs/NotificationSetting.vue
create mode 100644 console-src/modules/system/settings/tabs/Notifications.vue
create mode 100644 console-src/modules/system/settings/tabs/Setting.vue
create mode 100644 console-src/modules/system/tools/Tools.vue
create mode 100644 console-src/modules/system/tools/module.ts
create mode 100644 console-src/modules/system/users/UserDetail.vue
create mode 100644 console-src/modules/system/users/UserList.vue
create mode 100644 console-src/modules/system/users/components/GrantPermissionModal.vue
create mode 100644 console-src/modules/system/users/components/UserCreationModal.vue
create mode 100644 console-src/modules/system/users/components/UserEditingModal.vue
create mode 100644 console-src/modules/system/users/components/UserPasswordChangeModal.vue
create mode 100644 console-src/modules/system/users/composables/use-user.ts
create mode 100644 console-src/modules/system/users/module.ts
create mode 100644 console-src/modules/system/users/tabs/Detail.vue
create mode 100644 console-src/modules/system/users/widgets/NotificationWidget.vue
create mode 100644 console-src/modules/system/users/widgets/RecentLoginWidget.vue
create mode 100644 console-src/modules/system/users/widgets/UserStatsWidget.vue
create mode 100644 console-src/router/constant.ts
create mode 100644 console-src/router/guards/auth-check.ts
create mode 100644 console-src/router/guards/check-states.ts
create mode 100644 console-src/router/guards/permission.ts
create mode 100644 console-src/router/index.ts
create mode 100644 console-src/router/routes.config.ts
create mode 100644 console-src/setup/setupModules.ts
create mode 100644 console-src/stores/system-configmap.ts
create mode 100644 console-src/stores/theme.ts
create mode 100644 console-src/views/system/Binding.vue
create mode 100644 console-src/views/system/Login.vue
create mode 100644 console-src/views/system/Redirect.vue
create mode 100644 console-src/views/system/ResetPassword.vue
create mode 100644 console-src/views/system/Setup.vue
create mode 100644 console-src/views/system/SetupInitialData.vue
create mode 100644 console-src/views/system/setup-data/category.json
create mode 100644 console-src/views/system/setup-data/menu-items.json
create mode 100644 console-src/views/system/setup-data/menu.json
create mode 100644 console-src/views/system/setup-data/post.json
create mode 100644 console-src/views/system/setup-data/singlePage.json
create mode 100644 console-src/views/system/setup-data/tag.json
create mode 100644 cypress.json
create mode 100644 cypress/fixtures/example.json
create mode 100644 cypress/integration/example.spec.ts
create mode 100644 cypress/plugins/index.ts
create mode 100644 cypress/plugins/tsconfig.json
create mode 100644 cypress/support/commands.ts
create mode 100644 cypress/support/index.ts
create mode 100644 cypress/tsconfig.json
create mode 100644 docs/components/README.md
create mode 100644 docs/custom-formkit-input/README.md
create mode 100644 docs/extension-points/backup.md
create mode 100644 docs/extension-points/comment-subject-ref.md
create mode 100644 docs/extension-points/default-editor–extension.md
create mode 100644 docs/extension-points/editor.md
create mode 100644 docs/extension-points/entity-listitem-field.md
create mode 100644 docs/extension-points/entity-listitem-operation.md
create mode 100644 docs/extension-points/plugin-installation-tabs.md
create mode 100644 docs/extension-points/plugin-self-tabs.md
create mode 100644 docs/extension-points/theme-list-tabs.md
create mode 100644 docs/project-structure/README.md
create mode 100644 docs/routes-generation/README.md
create mode 100644 env.d.ts
create mode 100644 index.html
create mode 100644 package.json
create mode 100644 packages/api-client/.eslintignore
create mode 100644 packages/api-client/.eslintrc.js
create mode 100644 packages/api-client/.gitignore
create mode 100644 packages/api-client/.openapi_config.yaml
create mode 100644 packages/api-client/.prettierrc.js
create mode 100644 packages/api-client/README.md
create mode 100644 packages/api-client/build.config.ts
create mode 100644 packages/api-client/entry/api-client.ts
create mode 100644 packages/api-client/entry/index.ts
create mode 100644 packages/api-client/openapitools.json
create mode 100644 packages/api-client/package.json
create mode 100644 packages/api-client/src/.gitignore
create mode 100644 packages/api-client/src/.npmignore
create mode 100644 packages/api-client/src/.openapi-generator-ignore
create mode 100644 packages/api-client/src/.openapi-generator/FILES
create mode 100644 packages/api-client/src/.openapi-generator/VERSION
create mode 100644 packages/api-client/src/api.ts
create mode 100644 packages/api-client/src/api/annotation-setting-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/api-notification-halo-run-v1alpha1-subscription-api.ts
create mode 100644 packages/api-client/src/api/attachment-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/attachment-v1alpha1-console-api.ts
create mode 100644 packages/api-client/src/api/attachment-v1alpha1-uc-api.ts
create mode 100644 packages/api-client/src/api/auth-provider-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/auth-provider-v1alpha1-console-api.ts
create mode 100644 packages/api-client/src/api/backup-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/category-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/category-v1alpha1-public-api.ts
create mode 100644 packages/api-client/src/api/comment-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/comment-v1alpha1-console-api.ts
create mode 100644 packages/api-client/src/api/comment-v1alpha1-public-api.ts
create mode 100644 packages/api-client/src/api/config-map-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/counter-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/device-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/device-v1alpha1-uc-api.ts
create mode 100644 packages/api-client/src/api/extension-definition-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/extension-point-definition-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/group-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/index-v1alpha1-public-api.ts
create mode 100644 packages/api-client/src/api/indices-v1alpha1-console-api.ts
create mode 100644 packages/api-client/src/api/login-api.ts
create mode 100644 packages/api-client/src/api/menu-item-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/menu-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/menu-v1alpha1-public-api.ts
create mode 100644 packages/api-client/src/api/metrics-v1alpha1-public-api.ts
create mode 100644 packages/api-client/src/api/migration-v1alpha1-console-api.ts
create mode 100644 packages/api-client/src/api/notification-template-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/notification-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/notification-v1alpha1-uc-api.ts
create mode 100644 packages/api-client/src/api/notifier-descriptor-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/notifier-v1alpha1-console-api.ts
create mode 100644 packages/api-client/src/api/notifier-v1alpha1-uc-api.ts
create mode 100644 packages/api-client/src/api/personal-access-token-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/personal-access-token-v1alpha1-uc-api.ts
create mode 100644 packages/api-client/src/api/plugin-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/plugin-v1alpha1-console-api.ts
create mode 100644 packages/api-client/src/api/plugin-v1alpha1-public-api.ts
create mode 100644 packages/api-client/src/api/policy-template-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/policy-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/post-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/post-v1alpha1-console-api.ts
create mode 100644 packages/api-client/src/api/post-v1alpha1-public-api.ts
create mode 100644 packages/api-client/src/api/post-v1alpha1-uc-api.ts
create mode 100644 packages/api-client/src/api/reason-type-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/reason-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/remember-me-token-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/reply-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/reply-v1alpha1-console-api.ts
create mode 100644 packages/api-client/src/api/reverse-proxy-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/role-binding-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/role-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/search-engine-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/secret-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/setting-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/single-page-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/single-page-v1alpha1-console-api.ts
create mode 100644 packages/api-client/src/api/single-page-v1alpha1-public-api.ts
create mode 100644 packages/api-client/src/api/snapshot-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/snapshot-v1alpha1-uc-api.ts
create mode 100644 packages/api-client/src/api/subscription-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/system-v1alpha1-console-api.ts
create mode 100644 packages/api-client/src/api/system-v1alpha1-public-api.ts
create mode 100644 packages/api-client/src/api/tag-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/tag-v1alpha1-console-api.ts
create mode 100644 packages/api-client/src/api/tag-v1alpha1-public-api.ts
create mode 100644 packages/api-client/src/api/theme-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/theme-v1alpha1-console-api.ts
create mode 100644 packages/api-client/src/api/two-factor-auth-v1alpha1-uc-api.ts
create mode 100644 packages/api-client/src/api/user-connection-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/user-v1alpha1-api.ts
create mode 100644 packages/api-client/src/api/user-v1alpha1-console-api.ts
create mode 100644 packages/api-client/src/api/user-v1alpha1-public-api.ts
create mode 100644 packages/api-client/src/base.ts
create mode 100644 packages/api-client/src/common.ts
create mode 100644 packages/api-client/src/configuration.ts
create mode 100644 packages/api-client/src/git_push.sh
create mode 100644 packages/api-client/src/index.ts
create mode 100644 packages/api-client/src/models/add-operation.ts
create mode 100644 packages/api-client/src/models/annotation-setting-list.ts
create mode 100644 packages/api-client/src/models/annotation-setting-spec.ts
create mode 100644 packages/api-client/src/models/annotation-setting.ts
create mode 100644 packages/api-client/src/models/attachment-list.ts
create mode 100644 packages/api-client/src/models/attachment-spec.ts
create mode 100644 packages/api-client/src/models/attachment-status.ts
create mode 100644 packages/api-client/src/models/attachment.ts
create mode 100644 packages/api-client/src/models/auth-provider-list.ts
create mode 100644 packages/api-client/src/models/auth-provider-spec.ts
create mode 100644 packages/api-client/src/models/auth-provider.ts
create mode 100644 packages/api-client/src/models/author.ts
create mode 100644 packages/api-client/src/models/backup-file.ts
create mode 100644 packages/api-client/src/models/backup-list.ts
create mode 100644 packages/api-client/src/models/backup-spec.ts
create mode 100644 packages/api-client/src/models/backup-status.ts
create mode 100644 packages/api-client/src/models/backup.ts
create mode 100644 packages/api-client/src/models/category-list.ts
create mode 100644 packages/api-client/src/models/category-spec.ts
create mode 100644 packages/api-client/src/models/category-status.ts
create mode 100644 packages/api-client/src/models/category-vo-list.ts
create mode 100644 packages/api-client/src/models/category-vo.ts
create mode 100644 packages/api-client/src/models/category.ts
create mode 100644 packages/api-client/src/models/change-own-password-request.ts
create mode 100644 packages/api-client/src/models/change-password-request.ts
create mode 100644 packages/api-client/src/models/comment-email-owner.ts
create mode 100644 packages/api-client/src/models/comment-list.ts
create mode 100644 packages/api-client/src/models/comment-owner.ts
create mode 100644 packages/api-client/src/models/comment-request.ts
create mode 100644 packages/api-client/src/models/comment-spec.ts
create mode 100644 packages/api-client/src/models/comment-stats-vo.ts
create mode 100644 packages/api-client/src/models/comment-stats.ts
create mode 100644 packages/api-client/src/models/comment-status.ts
create mode 100644 packages/api-client/src/models/comment-vo-list.ts
create mode 100644 packages/api-client/src/models/comment-vo.ts
create mode 100644 packages/api-client/src/models/comment-with-reply-vo-list.ts
create mode 100644 packages/api-client/src/models/comment-with-reply-vo.ts
create mode 100644 packages/api-client/src/models/comment.ts
create mode 100644 packages/api-client/src/models/condition.ts
create mode 100644 packages/api-client/src/models/config-map-list.ts
create mode 100644 packages/api-client/src/models/config-map-ref.ts
create mode 100644 packages/api-client/src/models/config-map.ts
create mode 100644 packages/api-client/src/models/content-update-param.ts
create mode 100644 packages/api-client/src/models/content-vo.ts
create mode 100644 packages/api-client/src/models/content-wrapper.ts
create mode 100644 packages/api-client/src/models/content.ts
create mode 100644 packages/api-client/src/models/contributor-vo.ts
create mode 100644 packages/api-client/src/models/contributor.ts
create mode 100644 packages/api-client/src/models/copy-operation.ts
create mode 100644 packages/api-client/src/models/counter-list.ts
create mode 100644 packages/api-client/src/models/counter-request.ts
create mode 100644 packages/api-client/src/models/counter.ts
create mode 100644 packages/api-client/src/models/create-user-request.ts
create mode 100644 packages/api-client/src/models/custom-templates.ts
create mode 100644 packages/api-client/src/models/dashboard-stats.ts
create mode 100644 packages/api-client/src/models/detailed-user.ts
create mode 100644 packages/api-client/src/models/device-list.ts
create mode 100644 packages/api-client/src/models/device-spec.ts
create mode 100644 packages/api-client/src/models/device-status.ts
create mode 100644 packages/api-client/src/models/device.ts
create mode 100644 packages/api-client/src/models/email-config-validation-request.ts
create mode 100644 packages/api-client/src/models/email-verify-request.ts
create mode 100644 packages/api-client/src/models/excerpt.ts
create mode 100644 packages/api-client/src/models/extension-definition-list.ts
create mode 100644 packages/api-client/src/models/extension-definition.ts
create mode 100644 packages/api-client/src/models/extension-point-definition-list.ts
create mode 100644 packages/api-client/src/models/extension-point-definition.ts
create mode 100644 packages/api-client/src/models/extension-point-spec.ts
create mode 100644 packages/api-client/src/models/extension-spec.ts
create mode 100644 packages/api-client/src/models/extension.ts
create mode 100644 packages/api-client/src/models/file-reverse-proxy-provider.ts
create mode 100644 packages/api-client/src/models/grant-request.ts
create mode 100644 packages/api-client/src/models/group-kind.ts
create mode 100644 packages/api-client/src/models/group-list.ts
create mode 100644 packages/api-client/src/models/group-spec.ts
create mode 100644 packages/api-client/src/models/group-status.ts
create mode 100644 packages/api-client/src/models/group.ts
create mode 100644 packages/api-client/src/models/halo-document.ts
create mode 100644 packages/api-client/src/models/index.ts
create mode 100644 packages/api-client/src/models/install-from-uri-request.ts
create mode 100644 packages/api-client/src/models/interest-reason-subject.ts
create mode 100644 packages/api-client/src/models/interest-reason.ts
create mode 100644 packages/api-client/src/models/json-patch-inner.ts
create mode 100644 packages/api-client/src/models/license.ts
create mode 100644 packages/api-client/src/models/list-result-reply-vo.ts
create mode 100644 packages/api-client/src/models/listed-auth-provider.ts
create mode 100644 packages/api-client/src/models/listed-comment-list.ts
create mode 100644 packages/api-client/src/models/listed-comment.ts
create mode 100644 packages/api-client/src/models/listed-post-list.ts
create mode 100644 packages/api-client/src/models/listed-post-vo-list.ts
create mode 100644 packages/api-client/src/models/listed-post-vo.ts
create mode 100644 packages/api-client/src/models/listed-post.ts
create mode 100644 packages/api-client/src/models/listed-reply-list.ts
create mode 100644 packages/api-client/src/models/listed-reply.ts
create mode 100644 packages/api-client/src/models/listed-single-page-list.ts
create mode 100644 packages/api-client/src/models/listed-single-page-vo-list.ts
create mode 100644 packages/api-client/src/models/listed-single-page-vo.ts
create mode 100644 packages/api-client/src/models/listed-single-page.ts
create mode 100644 packages/api-client/src/models/listed-snapshot-dto.ts
create mode 100644 packages/api-client/src/models/listed-snapshot-spec.ts
create mode 100644 packages/api-client/src/models/listed-user.ts
create mode 100644 packages/api-client/src/models/login-history.ts
create mode 100644 packages/api-client/src/models/mark-specified-request.ts
create mode 100644 packages/api-client/src/models/menu-item-list.ts
create mode 100644 packages/api-client/src/models/menu-item-spec.ts
create mode 100644 packages/api-client/src/models/menu-item-status.ts
create mode 100644 packages/api-client/src/models/menu-item-vo.ts
create mode 100644 packages/api-client/src/models/menu-item.ts
create mode 100644 packages/api-client/src/models/menu-list.ts
create mode 100644 packages/api-client/src/models/menu-spec.ts
create mode 100644 packages/api-client/src/models/menu-vo.ts
create mode 100644 packages/api-client/src/models/menu.ts
create mode 100644 packages/api-client/src/models/metadata.ts
create mode 100644 packages/api-client/src/models/move-operation.ts
create mode 100644 packages/api-client/src/models/navigation-post-vo.ts
create mode 100644 packages/api-client/src/models/notification-list.ts
create mode 100644 packages/api-client/src/models/notification-spec.ts
create mode 100644 packages/api-client/src/models/notification-template-list.ts
create mode 100644 packages/api-client/src/models/notification-template-spec.ts
create mode 100644 packages/api-client/src/models/notification-template.ts
create mode 100644 packages/api-client/src/models/notification.ts
create mode 100644 packages/api-client/src/models/notifier-descriptor-list.ts
create mode 100644 packages/api-client/src/models/notifier-descriptor-spec.ts
create mode 100644 packages/api-client/src/models/notifier-descriptor.ts
create mode 100644 packages/api-client/src/models/notifier-info.ts
create mode 100644 packages/api-client/src/models/notifier-setting-ref.ts
create mode 100644 packages/api-client/src/models/owner-info.ts
create mode 100644 packages/api-client/src/models/password-request.ts
create mode 100644 packages/api-client/src/models/password-reset-email-request.ts
create mode 100644 packages/api-client/src/models/pat-spec.ts
create mode 100644 packages/api-client/src/models/personal-access-token-list.ts
create mode 100644 packages/api-client/src/models/personal-access-token.ts
create mode 100644 packages/api-client/src/models/plugin-author.ts
create mode 100644 packages/api-client/src/models/plugin-list.ts
create mode 100644 packages/api-client/src/models/plugin-running-state-request.ts
create mode 100644 packages/api-client/src/models/plugin-spec.ts
create mode 100644 packages/api-client/src/models/plugin-status.ts
create mode 100644 packages/api-client/src/models/plugin.ts
create mode 100644 packages/api-client/src/models/policy-list.ts
create mode 100644 packages/api-client/src/models/policy-rule.ts
create mode 100644 packages/api-client/src/models/policy-spec.ts
create mode 100644 packages/api-client/src/models/policy-template-list.ts
create mode 100644 packages/api-client/src/models/policy-template-spec.ts
create mode 100644 packages/api-client/src/models/policy-template.ts
create mode 100644 packages/api-client/src/models/policy.ts
create mode 100644 packages/api-client/src/models/post-list.ts
create mode 100644 packages/api-client/src/models/post-request.ts
create mode 100644 packages/api-client/src/models/post-spec.ts
create mode 100644 packages/api-client/src/models/post-status.ts
create mode 100644 packages/api-client/src/models/post-vo.ts
create mode 100644 packages/api-client/src/models/post.ts
create mode 100644 packages/api-client/src/models/public-key-response.ts
create mode 100644 packages/api-client/src/models/reason-attributes.ts
create mode 100644 packages/api-client/src/models/reason-list.ts
create mode 100644 packages/api-client/src/models/reason-property.ts
create mode 100644 packages/api-client/src/models/reason-selector.ts
create mode 100644 packages/api-client/src/models/reason-spec.ts
create mode 100644 packages/api-client/src/models/reason-subject.ts
create mode 100644 packages/api-client/src/models/reason-type-info.ts
create mode 100644 packages/api-client/src/models/reason-type-list.ts
create mode 100644 packages/api-client/src/models/reason-type-notifier-collection-request.ts
create mode 100644 packages/api-client/src/models/reason-type-notifier-matrix.ts
create mode 100644 packages/api-client/src/models/reason-type-notifier-request.ts
create mode 100644 packages/api-client/src/models/reason-type-spec.ts
create mode 100644 packages/api-client/src/models/reason-type.ts
create mode 100644 packages/api-client/src/models/reason.ts
create mode 100644 packages/api-client/src/models/ref.ts
create mode 100644 packages/api-client/src/models/register-verify-email-request.ts
create mode 100644 packages/api-client/src/models/remember-me-token-list.ts
create mode 100644 packages/api-client/src/models/remember-me-token-spec.ts
create mode 100644 packages/api-client/src/models/remember-me-token.ts
create mode 100644 packages/api-client/src/models/remove-operation.ts
create mode 100644 packages/api-client/src/models/replace-operation.ts
create mode 100644 packages/api-client/src/models/reply-list.ts
create mode 100644 packages/api-client/src/models/reply-request.ts
create mode 100644 packages/api-client/src/models/reply-spec.ts
create mode 100644 packages/api-client/src/models/reply-status.ts
create mode 100644 packages/api-client/src/models/reply-vo-list.ts
create mode 100644 packages/api-client/src/models/reply-vo.ts
create mode 100644 packages/api-client/src/models/reply.ts
create mode 100644 packages/api-client/src/models/reset-password-request.ts
create mode 100644 packages/api-client/src/models/reverse-proxy-list.ts
create mode 100644 packages/api-client/src/models/reverse-proxy-rule.ts
create mode 100644 packages/api-client/src/models/reverse-proxy.ts
create mode 100644 packages/api-client/src/models/revert-snapshot-for-post-param.ts
create mode 100644 packages/api-client/src/models/revert-snapshot-for-single-param.ts
create mode 100644 packages/api-client/src/models/role-binding-list.ts
create mode 100644 packages/api-client/src/models/role-binding.ts
create mode 100644 packages/api-client/src/models/role-list.ts
create mode 100644 packages/api-client/src/models/role-ref.ts
create mode 100644 packages/api-client/src/models/role.ts
create mode 100644 packages/api-client/src/models/search-engine-list.ts
create mode 100644 packages/api-client/src/models/search-engine-spec.ts
create mode 100644 packages/api-client/src/models/search-engine.ts
create mode 100644 packages/api-client/src/models/search-option.ts
create mode 100644 packages/api-client/src/models/search-result.ts
create mode 100644 packages/api-client/src/models/secret-list.ts
create mode 100644 packages/api-client/src/models/secret.ts
create mode 100644 packages/api-client/src/models/setting-form.ts
create mode 100644 packages/api-client/src/models/setting-list.ts
create mode 100644 packages/api-client/src/models/setting-ref.ts
create mode 100644 packages/api-client/src/models/setting-spec.ts
create mode 100644 packages/api-client/src/models/setting.ts
create mode 100644 packages/api-client/src/models/sign-up-request.ts
create mode 100644 packages/api-client/src/models/single-page-list.ts
create mode 100644 packages/api-client/src/models/single-page-request.ts
create mode 100644 packages/api-client/src/models/single-page-spec.ts
create mode 100644 packages/api-client/src/models/single-page-status.ts
create mode 100644 packages/api-client/src/models/single-page-vo.ts
create mode 100644 packages/api-client/src/models/single-page.ts
create mode 100644 packages/api-client/src/models/site-stats-vo.ts
create mode 100644 packages/api-client/src/models/snap-shot-spec.ts
create mode 100644 packages/api-client/src/models/snapshot-list.ts
create mode 100644 packages/api-client/src/models/snapshot.ts
create mode 100644 packages/api-client/src/models/stats-vo.ts
create mode 100644 packages/api-client/src/models/stats.ts
create mode 100644 packages/api-client/src/models/subject.ts
create mode 100644 packages/api-client/src/models/subscription-list.ts
create mode 100644 packages/api-client/src/models/subscription-spec.ts
create mode 100644 packages/api-client/src/models/subscription-subscriber.ts
create mode 100644 packages/api-client/src/models/subscription.ts
create mode 100644 packages/api-client/src/models/system-initialization-request.ts
create mode 100644 packages/api-client/src/models/tag-list.ts
create mode 100644 packages/api-client/src/models/tag-spec.ts
create mode 100644 packages/api-client/src/models/tag-status.ts
create mode 100644 packages/api-client/src/models/tag-vo-list.ts
create mode 100644 packages/api-client/src/models/tag-vo.ts
create mode 100644 packages/api-client/src/models/tag.ts
create mode 100644 packages/api-client/src/models/template-content.ts
create mode 100644 packages/api-client/src/models/template-descriptor.ts
create mode 100644 packages/api-client/src/models/test-operation.ts
create mode 100644 packages/api-client/src/models/theme-list.ts
create mode 100644 packages/api-client/src/models/theme-spec.ts
create mode 100644 packages/api-client/src/models/theme-status.ts
create mode 100644 packages/api-client/src/models/theme.ts
create mode 100644 packages/api-client/src/models/totp-auth-link-response.ts
create mode 100644 packages/api-client/src/models/totp-request.ts
create mode 100644 packages/api-client/src/models/two-factor-auth-settings.ts
create mode 100644 packages/api-client/src/models/upgrade-from-uri-request.ts
create mode 100644 packages/api-client/src/models/user-connection-list.ts
create mode 100644 packages/api-client/src/models/user-connection-spec.ts
create mode 100644 packages/api-client/src/models/user-connection.ts
create mode 100644 packages/api-client/src/models/user-device.ts
create mode 100644 packages/api-client/src/models/user-endpoint-listed-user-list.ts
create mode 100644 packages/api-client/src/models/user-list.ts
create mode 100644 packages/api-client/src/models/user-permission.ts
create mode 100644 packages/api-client/src/models/user-spec.ts
create mode 100644 packages/api-client/src/models/user-status.ts
create mode 100644 packages/api-client/src/models/user.ts
create mode 100644 packages/api-client/src/models/verify-code-request.ts
create mode 100644 packages/api-client/src/models/vote-request.ts
create mode 100644 packages/api-client/tsconfig.json
create mode 100644 packages/api-client/vite.config.ts
create mode 100644 packages/components/.eslintignore
create mode 100644 packages/components/.eslintrc.cjs
create mode 100644 packages/components/.storybook/main.ts
create mode 100644 packages/components/.storybook/preview.ts
create mode 100644 packages/components/env.d.ts
create mode 100644 packages/components/package.json
create mode 100644 packages/components/postcss.config.js
create mode 100644 packages/components/prettier.config.js
create mode 100644 packages/components/src/components.ts
create mode 100644 packages/components/src/components/alert/Alert.stories.ts
create mode 100644 packages/components/src/components/alert/Alert.vue
create mode 100644 packages/components/src/components/alert/__tests__/Alert.spec.ts
create mode 100644 packages/components/src/components/alert/index.ts
create mode 100644 packages/components/src/components/alert/interface.ts
create mode 100644 packages/components/src/components/avatar/Avatar.stories.ts
create mode 100644 packages/components/src/components/avatar/Avatar.vue
create mode 100644 packages/components/src/components/avatar/AvatarGroup.stories.ts
create mode 100644 packages/components/src/components/avatar/AvatarGroup.vue
create mode 100644 packages/components/src/components/avatar/index.ts
create mode 100644 packages/components/src/components/avatar/interface.ts
create mode 100644 packages/components/src/components/button/Button.stories.ts
create mode 100644 packages/components/src/components/button/Button.vue
create mode 100644 packages/components/src/components/button/__tests__/Button.spec.ts
create mode 100644 packages/components/src/components/button/__tests__/__snapshots__/Button.spec.ts.snap
create mode 100644 packages/components/src/components/button/index.ts
create mode 100644 packages/components/src/components/button/interface.ts
create mode 100644 packages/components/src/components/card/Card.stories.ts
create mode 100644 packages/components/src/components/card/Card.vue
create mode 100644 packages/components/src/components/card/__tests__/Card.spec.ts
create mode 100644 packages/components/src/components/card/index.ts
create mode 100644 packages/components/src/components/description/Description.vue
create mode 100644 packages/components/src/components/description/DescriptionItem.vue
create mode 100644 packages/components/src/components/description/index.ts
create mode 100644 packages/components/src/components/dialog/Dialog.stories.ts
create mode 100644 packages/components/src/components/dialog/Dialog.vue
create mode 100644 packages/components/src/components/dialog/__tests__/Dialog.spec.ts
create mode 100644 packages/components/src/components/dialog/dialog-manager.ts
create mode 100644 packages/components/src/components/dialog/index.ts
create mode 100644 packages/components/src/components/dialog/interface.ts
create mode 100644 packages/components/src/components/dropdown/Draopdown.stories.ts
create mode 100644 packages/components/src/components/dropdown/Dropdown.vue
create mode 100644 packages/components/src/components/dropdown/DropdownDivider.vue
create mode 100644 packages/components/src/components/dropdown/DropdownItem.vue
create mode 100644 packages/components/src/components/dropdown/index.ts
create mode 100644 packages/components/src/components/dropdown/symbols.ts
create mode 100644 packages/components/src/components/empty/Empty.stories.ts
create mode 100644 packages/components/src/components/empty/Empty.svg
create mode 100644 packages/components/src/components/empty/Empty.vue
create mode 100644 packages/components/src/components/empty/__tests__/Empty.spec.ts
create mode 100644 packages/components/src/components/empty/__tests__/__snapshots__/Empty.spec.ts.snap
create mode 100644 packages/components/src/components/empty/index.ts
create mode 100644 packages/components/src/components/entity/Entity.vue
create mode 100644 packages/components/src/components/entity/EntityField.vue
create mode 100644 packages/components/src/components/entity/__tests__/Entity.spec.ts
create mode 100644 packages/components/src/components/entity/__tests__/EntityField.spec.ts
create mode 100644 packages/components/src/components/entity/index.ts
create mode 100644 packages/components/src/components/header/PageHeader.vue
create mode 100644 packages/components/src/components/header/index.ts
create mode 100644 packages/components/src/components/loading/Loading.stories.ts
create mode 100644 packages/components/src/components/loading/Loading.vue
create mode 100644 packages/components/src/components/loading/index.ts
create mode 100644 packages/components/src/components/menu/Menu.stories.ts
create mode 100644 packages/components/src/components/menu/Menu.vue
create mode 100644 packages/components/src/components/menu/MenuItem.vue
create mode 100644 packages/components/src/components/menu/MenuLabel.vue
create mode 100644 packages/components/src/components/menu/__tests__/Menu.spec.tsx
create mode 100644 packages/components/src/components/menu/__tests__/MenuLabel.spec.ts
create mode 100644 packages/components/src/components/menu/__tests__/__snapshots__/Menu.spec.tsx.snap
create mode 100644 packages/components/src/components/menu/__tests__/__snapshots__/MenuLabel.spec.ts.snap
create mode 100644 packages/components/src/components/menu/index.ts
create mode 100644 packages/components/src/components/modal/Modal.stories.ts
create mode 100644 packages/components/src/components/modal/Modal.vue
create mode 100644 packages/components/src/components/modal/__tests__/Modal.spec.ts
create mode 100644 packages/components/src/components/modal/index.ts
create mode 100644 packages/components/src/components/pagination/Pagination.stories.ts
create mode 100644 packages/components/src/components/pagination/Pagination.vue
create mode 100644 packages/components/src/components/pagination/__tests__/Pagination.spec.ts
create mode 100644 packages/components/src/components/pagination/index.ts
create mode 100644 packages/components/src/components/space/Space.stories.ts
create mode 100644 packages/components/src/components/space/Space.vue
create mode 100644 packages/components/src/components/space/__tests__/Space.spec.ts
create mode 100644 packages/components/src/components/space/index.ts
create mode 100644 packages/components/src/components/space/interface.ts
create mode 100644 packages/components/src/components/status/StatusDot.stories.ts
create mode 100644 packages/components/src/components/status/StatusDot.vue
create mode 100644 packages/components/src/components/status/__tests__/StatusDot.spec.ts
create mode 100644 packages/components/src/components/status/__tests__/__snapshots__/StatusDot.spec.ts.snap
create mode 100644 packages/components/src/components/status/index.ts
create mode 100644 packages/components/src/components/status/interface.ts
create mode 100644 packages/components/src/components/switch/Switch.stories.ts
create mode 100644 packages/components/src/components/switch/Switch.vue
create mode 100644 packages/components/src/components/switch/__tests__/Switch.spec.ts
create mode 100644 packages/components/src/components/switch/index.ts
create mode 100644 packages/components/src/components/tabs/TabItem.vue
create mode 100644 packages/components/src/components/tabs/Tabbar.stories.ts
create mode 100644 packages/components/src/components/tabs/Tabbar.vue
create mode 100644 packages/components/src/components/tabs/Tabs.stories.ts
create mode 100644 packages/components/src/components/tabs/Tabs.vue
create mode 100644 packages/components/src/components/tabs/__tests__/TabItem.spec.ts
create mode 100644 packages/components/src/components/tabs/__tests__/Tabbar.spec.ts
create mode 100644 packages/components/src/components/tabs/__tests__/Tabs.spec.ts
create mode 100644 packages/components/src/components/tabs/index.ts
create mode 100644 packages/components/src/components/tabs/interface.ts
create mode 100644 packages/components/src/components/tag/Tag.stories.ts
create mode 100644 packages/components/src/components/tag/Tag.story.vue
create mode 100644 packages/components/src/components/tag/Tag.vue
create mode 100644 packages/components/src/components/tag/__tests__/Tag.spec.ts
create mode 100644 packages/components/src/components/tag/index.ts
create mode 100644 packages/components/src/components/tag/interface.ts
create mode 100644 packages/components/src/components/toast/Toast.story.vue
create mode 100644 packages/components/src/components/toast/Toast.vue
create mode 100644 packages/components/src/components/toast/index.ts
create mode 100644 packages/components/src/components/toast/interface.ts
create mode 100644 packages/components/src/components/toast/toast-manager.ts
create mode 100644 packages/components/src/components/tooltip/index.ts
create mode 100644 packages/components/src/components/tooltip/style.css
create mode 100644 packages/components/src/icons/icons.ts
create mode 100644 packages/components/src/index.ts
create mode 100644 packages/components/src/stories/Introduction.mdx
create mode 100644 packages/components/src/stories/assets/code-brackets.svg
create mode 100644 packages/components/src/stories/assets/colors.svg
create mode 100644 packages/components/src/stories/assets/comments.svg
create mode 100644 packages/components/src/stories/assets/direction.svg
create mode 100644 packages/components/src/stories/assets/flow.svg
create mode 100644 packages/components/src/stories/assets/plugin.svg
create mode 100644 packages/components/src/stories/assets/repo.svg
create mode 100644 packages/components/src/stories/assets/stackalt.svg
create mode 100644 packages/components/src/styles/tailwind.css
create mode 100644 packages/components/tailwind.config.js
create mode 100644 packages/components/tsconfig.app.json
create mode 100644 packages/components/tsconfig.json
create mode 100644 packages/components/tsconfig.node.json
create mode 100644 packages/components/tsconfig.vitest.json
create mode 100644 packages/components/vite.config.ts
create mode 100644 packages/editor/.eslintrc.cjs
create mode 100644 packages/editor/.gitignore
create mode 100644 packages/editor/.release-it.json
create mode 100644 packages/editor/docs/extension.md
create mode 100644 packages/editor/docs/extension.png
create mode 100644 packages/editor/env.d.ts
create mode 100644 packages/editor/index.html
create mode 100644 packages/editor/package.json
create mode 100644 packages/editor/postcss.config.js
create mode 100644 packages/editor/prettier.config.js
create mode 100644 packages/editor/src/components/Editor.vue
create mode 100644 packages/editor/src/components/EditorBubbleMenu.vue
create mode 100644 packages/editor/src/components/EditorHeader.vue
create mode 100644 packages/editor/src/components/block/BlockActionButton.vue
create mode 100644 packages/editor/src/components/block/BlockActionInput.vue
create mode 100644 packages/editor/src/components/block/BlockActionSeparator.vue
create mode 100644 packages/editor/src/components/block/BlockCard.vue
create mode 100644 packages/editor/src/components/block/index.ts
create mode 100644 packages/editor/src/components/bubble/BubbleItem.vue
create mode 100644 packages/editor/src/components/bubble/BubbleMenu.vue
create mode 100644 packages/editor/src/components/bubble/BubbleMenuPlugin.ts
create mode 100644 packages/editor/src/components/bubble/index.ts
create mode 100644 packages/editor/src/components/common/ColorPickerDropdown.vue
create mode 100644 packages/editor/src/components/icon/MdiDeleteForeverOutline.vue
create mode 100644 packages/editor/src/components/index.ts
create mode 100644 packages/editor/src/components/toolbar/ToolbarItem.vue
create mode 100644 packages/editor/src/components/toolbar/ToolbarSubItem.vue
create mode 100644 packages/editor/src/components/toolbar/index.ts
create mode 100644 packages/editor/src/components/toolbox/ToolboxItem.vue
create mode 100644 packages/editor/src/components/toolbox/index.ts
create mode 100644 packages/editor/src/dev/App.vue
create mode 100644 packages/editor/src/dev/main.ts
create mode 100644 packages/editor/src/extensions/audio/AudioView.vue
create mode 100644 packages/editor/src/extensions/audio/BubbleItemAudioLink.vue
create mode 100644 packages/editor/src/extensions/audio/index.ts
create mode 100644 packages/editor/src/extensions/blockquote/index.ts
create mode 100644 packages/editor/src/extensions/bold/index.ts
create mode 100644 packages/editor/src/extensions/bullet-list/index.ts
create mode 100644 packages/editor/src/extensions/clear-format/index.ts
create mode 100644 packages/editor/src/extensions/code-block/CodeBlockViewRenderer.vue
create mode 100644 packages/editor/src/extensions/code-block/code-block.ts
create mode 100644 packages/editor/src/extensions/code-block/index.ts
create mode 100644 packages/editor/src/extensions/code-block/lowlight.ts
create mode 100644 packages/editor/src/extensions/code/index.ts
create mode 100644 packages/editor/src/extensions/color/ColorBubbleItem.vue
create mode 100644 packages/editor/src/extensions/color/ColorToolbarItem.vue
create mode 100644 packages/editor/src/extensions/color/index.ts
create mode 100644 packages/editor/src/extensions/columns/column.ts
create mode 100644 packages/editor/src/extensions/columns/columns.ts
create mode 100644 packages/editor/src/extensions/columns/index.ts
create mode 100644 packages/editor/src/extensions/commands-menu/CommandsView.vue
create mode 100644 packages/editor/src/extensions/commands-menu/commands.ts
create mode 100644 packages/editor/src/extensions/commands-menu/index.ts
create mode 100644 packages/editor/src/extensions/draggable/index.ts
create mode 100644 packages/editor/src/extensions/font-size/index.ts
create mode 100644 packages/editor/src/extensions/format-brush/index.ts
create mode 100644 packages/editor/src/extensions/format-brush/util.ts
create mode 100644 packages/editor/src/extensions/gap-cursor/gap-cursor-selection.ts
create mode 100644 packages/editor/src/extensions/gap-cursor/index.ts
create mode 100644 packages/editor/src/extensions/heading/index.ts
create mode 100644 packages/editor/src/extensions/highlight/HighlightBubbleItem.vue
create mode 100644 packages/editor/src/extensions/highlight/HighlightToolbarItem.vue
create mode 100644 packages/editor/src/extensions/highlight/index.ts
create mode 100644 packages/editor/src/extensions/history/index.ts
create mode 100644 packages/editor/src/extensions/iframe/BubbleItemIframeLink.vue
create mode 100644 packages/editor/src/extensions/iframe/BubbleItemIframeSize.vue
create mode 100644 packages/editor/src/extensions/iframe/IframeView.vue
create mode 100644 packages/editor/src/extensions/iframe/index.ts
create mode 100644 packages/editor/src/extensions/image/BubbleItemImageAlt.vue
create mode 100644 packages/editor/src/extensions/image/BubbleItemImageHref.vue
create mode 100644 packages/editor/src/extensions/image/BubbleItemImageLink.vue
create mode 100644 packages/editor/src/extensions/image/BubbleItemImageSize.vue
create mode 100644 packages/editor/src/extensions/image/ImageView.vue
create mode 100644 packages/editor/src/extensions/image/index.ts
create mode 100644 packages/editor/src/extensions/indent/index.ts
create mode 100644 packages/editor/src/extensions/index.ts
create mode 100644 packages/editor/src/extensions/italic/index.ts
create mode 100644 packages/editor/src/extensions/link/LinkBubbleButton.vue
create mode 100644 packages/editor/src/extensions/link/index.ts
create mode 100644 packages/editor/src/extensions/list-keymap/index.ts
create mode 100644 packages/editor/src/extensions/node-selected/index.ts
create mode 100644 packages/editor/src/extensions/ordered-list/index.ts
create mode 100644 packages/editor/src/extensions/paragraph/index.ts
create mode 100644 packages/editor/src/extensions/range-selection/index.ts
create mode 100644 packages/editor/src/extensions/range-selection/range-selection.ts
create mode 100644 packages/editor/src/extensions/search-and-replace/SearchAndReplace.vue
create mode 100644 packages/editor/src/extensions/search-and-replace/SearchAndReplacePlugin.ts
create mode 100644 packages/editor/src/extensions/search-and-replace/index.ts
create mode 100644 packages/editor/src/extensions/strike/index.ts
create mode 100644 packages/editor/src/extensions/subscript/index.ts
create mode 100644 packages/editor/src/extensions/superscript/index.ts
create mode 100644 packages/editor/src/extensions/table/index.ts
create mode 100644 packages/editor/src/extensions/table/table-cell.ts
create mode 100644 packages/editor/src/extensions/table/table-header.ts
create mode 100644 packages/editor/src/extensions/table/table-row.ts
create mode 100644 packages/editor/src/extensions/table/util.ts
create mode 100644 packages/editor/src/extensions/task-list/index.ts
create mode 100644 packages/editor/src/extensions/text-align/index.ts
create mode 100644 packages/editor/src/extensions/text-style/index.ts
create mode 100644 packages/editor/src/extensions/text/index.ts
create mode 100644 packages/editor/src/extensions/trailing-node/index.ts
create mode 100644 packages/editor/src/extensions/underline/index.ts
create mode 100644 packages/editor/src/extensions/video/BubbleItemVideoLink.vue
create mode 100644 packages/editor/src/extensions/video/BubbleItemVideoSize.vue
create mode 100644 packages/editor/src/extensions/video/VideoView.vue
create mode 100644 packages/editor/src/extensions/video/index.ts
create mode 100644 packages/editor/src/index.ts
create mode 100644 packages/editor/src/locales/en.yaml
create mode 100644 packages/editor/src/locales/index.ts
create mode 100644 packages/editor/src/locales/zh-CN.yaml
create mode 100644 packages/editor/src/styles/base.scss
create mode 100644 packages/editor/src/styles/columns.scss
create mode 100644 packages/editor/src/styles/draggable.scss
create mode 100644 packages/editor/src/styles/format-brush.scss
create mode 100644 packages/editor/src/styles/gap-cursor.scss
create mode 100644 packages/editor/src/styles/index.scss
create mode 100644 packages/editor/src/styles/node-select.scss
create mode 100644 packages/editor/src/styles/range-selection.scss
create mode 100644 packages/editor/src/styles/search.scss
create mode 100644 packages/editor/src/styles/table.scss
create mode 100644 packages/editor/src/styles/tailwind.css
create mode 100644 packages/editor/src/tiptap/core/index.ts
create mode 100644 packages/editor/src/tiptap/index.ts
create mode 100644 packages/editor/src/tiptap/pm/index.ts
create mode 100644 packages/editor/src/tiptap/vue-3/index.ts
create mode 100644 packages/editor/src/types/index.ts
create mode 100644 packages/editor/src/utils/anchor.ts
create mode 100644 packages/editor/src/utils/delete-node.ts
create mode 100644 packages/editor/src/utils/index.ts
create mode 100644 packages/editor/src/utils/is-allowed-uri.ts
create mode 100644 packages/editor/src/utils/isListActive.ts
create mode 100644 packages/editor/src/utils/isNodeEmpty.ts
create mode 100644 packages/editor/tailwind.config.js
create mode 100644 packages/editor/tsconfig.app.json
create mode 100644 packages/editor/tsconfig.json
create mode 100644 packages/editor/tsconfig.node.json
create mode 100644 packages/editor/tsconfig.vitest.json
create mode 100644 packages/editor/vite.config.ts
create mode 100644 packages/editor/vite.lib.config.ts
create mode 100644 packages/shared/.eslintrc.cjs
create mode 100644 packages/shared/env.d.ts
create mode 100644 packages/shared/package.json
create mode 100644 packages/shared/prettier.config.js
create mode 100644 packages/shared/src/components/.gitkeep
create mode 100644 packages/shared/src/core/plugins.ts
create mode 100644 packages/shared/src/index.ts
create mode 100644 packages/shared/src/states/attachment-selector.ts
create mode 100644 packages/shared/src/states/backup.ts
create mode 100644 packages/shared/src/states/comment-subject-ref.ts
create mode 100644 packages/shared/src/states/editor.ts
create mode 100644 packages/shared/src/states/entity.ts
create mode 100644 packages/shared/src/states/operation.ts
create mode 100644 packages/shared/src/states/pages.ts
create mode 100644 packages/shared/src/states/plugin-installation-tabs.ts
create mode 100644 packages/shared/src/states/plugin-tab.ts
create mode 100644 packages/shared/src/states/theme-list-tabs.ts
create mode 100644 packages/shared/src/states/user-tab.ts
create mode 100644 packages/shared/src/types/menus.ts
create mode 100644 packages/shared/src/types/plugin.ts
create mode 100644 packages/shared/tsconfig.app.json
create mode 100644 packages/shared/tsconfig.json
create mode 100644 packages/shared/tsconfig.node.json
create mode 100644 packages/shared/tsconfig.vitest.json
create mode 100644 packages/shared/vite.config.ts
create mode 100644 packages/ui-plugin-bundler-kit/.eslintignore
create mode 100644 packages/ui-plugin-bundler-kit/.eslintrc.js
create mode 100644 packages/ui-plugin-bundler-kit/.prettierrc.cjs
create mode 100644 packages/ui-plugin-bundler-kit/build.config.ts
create mode 100644 packages/ui-plugin-bundler-kit/package.json
create mode 100644 packages/ui-plugin-bundler-kit/src/index.ts
create mode 100644 packages/ui-plugin-bundler-kit/tsconfig.json
create mode 100644 pnpm-lock.yaml
create mode 100644 pnpm-workspace.yaml
create mode 100644 postcss.config.cjs
create mode 100644 prettier.config.cjs
create mode 100644 public/favicon.ico
create mode 100644 src/assets/logo.png
create mode 100644 src/assets/logo.svg
create mode 100644 src/components/base-app/BaseApp.vue
create mode 100644 src/components/button/SubmitButton.vue
create mode 100644 src/components/codemirror/Codemirror.vue
create mode 100644 src/components/common/AppDownloadAlert.vue
create mode 100644 src/components/common/LocaleChange.vue
create mode 100644 src/components/dropdown-selector/EditorProviderSelector.vue
create mode 100644 src/components/editor/DefaultEditor.vue
create mode 100644 src/components/editor/components/EditorLinkObtain.vue
create mode 100644 src/components/editor/components/InlineBlockBox.vue
create mode 100644 src/components/editor/components/index.ts
create mode 100644 src/components/editor/composables/use-attachment.ts
create mode 100644 src/components/editor/extensions/audio/AudioView.vue
create mode 100644 src/components/editor/extensions/audio/index.ts
create mode 100644 src/components/editor/extensions/image/ImageView.vue
create mode 100644 src/components/editor/extensions/image/index.ts
create mode 100644 src/components/editor/extensions/index.ts
create mode 100644 src/components/editor/extensions/upload/index.ts
create mode 100644 src/components/editor/extensions/video/VideoView.vue
create mode 100644 src/components/editor/extensions/video/index.ts
create mode 100644 src/components/editor/utils/attachment.ts
create mode 100644 src/components/editor/utils/upload.ts
create mode 100644 src/components/entity-fields/EntityFieldItems.vue
create mode 100644 src/components/entity-fields/StatusDotField.vue
create mode 100644 src/components/entity/EntityDropdownItems.vue
create mode 100644 src/components/filter/CategoryFilterDropdown.vue
create mode 100644 src/components/filter/FilterCleanButton.vue
create mode 100644 src/components/filter/FilterDropdown.vue
create mode 100644 src/components/filter/FilterTag.vue
create mode 100644 src/components/filter/TagFilterDropdown.vue
create mode 100644 src/components/filter/UserFilterDropdown.vue
create mode 100644 src/components/form/AnnotationsForm.vue
create mode 100644 src/components/global-search/GlobalSearchModal.vue
create mode 100644 src/components/icon/AttachmentFileTypeIcon.vue
create mode 100644 src/components/image/LazyImage.vue
create mode 100644 src/components/input/SearchInput.vue
create mode 100644 src/components/login/LoginForm.vue
create mode 100644 src/components/login/LoginModal.vue
create mode 100644 src/components/login/MfaForm.vue
create mode 100644 src/components/login/SocialAuthProviderItem.vue
create mode 100644 src/components/login/SocialAuthProviders.vue
create mode 100644 src/components/menu/RoutesMenu.tsx
create mode 100644 src/components/permission/HasPermission.vue
create mode 100644 src/components/preview/UrlPreviewModal.vue
create mode 100644 src/components/signup/SignupForm.vue
create mode 100644 src/components/sticky-block/StickyBlock.vue
create mode 100644 src/components/upload/UppyUpload.vue
create mode 100644 src/components/user-avatar/UserAvatar.vue
create mode 100644 src/components/user-avatar/UserAvatarCropper.vue
create mode 100644 src/components/user/PostContributorList.vue
create mode 100644 src/components/video/LazyVideo.vue
create mode 100644 src/composables/use-auto-save-content.ts
create mode 100644 src/composables/use-content-cache.ts
create mode 100644 src/composables/use-editor-extension-points.ts
create mode 100644 src/composables/use-role.ts
create mode 100644 src/composables/use-route-menu-generator.ts
create mode 100644 src/composables/use-session-keep-alive.ts
create mode 100644 src/composables/use-title.ts
create mode 100644 src/constants/annotations.ts
create mode 100644 src/constants/app.ts
create mode 100644 src/constants/constants.ts
create mode 100644 src/constants/error-types.ts
create mode 100644 src/constants/labels.ts
create mode 100644 src/formkit/formkit.config.ts
create mode 100644 src/formkit/inputs/attachment-group-select.ts
create mode 100644 src/formkit/inputs/attachment-policy-select.ts
create mode 100644 src/formkit/inputs/attachment/AttachmentInput.vue
create mode 100644 src/formkit/inputs/attachment/index.ts
create mode 100644 src/formkit/inputs/category-checkbox.ts
create mode 100644 src/formkit/inputs/category-select/CategorySelect.vue
create mode 100644 src/formkit/inputs/category-select/components/CategoryListItem.vue
create mode 100644 src/formkit/inputs/category-select/components/CategoryTag.vue
create mode 100644 src/formkit/inputs/category-select/components/SearchResultListItem.vue
create mode 100644 src/formkit/inputs/category-select/index.ts
create mode 100644 src/formkit/inputs/category-select/sections/index.ts
create mode 100644 src/formkit/inputs/code/CodeInput.vue
create mode 100644 src/formkit/inputs/code/index.ts
create mode 100644 src/formkit/inputs/form.ts
create mode 100644 src/formkit/inputs/group.ts
create mode 100644 src/formkit/inputs/list/AddButton.vue
create mode 100644 src/formkit/inputs/list/features/lists.ts
create mode 100644 src/formkit/inputs/list/index.ts
create mode 100644 src/formkit/inputs/list/listSection.ts
create mode 100644 src/formkit/inputs/list/sections/index.ts
create mode 100644 src/formkit/inputs/menu-checkbox.ts
create mode 100644 src/formkit/inputs/menu-item-select.ts
create mode 100644 src/formkit/inputs/menu-radio.ts
create mode 100644 src/formkit/inputs/password/RevealButton.vue
create mode 100644 src/formkit/inputs/password/index.ts
create mode 100644 src/formkit/inputs/post-select.ts
create mode 100644 src/formkit/inputs/repeater/AddButton.vue
create mode 100644 src/formkit/inputs/repeater/features/repeats.ts
create mode 100644 src/formkit/inputs/repeater/index.ts
create mode 100644 src/formkit/inputs/repeater/repeaterSection.ts
create mode 100644 src/formkit/inputs/repeater/sections/index.ts
create mode 100644 src/formkit/inputs/role-select.ts
create mode 100644 src/formkit/inputs/secret/SecretSelect.vue
create mode 100644 src/formkit/inputs/secret/components/SecretCreationModal.vue
create mode 100644 src/formkit/inputs/secret/components/SecretEditModal.vue
create mode 100644 src/formkit/inputs/secret/components/SecretForm.vue
create mode 100644 src/formkit/inputs/secret/components/SecretListItem.vue
create mode 100644 src/formkit/inputs/secret/components/SecretListModal.vue
create mode 100644 src/formkit/inputs/secret/composables/use-secrets-fetch.ts
create mode 100644 src/formkit/inputs/secret/index.ts
create mode 100644 src/formkit/inputs/secret/sections/index.ts
create mode 100644 src/formkit/inputs/secret/types/index.ts
create mode 100644 src/formkit/inputs/singlePage-select.ts
create mode 100644 src/formkit/inputs/tag-checkbox.ts
create mode 100644 src/formkit/inputs/tag-select/TagSelect.vue
create mode 100644 src/formkit/inputs/tag-select/index.ts
create mode 100644 src/formkit/inputs/tag-select/sections/index.ts
create mode 100644 src/formkit/inputs/user-select.ts
create mode 100644 src/formkit/inputs/verify-form/VerificationButton.vue
create mode 100644 src/formkit/inputs/verify-form/features/index.ts
create mode 100644 src/formkit/inputs/verify-form/index.ts
create mode 100644 src/formkit/plugins/auto-scroll-to-errors.ts
create mode 100644 src/formkit/plugins/password-prevent-autocomplete.ts
create mode 100644 src/formkit/plugins/radio-alt.ts
create mode 100644 src/formkit/plugins/required-asterisk.ts
create mode 100644 src/formkit/plugins/stop-implicit-submission.ts
create mode 100644 src/formkit/theme.ts
create mode 100644 src/formkit/utils/focus.ts
create mode 100644 src/layouts/GatewayLayout.vue
create mode 100644 src/locales/en.yaml
create mode 100644 src/locales/es.yaml
create mode 100644 src/locales/index.ts
create mode 100644 src/locales/zh-CN.yaml
create mode 100644 src/locales/zh-TW.yaml
create mode 100644 src/setup/setupApiClient.ts
create mode 100644 src/setup/setupComponents.ts
create mode 100644 src/setup/setupStyles.ts
create mode 100644 src/setup/setupVueQuery.ts
create mode 100644 src/stores/global-info.ts
create mode 100644 src/stores/plugin.ts
create mode 100644 src/stores/role.ts
create mode 100644 src/stores/user.ts
create mode 100644 src/styles/index.css
create mode 100644 src/styles/tailwind.css
create mode 100644 src/types/actuator.ts
create mode 100644 src/types/index.ts
create mode 100644 src/types/slug.ts
create mode 100644 src/utils/__tests__/date.spec.ts
create mode 100644 src/utils/__tests__/media-type.spec.ts
create mode 100644 src/utils/__tests__/permission.spec.ts
create mode 100644 src/utils/date.ts
create mode 100644 src/utils/device.ts
create mode 100644 src/utils/id.ts
create mode 100644 src/utils/image.ts
create mode 100644 src/utils/load-style.ts
create mode 100644 src/utils/media-type.ts
create mode 100644 src/utils/permission.ts
create mode 100644 src/utils/role.ts
create mode 100644 src/views/exceptions/Forbidden.vue
create mode 100644 src/views/exceptions/NotFound.vue
create mode 100644 src/views/exceptions/__tests__/NotFound.spec.ts
create mode 100644 src/views/exceptions/components/Exception.vue
create mode 100644 src/vite/config-builder.ts
create mode 100644 src/vite/library-external.ts
create mode 100644 tailwind.config.cjs
create mode 100644 tsconfig.app.json
create mode 100644 tsconfig.json
create mode 100644 tsconfig.node.json
create mode 100644 tsconfig.vitest.json
create mode 100644 uc-src/App.vue
create mode 100644 uc-src/layouts/BasicLayout.vue
create mode 100644 uc-src/main.ts
create mode 100644 uc-src/modules/contents/posts/PostEditor.vue
create mode 100644 uc-src/modules/contents/posts/PostList.vue
create mode 100644 uc-src/modules/contents/posts/components/PostCreationModal.vue
create mode 100644 uc-src/modules/contents/posts/components/PostListItem.vue
create mode 100644 uc-src/modules/contents/posts/components/PostSettingEditModal.vue
create mode 100644 uc-src/modules/contents/posts/components/PostSettingForm.vue
create mode 100644 uc-src/modules/contents/posts/composables/use-post-update-mutate.ts
create mode 100644 uc-src/modules/contents/posts/module.ts
create mode 100644 uc-src/modules/contents/posts/types/index.ts
create mode 100644 uc-src/modules/index.ts
create mode 100644 uc-src/modules/notifications/Notifications.vue
create mode 100644 uc-src/modules/notifications/components/NotificationContent.vue
create mode 100644 uc-src/modules/notifications/components/NotificationListItem.vue
create mode 100644 uc-src/modules/notifications/module.ts
create mode 100644 uc-src/modules/profile/Profile.vue
create mode 100644 uc-src/modules/profile/components/EmailVerifyModal.vue
create mode 100644 uc-src/modules/profile/components/PasswordChangeModal.vue
create mode 100644 uc-src/modules/profile/components/PersonalAccessTokenCreationModal.vue
create mode 100644 uc-src/modules/profile/components/PersonalAccessTokenListItem.vue
create mode 100644 uc-src/modules/profile/components/ProfileEditingModal.vue
create mode 100644 uc-src/modules/profile/composables/use-user.ts
create mode 100644 uc-src/modules/profile/module.ts
create mode 100644 uc-src/modules/profile/tabs/Detail.vue
create mode 100644 uc-src/modules/profile/tabs/Devices.vue
create mode 100644 uc-src/modules/profile/tabs/NotificationPreferences.vue
create mode 100644 uc-src/modules/profile/tabs/PersonalAccessTokens.vue
create mode 100644 uc-src/modules/profile/tabs/TwoFactor.vue
create mode 100644 uc-src/modules/profile/tabs/components/DeviceDetailModal.vue
create mode 100644 uc-src/modules/profile/tabs/components/DeviceListItem.vue
create mode 100644 uc-src/modules/profile/tabs/components/PasswordValidationForm.vue
create mode 100644 uc-src/modules/profile/tabs/components/TotpConfigureModal.vue
create mode 100644 uc-src/modules/profile/tabs/components/TotpDeletionModal.vue
create mode 100644 uc-src/modules/profile/tabs/components/TwoFactorDisableModal.vue
create mode 100644 uc-src/modules/profile/tabs/components/TwoFactorEnableModal.vue
create mode 100644 uc-src/modules/profile/tabs/composables/use-user-agent.ts
create mode 100644 uc-src/modules/profile/tabs/composables/use-user-device.ts
create mode 100644 uc-src/router/constant.ts
create mode 100644 uc-src/router/guards/auth-check.ts
create mode 100644 uc-src/router/guards/permission.ts
create mode 100644 uc-src/router/index.ts
create mode 100644 uc-src/router/routes.config.ts
create mode 100644 uc-src/setup/setupModules.ts
create mode 100644 uc-src/views/ResetPassword.vue
create mode 100644 vite.config.ts
create mode 100644 vite.uc.config.ts
create mode 100644 vitest.config.ts
diff --git a/.changeset/README.md b/.changeset/README.md
new file mode 100644
index 0000000..e5b6d8d
--- /dev/null
+++ b/.changeset/README.md
@@ -0,0 +1,8 @@
+# Changesets
+
+Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
+with multi-package repos, or single-package repos to help you version and publish your code. You can
+find the full documentation for it [in our repository](https://github.com/changesets/changesets)
+
+We have a quick list of common questions to get you started engaging with this project in
+[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
diff --git a/.changeset/config.json b/.changeset/config.json
new file mode 100644
index 0000000..ff9fe79
--- /dev/null
+++ b/.changeset/config.json
@@ -0,0 +1,11 @@
+{
+ "$schema": "https://unpkg.com/@changesets/config@2.0.1/schema.json",
+ "changelog": "@changesets/cli/changelog",
+ "commit": false,
+ "fixed": [],
+ "linked": [["@halo-dev/*"]],
+ "access": "public",
+ "baseBranch": "next",
+ "updateInternalDependencies": "patch",
+ "ignore": []
+}
diff --git a/.changeset/pre.json b/.changeset/pre.json
new file mode 100644
index 0000000..694d1d3
--- /dev/null
+++ b/.changeset/pre.json
@@ -0,0 +1,9 @@
+{
+ "mode": "pre",
+ "tag": "alpha",
+ "initialVersions": {
+ "@halo-dev/components": "0.0.0-alpha.0",
+ "@halo-dev/console-shared": "0.0.0-alpha.0"
+ },
+ "changesets": []
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..53fe054
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,12 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = false
+insert_final_newline = true
\ No newline at end of file
diff --git a/.env.development b/.env.development
new file mode 100644
index 0000000..e69de29
diff --git a/.env.production b/.env.production
new file mode 100644
index 0000000..e69de29
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
new file mode 100644
index 0000000..63132e5
--- /dev/null
+++ b/.eslintrc.cjs
@@ -0,0 +1,31 @@
+/* eslint-env node */
+require("@rushstack/eslint-patch/modern-module-resolution");
+
+module.exports = {
+ root: true,
+ extends: [
+ "plugin:vue/vue3-recommended",
+ "eslint:recommended",
+ "@vue/eslint-config-typescript/recommended",
+ "@vue/eslint-config-prettier",
+ ],
+ env: {
+ node: true,
+ "vue/setup-compiler-macros": true,
+ },
+ rules: {
+ "vue/multi-word-component-names": 0,
+ "@typescript-eslint/ban-ts-comment": 0,
+ "vue/no-v-html": 0,
+ },
+ overrides: [
+ {
+ files: ["cypress/integration/**.spec.{js,ts,jsx,tsx}"],
+ extends: ["plugin:cypress/recommended"],
+ },
+ ],
+ ignorePatterns: ["!.storybook", "packages/api-client"],
+ parserOptions: {
+ ecmaVersion: "latest",
+ },
+};
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b6c6ca7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,32 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+.DS_Store
+dist
+dist-ssr
+histoire-dist
+coverage
+*.local
+
+/cypress/videos/
+/cypress/screenshots/
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+!src/build
+storybook-static
diff --git a/.gitpod.yml b/.gitpod.yml
new file mode 100644
index 0000000..c7ed743
--- /dev/null
+++ b/.gitpod.yml
@@ -0,0 +1,15 @@
+ports:
+ - port: 3000
+ onOpen: open-browser
+
+tasks:
+ - init: npm install -g pnpm && pnpm install && pnpm build:packages
+ command: pnpm dev
+
+vscode:
+ extensions:
+ - dbaeumer.vscode-eslint
+ - editorconfig.editorconfig
+ - esbenp.prettier-vscode
+ - vue.volar
+ - vue.vscode-typescript-vue-plugin
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 0000000..f2c6343
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,4 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+cd ui && pnpm exec lint-staged
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..9357dbd
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,4 @@
+/node_modules/*
+/.idea/*
+/.git/*
+/.github/*
\ No newline at end of file
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..19be10e
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1,2 @@
+strict-peer-dependencies=false
+auto-install-peers=true
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..3909f45
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,2 @@
+pnpm-lock.yaml
+packages/api-client
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..80ef097
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,8 @@
+{
+ "recommendations": [
+ "dbaeumer.vscode-eslint",
+ "esbenp.prettier-vscode",
+ "editorconfig.editorconfig",
+ "vue.volar"
+ ]
+}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..f043af2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,28 @@
+SHELL := /usr/bin/env bash -o errexit -o pipefail -o nounset
+
+install: ## Install console
+ pnpm install
+
+build-packages: install ## Build packages of console
+ pnpm build:packages
+
+build: build-packages ## Build console
+ pnpm build
+
+lint: build-packages ## Lint console
+ pnpm lint
+ pnpm typecheck
+
+test: build-packages ## Test console
+ pnpm test:unit
+
+check: lint test ## Check console
+
+dev: build-packages ## Run console with development environment
+ pnpm dev
+
+api-client-gen: install ## Generate API client
+ pnpm api-client:gen
+
+help: ## print this help
+ @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9_-]+:.*?## / {gsub("\\\\n",sprintf("\n%22c",""), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..66b425d
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,15 @@
+reviewers:
+- ruibaby
+- guqing
+- JohnNiang
+- lan-yonghui
+- wan92hen
+- QuentinHsu
+- Aanko
+- wzrove
+- LIlGG
+
+approvers:
+- ruibaby
+- guqing
+- JohnNiang
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a377363
--- /dev/null
+++ b/README.md
@@ -0,0 +1,49 @@
+## README
+
+
+
+
+
+
+
+> Halo 2.0 的管理端项目(原 halo-admin)
+
+
+
+
+
+
+
+
+
+------------------------------
+
+当前仓库已经将 `halo-admin` 改为了 `console`。但对于 Halo 1.x 版本,依旧保持 halo-admin 的概念。
+
+## 开发环境运行
+
+```bash
+npm install -g pnpm@9
+```
+
+```bash
+pnpm install
+```
+
+```bash
+pnpm build:packages
+```
+
+```bash
+pnpm dev
+```
+
+## 生产构建
+
+```bash
+pnpm build
+```
+
+## 状态
+
+
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..0e4a649
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,74 @@
+plugins {
+ id 'idea'
+ id 'com.github.node-gradle.node'
+ id 'org.openapi.generator' version '7.6.0'
+}
+
+idea {
+ module {
+ excludeDirs += file('node_modules/')
+ excludeDirs += file('packages').listFiles().collect {
+ file(it.path + '/node_modules/')
+ }
+ excludeDirs += file('packages').listFiles().collect {
+ file(it.path + '/dist/')
+ }
+ }
+}
+
+tasks.register('clean', Delete) {
+ delete layout.buildDirectory
+ delete fileTree('packages') {
+ include '*/dist/**'
+ }
+}
+
+tasks.register('build', PnpmTask) {
+ dependsOn tasks.named('check'), tasks.named('buildPackages')
+ pnpmCommand = ['run', 'build']
+ inputs.files(fileTree(layout.projectDirectory) {
+ include 'console-src/**', 'uc-src/**', 'src/**', 'public/**', '*.js', '*.json', '*.yaml', 'index.html'
+ exclude '**/node_modules/**', '**/build/**', '**/dist/**'
+ })
+ outputs.dir(layout.buildDirectory.dir('dist'))
+ configure {
+ shouldRunAfter tasks.named('clean')
+ }
+}
+
+tasks.register('buildPackages', PnpmTask) {
+ dependsOn tasks.named('pnpmInstall')
+ inputs.files(fileTree('packages') {
+ exclude '**/node_modules/**', '**/dist/**'
+ })
+ inputs.file('package.json')
+ pnpmCommand = ['run', 'build:packages']
+ outputs.files(fileTree('packages') {
+ include '*/dist/**'
+ })
+}
+
+tasks.register('test', PnpmTask) {
+ dependsOn tasks.named('buildPackages')
+ pnpmCommand = ['run', 'test:unit']
+ shouldRunAfter tasks.named('lint'), tasks.named('typecheck')
+}
+
+tasks.register('lint', PnpmTask) {
+ dependsOn tasks.named('buildPackages')
+ pnpmCommand = ['run', 'lint']
+}
+
+tasks.register('typecheck', PnpmTask) {
+ dependsOn tasks.named('buildPackages')
+ pnpmCommand = ['run', 'typecheck']
+}
+
+tasks.register('check') {
+ dependsOn tasks.named('lint'), tasks.named('typecheck'), tasks.named('test')
+}
+
+tasks.register('dev', PnpmTask) {
+ dependsOn tasks.named('buildPackages')
+ pnpmCommand = ['run', 'dev']
+}
diff --git a/console-src/App.vue b/console-src/App.vue
new file mode 100644
index 0000000..3fda3d1
--- /dev/null
+++ b/console-src/App.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/console-src/composables/use-content-snapshot.ts b/console-src/composables/use-content-snapshot.ts
new file mode 100644
index 0000000..dde6e4e
--- /dev/null
+++ b/console-src/composables/use-content-snapshot.ts
@@ -0,0 +1,33 @@
+import { coreApiClient } from "@halo-dev/api-client";
+import { nextTick, ref, watch, type Ref } from "vue";
+
+interface SnapshotContent {
+ version: Ref;
+ handleFetchSnapshot: () => Promise;
+}
+
+export function useContentSnapshot(
+ snapshotName: Ref
+): SnapshotContent {
+ const version = ref(0);
+ watch(snapshotName, () => {
+ nextTick(() => {
+ handleFetchSnapshot();
+ });
+ });
+
+ const handleFetchSnapshot = async () => {
+ if (!snapshotName.value) {
+ return;
+ }
+ const { data } = await coreApiClient.content.snapshot.getSnapshot({
+ name: snapshotName.value,
+ });
+ version.value = data.metadata.version || 0;
+ };
+
+ return {
+ version,
+ handleFetchSnapshot,
+ };
+}
diff --git a/console-src/composables/use-dashboard-stats.ts b/console-src/composables/use-dashboard-stats.ts
new file mode 100644
index 0000000..3d7d375
--- /dev/null
+++ b/console-src/composables/use-dashboard-stats.ts
@@ -0,0 +1,13 @@
+import { consoleApiClient } from "@halo-dev/api-client";
+import { useQuery } from "@tanstack/vue-query";
+
+export function useDashboardStats() {
+ const { data } = useQuery({
+ queryKey: ["dashboard-stats"],
+ queryFn: async () => {
+ const { data } = await consoleApiClient.system.getStats();
+ return data;
+ },
+ });
+ return { data };
+}
diff --git a/console-src/composables/use-entity-extension-points.ts b/console-src/composables/use-entity-extension-points.ts
new file mode 100644
index 0000000..164644d
--- /dev/null
+++ b/console-src/composables/use-entity-extension-points.ts
@@ -0,0 +1,47 @@
+import { usePluginModuleStore } from "@/stores/plugin";
+import type { EntityFieldItem, PluginModule } from "@halo-dev/console-shared";
+import { computed, onMounted, ref, type ComputedRef, type Ref } from "vue";
+
+export function useEntityFieldItemExtensionPoint(
+ extensionPointName: string,
+ entity: Ref,
+ presets: ComputedRef
+) {
+ const { pluginModules } = usePluginModuleStore();
+ const itemsFromPlugins = ref([]);
+
+ const allItems = computed(() => {
+ return [...presets.value, ...itemsFromPlugins.value];
+ });
+
+ onMounted(() => {
+ pluginModules.forEach((pluginModule: PluginModule) => {
+ const { extensionPoints } = pluginModule;
+ if (!extensionPoints?.[extensionPointName]) {
+ return;
+ }
+ const items = extensionPoints[extensionPointName](
+ entity
+ ) as EntityFieldItem[];
+ itemsFromPlugins.value.push(...items);
+ });
+ });
+
+ const startFields = computed(() => {
+ return allItems.value
+ .filter((item) => item.position === "start")
+ .sort((a, b) => {
+ return a.priority - b.priority;
+ });
+ });
+
+ const endFields = computed(() => {
+ return allItems.value
+ .filter((item) => item.position === "end")
+ .sort((a, b) => {
+ return a.priority - b.priority;
+ });
+ });
+
+ return { startFields, endFields };
+}
diff --git a/console-src/composables/use-global-info.ts b/console-src/composables/use-global-info.ts
new file mode 100644
index 0000000..e9dc2a0
--- /dev/null
+++ b/console-src/composables/use-global-info.ts
@@ -0,0 +1,20 @@
+import type { GlobalInfo } from "@/types";
+import { useQuery } from "@tanstack/vue-query";
+import axios from "axios";
+
+export function useGlobalInfoFetch() {
+ const { data } = useQuery({
+ queryKey: ["globalinfo"],
+ queryFn: async () => {
+ const { data } = await axios.get(`/actuator/globalinfo`, {
+ withCredentials: true,
+ });
+
+ return data;
+ },
+ });
+
+ return {
+ globalInfo: data,
+ };
+}
diff --git a/console-src/composables/use-operation-extension-points.ts b/console-src/composables/use-operation-extension-points.ts
new file mode 100644
index 0000000..d594b58
--- /dev/null
+++ b/console-src/composables/use-operation-extension-points.ts
@@ -0,0 +1,36 @@
+import { usePluginModuleStore } from "@/stores/plugin";
+import type { OperationItem, PluginModule } from "@halo-dev/console-shared";
+import { computed, onMounted, ref, type ComputedRef, type Ref } from "vue";
+
+export function useOperationItemExtensionPoint(
+ extensionPointName: string,
+ entity: Ref,
+ presets: ComputedRef[]>
+) {
+ const { pluginModules } = usePluginModuleStore();
+
+ const itemsFromPlugins = ref[]>([]);
+
+ onMounted(() => {
+ pluginModules.forEach((pluginModule: PluginModule) => {
+ const { extensionPoints } = pluginModule;
+ if (!extensionPoints?.[extensionPointName]) {
+ return;
+ }
+
+ const items = extensionPoints[extensionPointName](
+ entity
+ ) as OperationItem[];
+
+ itemsFromPlugins.value.push(...items);
+ });
+ });
+
+ const operationItems = computed(() => {
+ return [...presets.value, ...itemsFromPlugins.value].sort((a, b) => {
+ return a.priority - b.priority;
+ });
+ });
+
+ return { operationItems };
+}
diff --git a/console-src/composables/use-save-keybinding.ts b/console-src/composables/use-save-keybinding.ts
new file mode 100644
index 0000000..cdfb768
--- /dev/null
+++ b/console-src/composables/use-save-keybinding.ts
@@ -0,0 +1,21 @@
+import { isMac } from "@/utils/device";
+import { useEventListener } from "@vueuse/core";
+import { useDebounceFn } from "@vueuse/shared";
+import { nextTick } from "vue";
+
+export function useSaveKeybinding(fn: () => void) {
+ const debouncedFn = useDebounceFn(() => {
+ fn();
+ }, 300);
+
+ useEventListener(window, "keydown", (e: KeyboardEvent) => {
+ if (isMac ? e.metaKey : e.ctrlKey) {
+ if (e.key === "s") {
+ e.preventDefault();
+ nextTick(() => {
+ debouncedFn();
+ });
+ }
+ }
+ });
+}
diff --git a/console-src/composables/use-setting-form.ts b/console-src/composables/use-setting-form.ts
new file mode 100644
index 0000000..63ef153
--- /dev/null
+++ b/console-src/composables/use-setting-form.ts
@@ -0,0 +1,92 @@
+// core libs
+// types
+import { computed, ref, watch, type ComputedRef, type Ref } from "vue";
+
+// libs
+import type { FormKitSchemaCondition, FormKitSchemaNode } from "@formkit/core";
+import type { ConfigMap, Setting, SettingForm } from "@halo-dev/api-client";
+import { cloneDeep } from "lodash-es";
+
+interface useSettingFormConvertReturn {
+ formSchema: ComputedRef<
+ (FormKitSchemaCondition | FormKitSchemaNode)[] | undefined
+ >;
+ configMapFormData: Ref>>;
+ convertToSave: () => ConfigMap | undefined;
+}
+
+export function useSettingFormConvert(
+ setting: Ref,
+ configMap: Ref,
+ group: Ref
+): useSettingFormConvertReturn {
+ const configMapFormData = ref>>({});
+
+ const formSchema = computed(() => {
+ if (!setting.value) {
+ return;
+ }
+ const { forms } = setting.value.spec;
+ return forms.find((item) => item.group === group?.value)?.formSchema as (
+ | FormKitSchemaCondition
+ | FormKitSchemaNode
+ )[];
+ });
+
+ watch(
+ () => configMap.value,
+ () => {
+ const { forms } = setting.value?.spec || {};
+
+ forms?.forEach((form) => {
+ configMapFormData.value[form.group] = JSON.parse(
+ configMap.value?.data?.[form.group] || "{}"
+ );
+ });
+
+ Object.keys(configMap.value?.data || {}).forEach((key) => {
+ if (!forms?.find((item) => item.group === key)) {
+ configMapFormData.value[key] = JSON.parse(
+ configMap.value?.data?.[key] || "{}"
+ );
+ }
+ });
+ },
+ {
+ immediate: true,
+ }
+ );
+
+ function convertToSave() {
+ const configMapToUpdate = cloneDeep(configMap.value);
+
+ if (!configMapToUpdate) {
+ return;
+ }
+
+ const data: {
+ [key: string]: string;
+ } = {};
+
+ const { forms } = setting.value?.spec || {};
+
+ forms?.forEach((item: SettingForm) => {
+ data[item.group] = JSON.stringify(configMapFormData?.value?.[item.group]);
+ });
+
+ Object.keys(configMap.value?.data || {}).forEach((key) => {
+ if (!forms?.find((item) => item.group === key)) {
+ data[key] = configMap.value?.data?.[key] || "{}";
+ }
+ });
+
+ configMapToUpdate.data = data;
+ return configMapToUpdate;
+ }
+
+ return {
+ formSchema,
+ configMapFormData,
+ convertToSave,
+ };
+}
diff --git a/console-src/composables/use-slugify.ts b/console-src/composables/use-slugify.ts
new file mode 100644
index 0000000..b94c1b4
--- /dev/null
+++ b/console-src/composables/use-slugify.ts
@@ -0,0 +1,69 @@
+import { useGlobalInfoStore } from "@/stores/global-info";
+import { FormType } from "@/types/slug";
+import { randomUUID } from "@/utils/id";
+import ShortUniqueId from "short-unique-id";
+import { slugify } from "transliteration";
+import { watch, type Ref } from "vue";
+const uid = new ShortUniqueId();
+const Strategy = {
+ generateByTitle: (value: string) => {
+ if (!value) return "";
+ return slugify(value, { trim: true });
+ },
+ shortUUID: (value: string) => {
+ if (!value) return "";
+ return uid.randomUUID(8);
+ },
+ UUID: (value: string) => {
+ if (!value) return "";
+ return randomUUID();
+ },
+ timestamp: (value: string) => {
+ if (!value) return "";
+ return new Date().getTime().toString();
+ },
+};
+
+const onceList = ["shortUUID", "UUID", "timestamp"];
+
+export default function useSlugify(
+ source: Ref,
+ target: Ref,
+ auto: Ref,
+ formType: FormType
+) {
+ const handleGenerateSlug = (forceUpdate = false, formType: FormType) => {
+ const globalInfoStore = useGlobalInfoStore();
+ const mode = globalInfoStore.globalInfo?.postSlugGenerationStrategy;
+
+ if (!mode) {
+ return;
+ }
+ if (formType != FormType.POST) {
+ target.value = Strategy["generateByTitle"](source.value);
+ return;
+ }
+ if (forceUpdate) {
+ target.value = Strategy[mode](source.value);
+ return;
+ }
+ if (onceList.includes(mode) && target.value) return;
+ target.value = Strategy[mode](source.value);
+ };
+
+ watch(
+ () => source.value,
+ () => {
+ if (auto.value) {
+ handleGenerateSlug(false, formType);
+ }
+ },
+ {
+ immediate: true,
+ }
+ );
+
+ return {
+ handleGenerateSlug,
+ };
+}
diff --git a/console-src/layouts/BasicLayout.vue b/console-src/layouts/BasicLayout.vue
new file mode 100644
index 0000000..b1fc71b
--- /dev/null
+++ b/console-src/layouts/BasicLayout.vue
@@ -0,0 +1,352 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/layouts/BlankLayout.vue b/console-src/layouts/BlankLayout.vue
new file mode 100644
index 0000000..c098042
--- /dev/null
+++ b/console-src/layouts/BlankLayout.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/console-src/main.ts b/console-src/main.ts
new file mode 100644
index 0000000..5c5c7e5
--- /dev/null
+++ b/console-src/main.ts
@@ -0,0 +1,113 @@
+import { consoleApiClient } from "@halo-dev/api-client";
+import { createPinia } from "pinia";
+import type { DirectiveBinding } from "vue";
+import { createApp } from "vue";
+import App from "./App.vue";
+import router from "./router";
+// setup
+import { getBrowserLanguage, i18n, setupI18n } from "@/locales";
+import { setupComponents } from "@/setup/setupComponents";
+import "@/setup/setupStyles";
+// core modules
+import { setupApiClient } from "@/setup/setupApiClient";
+import { setupVueQuery } from "@/setup/setupVueQuery";
+import { useGlobalInfoStore } from "@/stores/global-info";
+import { useRoleStore } from "@/stores/role";
+import { useUserStore } from "@/stores/user";
+import { hasPermission } from "@/utils/permission";
+import {
+ setupCoreModules,
+ setupPluginModules,
+} from "@console/setup/setupModules";
+import { useSystemConfigMapStore } from "@console/stores/system-configmap";
+import { useThemeStore } from "@console/stores/theme";
+
+const app = createApp(App);
+
+setupComponents(app);
+setupI18n(app);
+setupVueQuery(app);
+setupApiClient();
+
+app.use(createPinia());
+
+async function loadUserPermissions() {
+ const { data: currentPermissions } =
+ await consoleApiClient.user.getPermissions({
+ name: "-",
+ });
+ const roleStore = useRoleStore();
+ roleStore.$patch({
+ permissions: currentPermissions,
+ });
+ app.directive(
+ "permission",
+ (el: HTMLElement, binding: DirectiveBinding) => {
+ const uiPermissions = Array.from(
+ 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() {
+ 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");
+ }
+}
diff --git a/console-src/modules/contents/attachments/AttachmentList.vue b/console-src/modules/contents/attachments/AttachmentList.vue
new file mode 100644
index 0000000..ddce83b
--- /dev/null
+++ b/console-src/modules/contents/attachments/AttachmentList.vue
@@ -0,0 +1,627 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.attachment.actions.storage_policies") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.upload") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
+ {{
+ $t("core.attachment.operations.deselect_items.button")
+ }}
+
+
+
+ {{ $t("core.attachment.operations.move.button") }}
+
+
+
+ {{ group.spec.displayName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.refresh") }}
+
+
+
+
+
+ {{ $t("core.attachment.empty.actions.upload") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ attachment.spec.displayName }}
+
+
+
+ {{ $t("core.common.status.deleting") }}...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/attachments/components/AttachmentDetailModal.vue b/console-src/modules/contents/attachments/components/AttachmentDetailModal.vue
new file mode 100644
index 0000000..e9a401f
--- /dev/null
+++ b/console-src/modules/contents/attachments/components/AttachmentDetailModal.vue
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+
+
+
+
+ {{ $t("core.common.status.loading") }}...
+
+
+
+
+ {{ $t("core.common.status.loading_error") }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.attachment.detail_modal.preview.not_support") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.close_and_shortcut") }}
+
+
+
+
+
+
diff --git a/console-src/modules/contents/attachments/components/AttachmentError.vue b/console-src/modules/contents/attachments/components/AttachmentError.vue
new file mode 100644
index 0000000..17e6cc6
--- /dev/null
+++ b/console-src/modules/contents/attachments/components/AttachmentError.vue
@@ -0,0 +1,7 @@
+
+
+
+ {{ $t("core.common.status.loading_error") }}
+
+
+
diff --git a/console-src/modules/contents/attachments/components/AttachmentGroupBadge.vue b/console-src/modules/contents/attachments/components/AttachmentGroupBadge.vue
new file mode 100644
index 0000000..04c921d
--- /dev/null
+++ b/console-src/modules/contents/attachments/components/AttachmentGroupBadge.vue
@@ -0,0 +1,216 @@
+
+
+
+
+
diff --git a/console-src/modules/contents/attachments/components/AttachmentGroupEditingModal.vue b/console-src/modules/contents/attachments/components/AttachmentGroupEditingModal.vue
new file mode 100644
index 0000000..dad1ed4
--- /dev/null
+++ b/console-src/modules/contents/attachments/components/AttachmentGroupEditingModal.vue
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.cancel_and_shortcut") }}
+
+
+
+
+
diff --git a/console-src/modules/contents/attachments/components/AttachmentGroupList.vue b/console-src/modules/contents/attachments/components/AttachmentGroupList.vue
new file mode 100644
index 0000000..b8b01d3
--- /dev/null
+++ b/console-src/modules/contents/attachments/components/AttachmentGroupList.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/attachments/components/AttachmentListItem.vue b/console-src/modules/contents/attachments/components/AttachmentListItem.vue
new file mode 100644
index 0000000..85f0b57
--- /dev/null
+++ b/console-src/modules/contents/attachments/components/AttachmentListItem.vue
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ attachment.spec.mediaType }}
+
+
+ {{ prettyBytes(attachment.spec.size || 0) }}
+
+
+
+
+
+
+
+
+
+
+ {{ attachment.spec.ownerName }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ formatDatetime(attachment.metadata.creationTimestamp) }}
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/attachments/components/AttachmentLoading.vue b/console-src/modules/contents/attachments/components/AttachmentLoading.vue
new file mode 100644
index 0000000..6fd1a05
--- /dev/null
+++ b/console-src/modules/contents/attachments/components/AttachmentLoading.vue
@@ -0,0 +1,7 @@
+
+
+
+ {{ $t("core.common.status.loading") }}...
+
+
+
diff --git a/console-src/modules/contents/attachments/components/AttachmentPermalinkList.vue b/console-src/modules/contents/attachments/components/AttachmentPermalinkList.vue
new file mode 100644
index 0000000..1338f1c
--- /dev/null
+++ b/console-src/modules/contents/attachments/components/AttachmentPermalinkList.vue
@@ -0,0 +1,72 @@
+
+
+
+
+ -
+
+
+
+ {{ format.label }}
+
+
+ {{ format.value }}
+
+
+
+
+ {{ $t("core.common.buttons.copy") }}
+
+
+
+
+
+
diff --git a/console-src/modules/contents/attachments/components/AttachmentPoliciesModal.vue b/console-src/modules/contents/attachments/components/AttachmentPoliciesModal.vue
new file mode 100644
index 0000000..3b4b5ae
--- /dev/null
+++ b/console-src/modules/contents/attachments/components/AttachmentPoliciesModal.vue
@@ -0,0 +1,215 @@
+
+
+
+
+
+
+
+
+
+
+ {{ policyTemplate.spec?.displayName }}
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.refresh") }}
+
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+ {{ policyTemplate.spec?.displayName }}
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ formatDatetime(policy.metadata.creationTimestamp) }}
+
+
+
+
+
+
+ {{ $t("core.common.buttons.edit") }}
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.close_and_shortcut") }}
+
+
+
+
+
+
diff --git a/console-src/modules/contents/attachments/components/AttachmentPolicyBadge.vue b/console-src/modules/contents/attachments/components/AttachmentPolicyBadge.vue
new file mode 100644
index 0000000..eff608d
--- /dev/null
+++ b/console-src/modules/contents/attachments/components/AttachmentPolicyBadge.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
diff --git a/console-src/modules/contents/attachments/components/AttachmentPolicyEditingModal.vue b/console-src/modules/contents/attachments/components/AttachmentPolicyEditingModal.vue
new file mode 100644
index 0000000..ec09872
--- /dev/null
+++ b/console-src/modules/contents/attachments/components/AttachmentPolicyEditingModal.vue
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.cancel_and_shortcut") }}
+
+
+
+
+
diff --git a/console-src/modules/contents/attachments/components/AttachmentSelectorModal.vue b/console-src/modules/contents/attachments/components/AttachmentSelectorModal.vue
new file mode 100644
index 0000000..b0ae3f8
--- /dev/null
+++ b/console-src/modules/contents/attachments/components/AttachmentSelectorModal.vue
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.status.loading") }}
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.confirm") }}
+
+ {{
+ $t("core.attachment.select_modal.operations.select.result", {
+ count: confirmCountMessage,
+ })
+ }}
+
+
+
+ {{ $t("core.common.buttons.cancel") }}
+
+
+
+
+
diff --git a/console-src/modules/contents/attachments/components/AttachmentUploadModal.vue b/console-src/modules/contents/attachments/components/AttachmentUploadModal.vue
new file mode 100644
index 0000000..6091ecd
--- /dev/null
+++ b/console-src/modules/contents/attachments/components/AttachmentUploadModal.vue
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+ {{ $t("core.attachment.upload_modal.filters.policy.label") }}
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
+
+
+
+ {{ policyTemplate.spec?.displayName }}
+
+
+
+
+
+
+
+
+
+ {{ $t("core.attachment.upload_modal.filters.group.label") }}
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/attachments/components/selector-providers/CoreSelectorProvider.vue b/console-src/modules/contents/attachments/components/selector-providers/CoreSelectorProvider.vue
new file mode 100644
index 0000000..3c2b08f
--- /dev/null
+++ b/console-src/modules/contents/attachments/components/selector-providers/CoreSelectorProvider.vue
@@ -0,0 +1,250 @@
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.upload") }}
+
+
+
+
+
+
+ {{ $t("core.common.buttons.refresh") }}
+
+
+
+
+
+ {{ $t("core.attachment.empty.actions.upload") }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.status.loading") }}...
+
+
+
+
+
+
+ {{ $t("core.common.status.loading_error") }}
+
+
+
+
+
+
+
+ {{ attachment.spec.displayName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/attachments/composables/use-attachment-group.ts b/console-src/modules/contents/attachments/composables/use-attachment-group.ts
new file mode 100644
index 0000000..118297b
--- /dev/null
+++ b/console-src/modules/contents/attachments/composables/use-attachment-group.ts
@@ -0,0 +1,37 @@
+import type { Group } from "@halo-dev/api-client";
+import { coreApiClient } from "@halo-dev/api-client";
+import { useQuery } from "@tanstack/vue-query";
+import type { Ref } from "vue";
+
+interface useFetchAttachmentGroupReturn {
+ groups: Ref;
+ isLoading: Ref;
+ handleFetchGroups: () => void;
+}
+
+export function useFetchAttachmentGroup(): useFetchAttachmentGroupReturn {
+ const { data, isLoading, refetch } = useQuery({
+ queryKey: ["attachment-groups"],
+ queryFn: async () => {
+ const { data } = await coreApiClient.storage.group.listGroup({
+ labelSelector: ["!halo.run/hidden"],
+ sort: ["metadata.creationTimestamp,asc"],
+ });
+
+ return data.items;
+ },
+ refetchInterval(data) {
+ const hasDeletingGroup = data?.some(
+ (group) => !!group.metadata.deletionTimestamp
+ );
+
+ return hasDeletingGroup ? 1000 : false;
+ },
+ });
+
+ return {
+ groups: data,
+ isLoading,
+ handleFetchGroups: refetch,
+ };
+}
diff --git a/console-src/modules/contents/attachments/composables/use-attachment-policy.ts b/console-src/modules/contents/attachments/composables/use-attachment-policy.ts
new file mode 100644
index 0000000..47c3215
--- /dev/null
+++ b/console-src/modules/contents/attachments/composables/use-attachment-policy.ts
@@ -0,0 +1,55 @@
+import type { Policy, PolicyTemplate } from "@halo-dev/api-client";
+import { coreApiClient } from "@halo-dev/api-client";
+import { useQuery } from "@tanstack/vue-query";
+import type { Ref } from "vue";
+
+interface useFetchAttachmentPolicyReturn {
+ policies: Ref;
+ isLoading: Ref;
+ handleFetchPolicies: () => void;
+}
+
+interface useFetchAttachmentPolicyTemplatesReturn {
+ policyTemplates: Ref;
+ isLoading: Ref;
+ handleFetchPolicyTemplates: () => void;
+}
+
+export function useFetchAttachmentPolicy(): useFetchAttachmentPolicyReturn {
+ const { data, isLoading, refetch } = useQuery({
+ queryKey: ["attachment-policies"],
+ queryFn: async () => {
+ const { data } = await coreApiClient.storage.policy.listPolicy();
+ return data.items;
+ },
+ refetchInterval(data) {
+ const hasDeletingPolicy = data?.some(
+ (policy) => !!policy.metadata.deletionTimestamp
+ );
+ return hasDeletingPolicy ? 1000 : false;
+ },
+ });
+
+ return {
+ policies: data,
+ isLoading,
+ handleFetchPolicies: refetch,
+ };
+}
+
+export function useFetchAttachmentPolicyTemplate(): useFetchAttachmentPolicyTemplatesReturn {
+ const { data, isLoading, refetch } = useQuery({
+ queryKey: ["attachment-policy-templates"],
+ queryFn: async () => {
+ const { data } =
+ await coreApiClient.storage.policyTemplate.listPolicyTemplate();
+ return data.items;
+ },
+ });
+
+ return {
+ policyTemplates: data,
+ isLoading,
+ handleFetchPolicyTemplates: refetch,
+ };
+}
diff --git a/console-src/modules/contents/attachments/composables/use-attachment.ts b/console-src/modules/contents/attachments/composables/use-attachment.ts
new file mode 100644
index 0000000..1a386fd
--- /dev/null
+++ b/console-src/modules/contents/attachments/composables/use-attachment.ts
@@ -0,0 +1,307 @@
+import { matchMediaType } from "@/utils/media-type";
+import type { Attachment } from "@halo-dev/api-client";
+import { consoleApiClient, coreApiClient } from "@halo-dev/api-client";
+import { Dialog, Toast } from "@halo-dev/components";
+import { useQuery } from "@tanstack/vue-query";
+import { useClipboard } from "@vueuse/core";
+import { computed, nextTick, ref, watch, type Ref } from "vue";
+import { useI18n } from "vue-i18n";
+
+interface useAttachmentControlReturn {
+ attachments: Ref;
+ isLoading: Ref;
+ isFetching: Ref;
+ selectedAttachment: Ref;
+ selectedAttachments: Ref>;
+ checkedAll: Ref;
+ total: Ref;
+ handleFetchAttachments: () => void;
+ handleSelectPrevious: () => void;
+ handleSelectNext: () => void;
+ handleDeleteInBatch: () => void;
+ handleCheckAll: (checkAll: boolean) => void;
+ handleSelect: (attachment: Attachment | undefined) => void;
+ isChecked: (attachment: Attachment) => boolean;
+ handleReset: () => void;
+}
+
+export function useAttachmentControl(filterOptions: {
+ policyName?: Ref;
+ groupName?: Ref;
+ user?: Ref;
+ accepts?: Ref;
+ keyword?: Ref;
+ sort?: Ref;
+ page: Ref;
+ size: Ref;
+}): useAttachmentControlReturn {
+ const { t } = useI18n();
+
+ const { user, policyName, groupName, keyword, sort, page, size, accepts } =
+ filterOptions;
+
+ const selectedAttachment = ref();
+ const selectedAttachments = ref>(new Set());
+ const checkedAll = ref(false);
+
+ const total = ref(0);
+ const hasPrevious = ref(false);
+ const hasNext = ref(false);
+
+ const { data, isLoading, isFetching, refetch } = useQuery({
+ queryKey: [
+ "attachments",
+ policyName,
+ keyword,
+ groupName,
+ user,
+ accepts,
+ page,
+ size,
+ sort,
+ ],
+ queryFn: async () => {
+ const isUnGrouped = groupName?.value === "ungrouped";
+
+ const fieldSelectorMap: Record = {
+ "spec.policyName": policyName?.value,
+ "spec.ownerName": user?.value,
+ "spec.groupName": isUnGrouped ? undefined : groupName?.value,
+ };
+
+ const fieldSelector = Object.entries(fieldSelectorMap)
+ .map(([key, value]) => {
+ if (value) {
+ return `${key}=${value}`;
+ }
+ })
+ .filter(Boolean) as string[];
+
+ const { data } =
+ await consoleApiClient.storage.attachment.searchAttachments({
+ fieldSelector,
+ page: page.value,
+ size: size.value,
+ ungrouped: isUnGrouped,
+ accepts: accepts?.value,
+ keyword: keyword?.value,
+ sort: [sort?.value as string].filter(Boolean),
+ });
+
+ total.value = data.total;
+ hasPrevious.value = data.hasPrevious;
+ hasNext.value = data.hasNext;
+
+ return data.items;
+ },
+ refetchInterval(data) {
+ const hasDeletingAttachment = data?.some(
+ (attachment) => !!attachment.metadata.deletionTimestamp
+ );
+ return hasDeletingAttachment ? 1000 : false;
+ },
+ });
+
+ const handleSelectPrevious = async () => {
+ if (!data.value) return;
+
+ const index = data.value?.findIndex(
+ (attachment) =>
+ attachment.metadata.name === selectedAttachment.value?.metadata.name
+ );
+
+ if (index === undefined) return;
+
+ if (index > 0) {
+ selectedAttachment.value = data.value[index - 1];
+ return;
+ }
+
+ if (index === 0 && hasPrevious.value) {
+ page.value--;
+ await nextTick();
+ await refetch();
+ selectedAttachment.value = data.value[data.value.length - 1];
+ }
+ };
+
+ const handleSelectNext = async () => {
+ if (!data.value) return;
+
+ const index = data.value?.findIndex(
+ (attachment) =>
+ attachment.metadata.name === selectedAttachment.value?.metadata.name
+ );
+
+ if (index === undefined) return;
+
+ if (index < data.value?.length - 1) {
+ selectedAttachment.value = data.value[index + 1];
+ return;
+ }
+
+ if (index === data.value.length - 1 && hasNext.value) {
+ page.value++;
+ await nextTick();
+ await refetch();
+ selectedAttachment.value = data.value[0];
+ }
+ };
+
+ const handleDeleteInBatch = () => {
+ Dialog.warning({
+ title: t("core.attachment.operations.delete_in_batch.title"),
+ description: t("core.common.dialog.descriptions.cannot_be_recovered"),
+ confirmType: "danger",
+ confirmText: t("core.common.buttons.confirm"),
+ cancelText: t("core.common.buttons.cancel"),
+ onConfirm: async () => {
+ try {
+ const promises = Array.from(selectedAttachments.value).map(
+ (attachment) => {
+ return coreApiClient.storage.attachment.deleteAttachment({
+ name: attachment.metadata.name,
+ });
+ }
+ );
+ await Promise.all(promises);
+ selectedAttachments.value.clear();
+
+ Toast.success(t("core.common.toast.delete_success"));
+ } catch (e) {
+ console.error("Failed to delete attachments", e);
+ } finally {
+ await refetch();
+ }
+ },
+ });
+ };
+
+ const handleCheckAll = (checkAll: boolean) => {
+ if (checkAll) {
+ data.value?.forEach((attachment) => {
+ selectedAttachments.value.add(attachment);
+ });
+ } else {
+ selectedAttachments.value.clear();
+ }
+ };
+
+ const handleSelect = async (attachment: Attachment | undefined) => {
+ if (!attachment) return;
+ if (selectedAttachments.value.has(attachment)) {
+ selectedAttachments.value.delete(attachment);
+ return;
+ }
+ selectedAttachments.value.add(attachment);
+ };
+
+ watch(
+ () => selectedAttachments.value.size,
+ (newValue) => {
+ checkedAll.value = newValue === data.value?.length;
+ }
+ );
+
+ const isChecked = (attachment: Attachment) => {
+ return (
+ attachment.metadata.name === selectedAttachment.value?.metadata.name ||
+ Array.from(selectedAttachments.value)
+ .map((item) => item.metadata.name)
+ .includes(attachment.metadata.name)
+ );
+ };
+
+ const handleReset = () => {
+ page.value = 1;
+ selectedAttachment.value = undefined;
+ selectedAttachments.value.clear();
+ checkedAll.value = false;
+ };
+
+ return {
+ attachments: data,
+ isLoading,
+ isFetching,
+ selectedAttachment,
+ selectedAttachments,
+ checkedAll,
+ total,
+ handleFetchAttachments: refetch,
+ handleSelectPrevious,
+ handleSelectNext,
+ handleDeleteInBatch,
+ handleCheckAll,
+ handleSelect,
+ isChecked,
+ handleReset,
+ };
+}
+
+export function useAttachmentPermalinkCopy(
+ attachment: Ref
+) {
+ const { copy } = useClipboard({ legacy: true });
+ const { t } = useI18n();
+
+ const mediaType = computed(() => {
+ return attachment.value?.spec.mediaType;
+ });
+
+ const isImage = computed(() => {
+ return mediaType.value && matchMediaType(mediaType.value, "image/*");
+ });
+
+ const isVideo = computed(() => {
+ return mediaType.value && matchMediaType(mediaType.value, "video/*");
+ });
+
+ const isAudio = computed(() => {
+ return mediaType.value && matchMediaType(mediaType.value, "audio/*");
+ });
+
+ const htmlText = computed(() => {
+ const { permalink } = attachment.value?.status || {};
+ const { displayName } = attachment.value?.spec || {};
+
+ if (isImage.value) {
+ return `
`;
+ } else if (isVideo.value) {
+ return ``;
+ } else if (isAudio.value) {
+ return ``;
+ }
+ return `${displayName}`;
+ });
+
+ const markdownText = computed(() => {
+ const { permalink } = attachment.value?.status || {};
+ const { displayName } = attachment.value?.spec || {};
+ if (isImage.value) {
+ return ``;
+ }
+ return `[${displayName}](${permalink})`;
+ });
+
+ const handleCopy = (format: "markdown" | "html" | "url") => {
+ const { permalink } = attachment.value?.status || {};
+
+ if (!permalink) return;
+
+ if (format === "url") {
+ copy(permalink);
+ } else if (format === "markdown") {
+ copy(markdownText.value);
+ } else if (format === "html") {
+ copy(htmlText.value);
+ }
+
+ Toast.success(t("core.common.toast.copy_success"));
+ };
+
+ return {
+ htmlText,
+ markdownText,
+ handleCopy,
+ };
+}
diff --git a/console-src/modules/contents/attachments/module.ts b/console-src/modules/contents/attachments/module.ts
new file mode 100644
index 0000000..1ef394b
--- /dev/null
+++ b/console-src/modules/contents/attachments/module.ts
@@ -0,0 +1,37 @@
+import BasicLayout from "@console/layouts/BasicLayout.vue";
+import { IconFolder } from "@halo-dev/components";
+import { definePlugin } from "@halo-dev/console-shared";
+import { markRaw } from "vue";
+import AttachmentList from "./AttachmentList.vue";
+import AttachmentSelectorModal from "./components/AttachmentSelectorModal.vue";
+
+export default definePlugin({
+ components: {
+ AttachmentSelectorModal,
+ },
+ routes: [
+ {
+ path: "/attachments",
+ name: "AttachmentsRoot",
+ component: BasicLayout,
+ meta: {
+ title: "core.attachment.title",
+ permissions: ["system:attachments:view"],
+ menu: {
+ name: "core.sidebar.menu.items.attachments",
+ group: "content",
+ icon: markRaw(IconFolder),
+ priority: 3,
+ mobile: true,
+ },
+ },
+ children: [
+ {
+ path: "",
+ name: "Attachments",
+ component: AttachmentList,
+ },
+ ],
+ },
+ ],
+});
diff --git a/console-src/modules/contents/comments/CommentList.vue b/console-src/modules/contents/comments/CommentList.vue
new file mode 100644
index 0000000..047be82
--- /dev/null
+++ b/console-src/modules/contents/comments/CommentList.vue
@@ -0,0 +1,413 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ $t(
+ "core.comment.operations.approve_comment_in_batch.button"
+ )
+ }}
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.refresh") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/comments/components/CommentListItem.vue b/console-src/modules/contents/comments/components/CommentListItem.vue
new file mode 100644
index 0000000..6927cac
--- /dev/null
+++ b/console-src/modules/contents/comments/components/CommentListItem.vue
@@ -0,0 +1,508 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ subjectRefResult.label }}
+
+ {{ subjectRefResult.title }}
+
+
+
+
+
+
+ {{ comment?.comment?.spec.content }}
+
+
+
+ {{
+ $t("core.comment.list.fields.reply_count", {
+ count: comment?.comment?.status?.replyCount || 0,
+ })
+ }}
+
+
+
+ {{ $t("core.comment.operations.reply.button") }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.comment.list.fields.pending_review") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ formatDatetime(
+ comment?.comment.spec.creationTime ||
+ comment?.comment.metadata.creationTimestamp
+ )
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.refresh") }}
+
+
+
+
+
+ {{ $t("core.comment.reply_empty.new") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/comments/components/ReplyCreationModal.vue b/console-src/modules/contents/comments/components/ReplyCreationModal.vue
new file mode 100644
index 0000000..06842c8
--- /dev/null
+++ b/console-src/modules/contents/comments/components/ReplyCreationModal.vue
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.cancel_and_shortcut") }}
+
+
+
+
+
diff --git a/console-src/modules/contents/comments/components/ReplyListItem.vue b/console-src/modules/contents/comments/components/ReplyListItem.vue
new file mode 100644
index 0000000..9ea0a7e
--- /dev/null
+++ b/console-src/modules/contents/comments/components/ReplyListItem.vue
@@ -0,0 +1,250 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.comment.list.fields.pending_review") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ formatDatetime(
+ reply?.reply?.spec.creationTime ||
+ reply?.reply.metadata.creationTimestamp
+ )
+ }}
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/comments/module.ts b/console-src/modules/contents/comments/module.ts
new file mode 100644
index 0000000..eee1b12
--- /dev/null
+++ b/console-src/modules/contents/comments/module.ts
@@ -0,0 +1,38 @@
+import BasicLayout from "@console/layouts/BasicLayout.vue";
+import { IconMessage } from "@halo-dev/components";
+import { definePlugin } from "@halo-dev/console-shared";
+import { markRaw } from "vue";
+import CommentList from "./CommentList.vue";
+import CommentStatsWidget from "./widgets/CommentStatsWidget.vue";
+
+export default definePlugin({
+ components: {
+ CommentStatsWidget,
+ },
+ routes: [
+ {
+ path: "/comments",
+ name: "CommentsRoot",
+ component: BasicLayout,
+ meta: {
+ title: "core.comment.title",
+ searchable: true,
+ permissions: ["system:comments:view"],
+ menu: {
+ name: "core.sidebar.menu.items.comments",
+ group: "content",
+ icon: markRaw(IconMessage),
+ priority: 2,
+ mobile: true,
+ },
+ },
+ children: [
+ {
+ path: "",
+ name: "Comments",
+ component: CommentList,
+ },
+ ],
+ },
+ ],
+});
diff --git a/console-src/modules/contents/comments/widgets/CommentStatsWidget.vue b/console-src/modules/contents/comments/widgets/CommentStatsWidget.vue
new file mode 100644
index 0000000..ef7afbb
--- /dev/null
+++ b/console-src/modules/contents/comments/widgets/CommentStatsWidget.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.dashboard.widgets.presets.comment_stats.title") }}
+
+
+ {{ stats?.approvedComments }}
+
+
+
+
+
+
diff --git a/console-src/modules/contents/pages/DeletedSinglePageList.vue b/console-src/modules/contents/pages/DeletedSinglePageList.vue
new file mode 100644
index 0000000..90a9a05
--- /dev/null
+++ b/console-src/modules/contents/pages/DeletedSinglePageList.vue
@@ -0,0 +1,402 @@
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.back") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.delete_permanently") }}
+
+
+ {{ $t("core.common.buttons.recovery") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.refresh") }}
+
+
+ {{ $t("core.common.buttons.back") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/pages/SinglePageEditor.vue b/console-src/modules/contents/pages/SinglePageEditor.vue
new file mode 100644
index 0000000..6287f2e
--- /dev/null
+++ b/console-src/modules/contents/pages/SinglePageEditor.vue
@@ -0,0 +1,546 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.page_editor.actions.snapshots") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.preview") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.save") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.setting") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.publish") }}
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/pages/SinglePageList.vue b/console-src/modules/contents/pages/SinglePageList.vue
new file mode 100644
index 0000000..77b25c0
--- /dev/null
+++ b/console-src/modules/contents/pages/SinglePageList.vue
@@ -0,0 +1,479 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.page.actions.recycle_bin") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.refresh") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/pages/SinglePageSnapshots.vue b/console-src/modules/contents/pages/SinglePageSnapshots.vue
new file mode 100644
index 0000000..3db7fd4
--- /dev/null
+++ b/console-src/modules/contents/pages/SinglePageSnapshots.vue
@@ -0,0 +1,182 @@
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.back") }}
+
+
+ {{ $t("core.page_snapshots.operations.cleanup.button") }}
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/pages/components/SinglePageListItem.vue b/console-src/modules/contents/pages/components/SinglePageListItem.vue
new file mode 100644
index 0000000..c6e5bf4
--- /dev/null
+++ b/console-src/modules/contents/pages/components/SinglePageListItem.vue
@@ -0,0 +1,258 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ $t("core.page.list.fields.visits", {
+ visits: singlePage.stats.visit || 0,
+ })
+ }}
+
+
+ {{
+ $t("core.page.list.fields.comments", {
+ comments: singlePage.stats.totalComment || 0,
+ })
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ formatDatetime(singlePage.page.spec.publishTime) }}
+
+
+
+
+
+
+ {{ $t("core.common.buttons.edit") }}
+
+
+ {{ $t("core.common.buttons.setting") }}
+
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
+
+
diff --git a/console-src/modules/contents/pages/components/SinglePageSettingModal.vue b/console-src/modules/contents/pages/components/SinglePageSettingModal.vue
new file mode 100644
index 0000000..97a1043
--- /dev/null
+++ b/console-src/modules/contents/pages/components/SinglePageSettingModal.vue
@@ -0,0 +1,490 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.page.settings.groups.general") }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.page.settings.groups.advanced") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.page.settings.groups.annotations") }}
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.publish") }}
+
+
+ {{ $t("core.common.buttons.save") }}
+
+
+ {{ $t("core.common.buttons.close") }}
+
+
+
+
+ {{ $t("core.common.buttons.cancel_publish") }}
+
+
+
+
+
diff --git a/console-src/modules/contents/pages/components/SnapshotContent.vue b/console-src/modules/contents/pages/components/SnapshotContent.vue
new file mode 100644
index 0000000..bd06e2a
--- /dev/null
+++ b/console-src/modules/contents/pages/components/SnapshotContent.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/pages/components/SnapshotListItem.vue b/console-src/modules/contents/pages/components/SnapshotListItem.vue
new file mode 100644
index 0000000..6d87275
--- /dev/null
+++ b/console-src/modules/contents/pages/components/SnapshotListItem.vue
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+ {{ relativeTimeTo(snapshot.metadata.creationTimestamp) }}
+
+
+
+ {{ $t("core.page_snapshots.status.released") }}
+
+
+ {{ $t("core.page_snapshots.status.draft") }}
+
+
+ {{ $t("core.page_snapshots.status.base") }}
+
+
+
+
+
+
+ {{ snapshot.spec.owner }}
+
+
+
+ {{ $t("core.page_snapshots.operations.revert.button") }}
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
+
+
+
diff --git a/console-src/modules/contents/pages/composables/use-page-update-mutate.ts b/console-src/modules/contents/pages/composables/use-page-update-mutate.ts
new file mode 100644
index 0000000..2cb219d
--- /dev/null
+++ b/console-src/modules/contents/pages/composables/use-page-update-mutate.ts
@@ -0,0 +1,40 @@
+import type { SinglePage } from "@halo-dev/api-client";
+import { coreApiClient } from "@halo-dev/api-client";
+import { Toast } from "@halo-dev/components";
+import { useMutation } from "@tanstack/vue-query";
+import { useI18n } from "vue-i18n";
+
+export function usePageUpdateMutate() {
+ const { t } = useI18n();
+ return useMutation({
+ mutationKey: ["singlePage-update"],
+ mutationFn: async (page: SinglePage) => {
+ const { data: latestSinglePage } =
+ await coreApiClient.content.singlePage.getSinglePage({
+ name: page.metadata.name,
+ });
+
+ return coreApiClient.content.singlePage.updateSinglePage(
+ {
+ name: page.metadata.name,
+ singlePage: {
+ ...latestSinglePage,
+ spec: page.spec,
+ metadata: {
+ ...latestSinglePage.metadata,
+ annotations: page.metadata.annotations,
+ },
+ },
+ },
+ {
+ mute: true,
+ }
+ );
+ },
+ retry: 3,
+ onError: (error) => {
+ console.error("Failed to update singlePage", error);
+ Toast.error(t("core.common.toast.server_internal_error"));
+ },
+ });
+}
diff --git a/console-src/modules/contents/pages/module.ts b/console-src/modules/contents/pages/module.ts
new file mode 100644
index 0000000..1c7ab04
--- /dev/null
+++ b/console-src/modules/contents/pages/module.ts
@@ -0,0 +1,72 @@
+import BasicLayout from "@console/layouts/BasicLayout.vue";
+import { IconPages } from "@halo-dev/components";
+import { definePlugin } from "@halo-dev/console-shared";
+import { markRaw } from "vue";
+import DeletedSinglePageList from "./DeletedSinglePageList.vue";
+import SinglePageEditor from "./SinglePageEditor.vue";
+import SinglePageList from "./SinglePageList.vue";
+import SinglePageSnapshots from "./SinglePageSnapshots.vue";
+import SinglePageStatsWidget from "./widgets/SinglePageStatsWidget.vue";
+
+export default definePlugin({
+ components: {
+ SinglePageStatsWidget,
+ },
+ routes: [
+ {
+ path: "/single-pages",
+ name: "SinglePagesRoot",
+ component: BasicLayout,
+ meta: {
+ title: "core.page.title",
+ searchable: true,
+ permissions: ["system:singlepages:view"],
+ menu: {
+ name: "core.sidebar.menu.items.single_pages",
+ group: "content",
+ icon: markRaw(IconPages),
+ priority: 1,
+ },
+ },
+ children: [
+ {
+ path: "",
+ name: "SinglePages",
+ component: SinglePageList,
+ },
+ {
+ path: "deleted",
+ name: "DeletedSinglePages",
+ component: DeletedSinglePageList,
+ meta: {
+ title: "core.deleted_page.title",
+ searchable: true,
+ permissions: ["system:singlepages:view"],
+ },
+ },
+ {
+ path: "editor",
+ name: "SinglePageEditor",
+ component: SinglePageEditor,
+ meta: {
+ title: "core.page_editor.title",
+ searchable: true,
+ hideFooter: true,
+ permissions: ["system:singlepages:manage"],
+ },
+ },
+ {
+ path: "snapshots",
+ name: "SinglePageSnapshots",
+ component: SinglePageSnapshots,
+ meta: {
+ title: "core.page_snapshots.title",
+ searchable: false,
+ hideFooter: true,
+ permissions: ["system:singlepages:manage"],
+ },
+ },
+ ],
+ },
+ ],
+});
diff --git a/console-src/modules/contents/pages/widgets/SinglePageStatsWidget.vue b/console-src/modules/contents/pages/widgets/SinglePageStatsWidget.vue
new file mode 100644
index 0000000..46dae2b
--- /dev/null
+++ b/console-src/modules/contents/pages/widgets/SinglePageStatsWidget.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.dashboard.widgets.presets.page_stats.title") }}
+
+
+ {{ total || 0 }}
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/DeletedPostList.vue b/console-src/modules/contents/posts/DeletedPostList.vue
new file mode 100644
index 0000000..29e50b2
--- /dev/null
+++ b/console-src/modules/contents/posts/DeletedPostList.vue
@@ -0,0 +1,423 @@
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.back") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.delete_permanently") }}
+
+
+ {{ $t("core.common.buttons.recovery") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.refresh") }}
+
+
+ {{ $t("core.common.buttons.back") }}
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.post.list.fields.categories") }}
+
+ {{ category.spec.displayName }}
+
+
+
+ {{
+ $t("core.post.list.fields.visits", {
+ visits: post.stats.visit,
+ })
+ }}
+
+
+ {{
+ $t("core.post.list.fields.comments", {
+ comments: post.stats.totalComment || 0,
+ })
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ formatDatetime(post.post.spec.publishTime) }}
+
+
+
+
+
+
+ {{ $t("core.common.buttons.delete_permanently") }}
+
+
+ {{ $t("core.common.buttons.recovery") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/PostEditor.vue b/console-src/modules/contents/posts/PostEditor.vue
new file mode 100644
index 0000000..f55527c
--- /dev/null
+++ b/console-src/modules/contents/posts/PostEditor.vue
@@ -0,0 +1,582 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.post_editor.actions.snapshots") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.preview") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.save") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.setting") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.publish") }}
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/PostList.vue b/console-src/modules/contents/posts/PostList.vue
new file mode 100644
index 0000000..01c36fa
--- /dev/null
+++ b/console-src/modules/contents/posts/PostList.vue
@@ -0,0 +1,607 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.post.actions.categories") }}
+
+
+ {{ $t("core.post.actions.tags") }}
+
+
+ {{ $t("core.post.actions.recycle_bin") }}
+
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.publish") }}
+
+
+ {{ $t("core.common.buttons.cancel_publish") }}
+
+
+ {{ $t("core.post.operations.batch_setting.button") }}
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.refresh") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/PostSnapshots.vue b/console-src/modules/contents/posts/PostSnapshots.vue
new file mode 100644
index 0000000..c6478bf
--- /dev/null
+++ b/console-src/modules/contents/posts/PostSnapshots.vue
@@ -0,0 +1,181 @@
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.back") }}
+
+
+ {{ $t("core.post_snapshots.operations.cleanup.button") }}
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/categories/CategoryList.vue b/console-src/modules/contents/posts/categories/CategoryList.vue
new file mode 100644
index 0000000..43ba0e8
--- /dev/null
+++ b/console-src/modules/contents/posts/categories/CategoryList.vue
@@ -0,0 +1,143 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
+
+
+
+
+
+
+ {{
+ $t("core.post_category.header.title", {
+ count: categories?.length || 0,
+ })
+ }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.refresh") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/categories/components/CategoryEditingModal.vue b/console-src/modules/contents/posts/categories/components/CategoryEditingModal.vue
new file mode 100644
index 0000000..6067169
--- /dev/null
+++ b/console-src/modules/contents/posts/categories/components/CategoryEditingModal.vue
@@ -0,0 +1,381 @@
+
+
+
+
+
+
+
+
+
+ {{ $t("core.post_category.editing_modal.groups.general") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.post_category.editing_modal.groups.annotations") }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.save_and_continue") }}
+
+
+
+ {{ $t("core.common.buttons.cancel_and_shortcut") }}
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/categories/components/CategoryListItem.vue b/console-src/modules/contents/posts/categories/components/CategoryListItem.vue
new file mode 100644
index 0000000..9c02542
--- /dev/null
+++ b/console-src/modules/contents/posts/categories/components/CategoryListItem.vue
@@ -0,0 +1,206 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ category.status.permalink }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ formatDatetime(category.metadata.creationTimestamp) }}
+
+
+
+
+
+
+ {{ $t("core.common.buttons.edit") }}
+
+
+ {{ $t("core.post_category.operations.add_sub_category.button") }}
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/categories/components/__tests__/CategoryEditingModal.spec.ts b/console-src/modules/contents/posts/categories/components/__tests__/CategoryEditingModal.spec.ts
new file mode 100644
index 0000000..2f856cb
--- /dev/null
+++ b/console-src/modules/contents/posts/categories/components/__tests__/CategoryEditingModal.spec.ts
@@ -0,0 +1,28 @@
+import messages from "@intlify/unplugin-vue-i18n/messages";
+import { mount } from "@vue/test-utils";
+import { createPinia, setActivePinia } from "pinia";
+import { beforeEach, describe, expect, it } from "vitest";
+import { createI18n } from "vue-i18n";
+import CategoryEditingModal from "../CategoryEditingModal.vue";
+
+describe("CategoryEditingModal", function () {
+ beforeEach(() => {
+ setActivePinia(createPinia());
+ });
+
+ it("should render", function () {
+ expect(
+ mount(CategoryEditingModal, {
+ global: {
+ plugins: [
+ createI18n({
+ legacy: false,
+ locale: "en",
+ messages,
+ }),
+ ],
+ },
+ })
+ ).toBeDefined();
+ });
+});
diff --git a/console-src/modules/contents/posts/categories/composables/use-post-category.ts b/console-src/modules/contents/posts/categories/composables/use-post-category.ts
new file mode 100644
index 0000000..eaf6a6d
--- /dev/null
+++ b/console-src/modules/contents/posts/categories/composables/use-post-category.ts
@@ -0,0 +1,52 @@
+import type { Category } from "@halo-dev/api-client";
+import { coreApiClient } from "@halo-dev/api-client";
+import { useQuery } from "@tanstack/vue-query";
+import type { Ref } from "vue";
+import { ref } from "vue";
+import type { CategoryTree } from "../utils";
+import { buildCategoriesTree } from "../utils";
+
+interface usePostCategoryReturn {
+ categories: Ref;
+ categoriesTree: Ref;
+ isLoading: Ref;
+ handleFetchCategories: () => void;
+}
+
+export function usePostCategory(): usePostCategoryReturn {
+ const categoriesTree = ref([] as CategoryTree[]);
+
+ const {
+ data: categories,
+ isLoading,
+ refetch,
+ } = useQuery({
+ queryKey: ["post-categories"],
+ queryFn: async () => {
+ const { data } = await coreApiClient.content.category.listCategory({
+ page: 0,
+ size: 0,
+ sort: ["metadata.creationTimestamp,desc"],
+ });
+
+ return data.items;
+ },
+ refetchInterval(data) {
+ const hasAbnormalCategory = data?.some(
+ (category) =>
+ !!category.metadata.deletionTimestamp || !category.status?.permalink
+ );
+ return hasAbnormalCategory ? 1000 : false;
+ },
+ onSuccess(data) {
+ categoriesTree.value = buildCategoriesTree(data);
+ },
+ });
+
+ return {
+ categories,
+ categoriesTree,
+ isLoading,
+ handleFetchCategories: refetch,
+ };
+}
diff --git a/console-src/modules/contents/posts/categories/utils/index.ts b/console-src/modules/contents/posts/categories/utils/index.ts
new file mode 100644
index 0000000..dc5863d
--- /dev/null
+++ b/console-src/modules/contents/posts/categories/utils/index.ts
@@ -0,0 +1,146 @@
+import type { Category, CategorySpec } from "@halo-dev/api-client";
+import { cloneDeep } from "lodash-es";
+
+export interface CategoryTreeSpec extends Omit {
+ children: CategoryTree[];
+}
+
+export interface CategoryTree extends Omit {
+ spec: CategoryTreeSpec;
+}
+
+export function buildCategoriesTree(categories: Category[]): CategoryTree[] {
+ const categoriesToUpdate = cloneDeep(categories);
+
+ const categoriesMap = {};
+ const parentMap = {};
+
+ categoriesToUpdate.forEach((category) => {
+ categoriesMap[category.metadata.name] = category;
+ // @ts-ignore
+ category.spec.children.forEach((child) => {
+ parentMap[child] = category.metadata.name;
+ });
+ // @ts-ignore
+ category.spec.children = [];
+ });
+
+ categoriesToUpdate.forEach((category) => {
+ const parentName = parentMap[category.metadata.name];
+ if (parentName && categoriesMap[parentName]) {
+ categoriesMap[parentName].spec.children.push(category);
+ }
+ });
+
+ const categoriesTree = categoriesToUpdate.filter(
+ (node) => parentMap[node.metadata.name] === undefined
+ );
+
+ return sortCategoriesTree(categoriesTree);
+}
+
+export function sortCategoriesTree(
+ categoriesTree: CategoryTree[] | Category[]
+): CategoryTree[] {
+ return categoriesTree
+ .sort((a, b) => {
+ if (a.spec.priority < b.spec.priority) {
+ return -1;
+ }
+ if (a.spec.priority > b.spec.priority) {
+ return 1;
+ }
+ return 0;
+ })
+ .map((category) => {
+ if (category.spec.children.length) {
+ return {
+ ...category,
+ spec: {
+ ...category.spec,
+ children: sortCategoriesTree(category.spec.children),
+ },
+ };
+ }
+ return category;
+ });
+}
+
+export function resetCategoriesTreePriority(
+ categoriesTree: CategoryTree[]
+): CategoryTree[] {
+ for (let i = 0; i < categoriesTree.length; i++) {
+ categoriesTree[i].spec.priority = i;
+ if (categoriesTree[i].spec.children) {
+ resetCategoriesTreePriority(categoriesTree[i].spec.children);
+ }
+ }
+ return categoriesTree;
+}
+
+export function convertTreeToCategories(categoriesTree: CategoryTree[]) {
+ const categories: Category[] = [];
+ const categoriesMap = new Map();
+ const convertCategory = (node: CategoryTree | undefined) => {
+ if (!node) {
+ return;
+ }
+ const children = node.spec.children || [];
+ categoriesMap.set(node.metadata.name, {
+ ...node,
+ spec: {
+ ...node.spec,
+ // @ts-ignore
+ children: children.map((child) => child.metadata.name),
+ },
+ });
+ children.forEach((child) => {
+ convertCategory(child);
+ });
+ };
+ categoriesTree.forEach((node) => {
+ convertCategory(node);
+ });
+ categoriesMap.forEach((node) => {
+ categories.push(node);
+ });
+ return categories;
+}
+
+export function convertCategoryTreeToCategory(
+ categoryTree: CategoryTree
+): Category {
+ const childNames = categoryTree.spec.children.map(
+ (child) => child.metadata.name
+ );
+ return {
+ ...categoryTree,
+ spec: {
+ ...categoryTree.spec,
+ children: childNames,
+ },
+ };
+}
+
+export const getCategoryPath = (
+ categories: CategoryTree[],
+ name: string,
+ path: CategoryTree[] = []
+): CategoryTree[] | undefined => {
+ for (const category of categories) {
+ if (category.metadata && category.metadata.name === name) {
+ return path.concat([category]);
+ }
+
+ if (category.spec && category.spec.children) {
+ const found = getCategoryPath(
+ category.spec.children,
+ name,
+ path.concat([category])
+ );
+ if (found) {
+ return found;
+ }
+ }
+ }
+};
diff --git a/console-src/modules/contents/posts/components/PostBatchSettingModal.vue b/console-src/modules/contents/posts/components/PostBatchSettingModal.vue
new file mode 100644
index 0000000..1b68e24
--- /dev/null
+++ b/console-src/modules/contents/posts/components/PostBatchSettingModal.vue
@@ -0,0 +1,338 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.save") }}
+
+
+ {{ $t("core.common.buttons.cancel") }}
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/components/PostListItem.vue b/console-src/modules/contents/posts/components/PostListItem.vue
new file mode 100644
index 0000000..01796f8
--- /dev/null
+++ b/console-src/modules/contents/posts/components/PostListItem.vue
@@ -0,0 +1,242 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/components/PostSettingModal.vue b/console-src/modules/contents/posts/components/PostSettingModal.vue
new file mode 100644
index 0000000..99bc246
--- /dev/null
+++ b/console-src/modules/contents/posts/components/PostSettingModal.vue
@@ -0,0 +1,520 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.post.settings.groups.general") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.post.settings.groups.advanced") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.post.settings.groups.annotations") }}
+
+
+
+
+
+
+
+
+
+
+ {{
+ isScheduledPublish
+ ? $t("core.common.buttons.schedule_publish")
+ : $t("core.common.buttons.publish")
+ }}
+
+
+ {{ $t("core.common.buttons.save") }}
+
+
+ {{ $t("core.common.buttons.close") }}
+
+
+
+
+ {{ $t("core.common.buttons.cancel_publish") }}
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/components/SnapshotContent.vue b/console-src/modules/contents/posts/components/SnapshotContent.vue
new file mode 100644
index 0000000..e8566d9
--- /dev/null
+++ b/console-src/modules/contents/posts/components/SnapshotContent.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/components/SnapshotListItem.vue b/console-src/modules/contents/posts/components/SnapshotListItem.vue
new file mode 100644
index 0000000..fb41df4
--- /dev/null
+++ b/console-src/modules/contents/posts/components/SnapshotListItem.vue
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+ {{ relativeTimeTo(snapshot.metadata.creationTimestamp) }}
+
+
+
+ {{ $t("core.post_snapshots.status.released") }}
+
+
+ {{ $t("core.post_snapshots.status.draft") }}
+
+
+ {{ $t("core.post_snapshots.status.base") }}
+
+
+
+
+
+
+ {{ snapshot.spec.owner }}
+
+
+
+ {{ $t("core.post_snapshots.operations.revert.button") }}
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/components/__tests__/PostSettingModal.spec.ts b/console-src/modules/contents/posts/components/__tests__/PostSettingModal.spec.ts
new file mode 100644
index 0000000..3e674d1
--- /dev/null
+++ b/console-src/modules/contents/posts/components/__tests__/PostSettingModal.spec.ts
@@ -0,0 +1,37 @@
+import messages from "@intlify/unplugin-vue-i18n/messages";
+import { VueQueryPlugin } from "@tanstack/vue-query";
+import { mount } from "@vue/test-utils";
+import { createPinia, setActivePinia } from "pinia";
+import { beforeEach, describe, expect, it } from "vitest";
+import { createI18n } from "vue-i18n";
+import PostSettingModal from "../PostSettingModal.vue";
+
+describe("PostSettingModal", () => {
+ beforeEach(() => {
+ setActivePinia(createPinia());
+ });
+
+ it("should render", () => {
+ const wrapper = mount(
+ {
+ components: {
+ PostSettingModal,
+ },
+ template: ``,
+ },
+ {
+ global: {
+ plugins: [
+ VueQueryPlugin,
+ createI18n({
+ legacy: false,
+ locale: "en",
+ messages,
+ }),
+ ],
+ },
+ }
+ );
+ expect(wrapper).toBeDefined();
+ });
+});
diff --git a/console-src/modules/contents/posts/components/entity-fields/ContributorsField.vue b/console-src/modules/contents/posts/components/entity-fields/ContributorsField.vue
new file mode 100644
index 0000000..c479415
--- /dev/null
+++ b/console-src/modules/contents/posts/components/entity-fields/ContributorsField.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/components/entity-fields/PublishStatusField.vue b/console-src/modules/contents/posts/components/entity-fields/PublishStatusField.vue
new file mode 100644
index 0000000..662afc3
--- /dev/null
+++ b/console-src/modules/contents/posts/components/entity-fields/PublishStatusField.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/components/entity-fields/PublishTimeField.vue b/console-src/modules/contents/posts/components/entity-fields/PublishTimeField.vue
new file mode 100644
index 0000000..74a13ed
--- /dev/null
+++ b/console-src/modules/contents/posts/components/entity-fields/PublishTimeField.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+ {{ formatDatetime(post.post.spec.publishTime) }}
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/components/entity-fields/TitleField.vue b/console-src/modules/contents/posts/components/entity-fields/TitleField.vue
new file mode 100644
index 0000000..df9bad2
--- /dev/null
+++ b/console-src/modules/contents/posts/components/entity-fields/TitleField.vue
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.post.list.fields.categories") }}
+
+ {{ category.spec.displayName }}
+
+
+
+ {{
+ $t("core.post.list.fields.visits", {
+ visits: post.stats.visit,
+ })
+ }}
+
+
+ {{
+ $t("core.post.list.fields.comments", {
+ comments: post.stats.totalComment || 0,
+ })
+ }}
+
+
+ {{ $t("core.post.list.fields.pinned") }}
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/components/entity-fields/VisibleField.vue b/console-src/modules/contents/posts/components/entity-fields/VisibleField.vue
new file mode 100644
index 0000000..06098b3
--- /dev/null
+++ b/console-src/modules/contents/posts/components/entity-fields/VisibleField.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/composables/use-post-update-mutate.ts b/console-src/modules/contents/posts/composables/use-post-update-mutate.ts
new file mode 100644
index 0000000..2716c75
--- /dev/null
+++ b/console-src/modules/contents/posts/composables/use-post-update-mutate.ts
@@ -0,0 +1,40 @@
+import type { Post } from "@halo-dev/api-client";
+import { coreApiClient } from "@halo-dev/api-client";
+import { Toast } from "@halo-dev/components";
+import { useMutation } from "@tanstack/vue-query";
+import { useI18n } from "vue-i18n";
+
+export function usePostUpdateMutate() {
+ const { t } = useI18n();
+
+ return useMutation({
+ mutationKey: ["post-update"],
+ mutationFn: async (post: Post) => {
+ const { data: latestPost } = await coreApiClient.content.post.getPost({
+ name: post.metadata.name,
+ });
+
+ return await coreApiClient.content.post.updatePost(
+ {
+ name: post.metadata.name,
+ post: {
+ ...latestPost,
+ spec: post.spec,
+ metadata: {
+ ...latestPost.metadata,
+ annotations: post.metadata.annotations,
+ },
+ },
+ },
+ {
+ mute: true,
+ }
+ );
+ },
+ retry: 3,
+ onError: (error) => {
+ console.error("Failed to update post", error);
+ Toast.error(t("core.common.toast.server_internal_error"));
+ },
+ });
+}
diff --git a/console-src/modules/contents/posts/module.ts b/console-src/modules/contents/posts/module.ts
new file mode 100644
index 0000000..6c391b3
--- /dev/null
+++ b/console-src/modules/contents/posts/module.ts
@@ -0,0 +1,110 @@
+import BasicLayout from "@console/layouts/BasicLayout.vue";
+import BlankLayout from "@console/layouts/BlankLayout.vue";
+import { IconBookRead } from "@halo-dev/components";
+import { definePlugin } from "@halo-dev/console-shared";
+import { markRaw } from "vue";
+import DeletedPostList from "./DeletedPostList.vue";
+import PostEditor from "./PostEditor.vue";
+import PostList from "./PostList.vue";
+import PostSnapshots from "./PostSnapshots.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";
+
+export default definePlugin({
+ components: {
+ PostStatsWidget,
+ RecentPublishedWidget,
+ },
+ routes: [
+ {
+ path: "/posts",
+ name: "PostsRoot",
+ component: BasicLayout,
+ meta: {
+ title: "core.post.title",
+ searchable: true,
+ permissions: ["system:posts:view"],
+ menu: {
+ name: "core.sidebar.menu.items.posts",
+ group: "content",
+ icon: markRaw(IconBookRead),
+ priority: 0,
+ mobile: true,
+ },
+ },
+ children: [
+ {
+ path: "",
+ name: "Posts",
+ component: PostList,
+ },
+ {
+ path: "deleted",
+ name: "DeletedPosts",
+ component: DeletedPostList,
+ meta: {
+ title: "core.deleted_post.title",
+ searchable: true,
+ permissions: ["system:posts:view"],
+ },
+ },
+ {
+ path: "editor",
+ name: "PostEditor",
+ component: PostEditor,
+ meta: {
+ title: "core.post_editor.title",
+ searchable: true,
+ hideFooter: true,
+ permissions: ["system:posts:manage"],
+ },
+ },
+ {
+ path: "snapshots",
+ name: "PostSnapshots",
+ component: PostSnapshots,
+ meta: {
+ title: "core.post_snapshots.title",
+ searchable: false,
+ hideFooter: true,
+ permissions: ["system:posts:manage"],
+ },
+ },
+ {
+ path: "categories",
+ component: BlankLayout,
+ children: [
+ {
+ path: "",
+ name: "Categories",
+ component: CategoryList,
+ meta: {
+ title: "core.post_category.title",
+ searchable: true,
+ permissions: ["system:posts:view"],
+ },
+ },
+ ],
+ },
+ {
+ path: "tags",
+ component: BlankLayout,
+ children: [
+ {
+ path: "",
+ name: "Tags",
+ component: TagList,
+ meta: {
+ title: "core.post_tag.title",
+ searchable: true,
+ permissions: ["system:posts:view"],
+ },
+ },
+ ],
+ },
+ ],
+ },
+ ],
+});
diff --git a/console-src/modules/contents/posts/tags/TagList.vue b/console-src/modules/contents/posts/tags/TagList.vue
new file mode 100644
index 0000000..6391286
--- /dev/null
+++ b/console-src/modules/contents/posts/tags/TagList.vue
@@ -0,0 +1,319 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ handleFetchTags">
+ {{ $t("core.common.buttons.refresh") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/tags/components/PostTag.vue b/console-src/modules/contents/posts/tags/components/PostTag.vue
new file mode 100644
index 0000000..b74382a
--- /dev/null
+++ b/console-src/modules/contents/posts/tags/components/PostTag.vue
@@ -0,0 +1,57 @@
+
+
+
+ {{ tag.spec.displayName }}
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/tags/components/TagEditingModal.vue b/console-src/modules/contents/posts/tags/components/TagEditingModal.vue
new file mode 100644
index 0000000..a7b6378
--- /dev/null
+++ b/console-src/modules/contents/posts/tags/components/TagEditingModal.vue
@@ -0,0 +1,290 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.post_tag.editing_modal.groups.general") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.post_tag.editing_modal.groups.annotations") }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.save_and_continue") }}
+
+
+
+ {{ $t("core.common.buttons.cancel_and_shortcut") }}
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/tags/components/TagListItem.vue b/console-src/modules/contents/posts/tags/components/TagListItem.vue
new file mode 100644
index 0000000..7282633
--- /dev/null
+++ b/console-src/modules/contents/posts/tags/components/TagListItem.vue
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ tag.status.permalink }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ formatDatetime(tag.metadata.creationTimestamp) }}
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.edit") }}
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/tags/composables/use-post-tag.ts b/console-src/modules/contents/posts/tags/composables/use-post-tag.ts
new file mode 100644
index 0000000..446926a
--- /dev/null
+++ b/console-src/modules/contents/posts/tags/composables/use-post-tag.ts
@@ -0,0 +1,138 @@
+import type { Tag } from "@halo-dev/api-client";
+import { consoleApiClient, coreApiClient } from "@halo-dev/api-client";
+import { Dialog, Toast } from "@halo-dev/components";
+import { useQuery, type QueryObserverResult } from "@tanstack/vue-query";
+import { ref, watch, type Ref } from "vue";
+import { useI18n } from "vue-i18n";
+
+interface usePostTagReturn {
+ tags: Ref;
+ total: Ref;
+ hasPrevious: Ref;
+ hasNext: Ref;
+ isLoading: Ref;
+ isFetching: Ref;
+ handleFetchTags: () => Promise>;
+ handleDelete: (tag: Tag) => void;
+ handleDeleteInBatch: (tagNames: string[]) => Promise;
+}
+
+export function usePostTag(filterOptions?: {
+ sort?: Ref;
+ page?: Ref;
+ size?: Ref;
+ keyword?: Ref;
+}): usePostTagReturn {
+ const { t } = useI18n();
+
+ const { sort, page, size, keyword } = filterOptions || {};
+
+ const total = ref(0);
+ const hasPrevious = ref(false);
+ const hasNext = ref(false);
+
+ const {
+ data: tags,
+ isLoading,
+ isFetching,
+ refetch,
+ } = useQuery({
+ queryKey: ["post-tags", sort, page, size, keyword],
+ queryFn: async () => {
+ const { data } = await consoleApiClient.content.tag.listPostTags({
+ page: page?.value || 0,
+ size: size?.value || 0,
+ sort: [sort?.value as string].filter(Boolean) || [
+ "metadata.creationTimestamp,desc",
+ ],
+ keyword: keyword?.value,
+ });
+
+ total.value = data.total;
+ hasPrevious.value = data.hasPrevious;
+ hasNext.value = data.hasNext;
+
+ return data.items;
+ },
+ refetchInterval(data) {
+ const abnormalTags = data?.filter(
+ (tag) => !!tag.metadata.deletionTimestamp || !tag.status?.permalink
+ );
+ return abnormalTags?.length ? 1000 : false;
+ },
+ });
+
+ const handleDelete = async (tag: Tag) => {
+ Dialog.warning({
+ title: t("core.post_tag.operations.delete.title"),
+ description: t("core.post_tag.operations.delete.description"),
+ confirmType: "danger",
+ confirmText: t("core.common.buttons.confirm"),
+ cancelText: t("core.common.buttons.cancel"),
+ onConfirm: async () => {
+ try {
+ await coreApiClient.content.tag.deleteTag({
+ name: tag.metadata.name,
+ });
+
+ Toast.success(t("core.common.toast.delete_success"));
+ } catch (e) {
+ console.error("Failed to delete tag", e);
+ } finally {
+ await refetch();
+ }
+ },
+ });
+ };
+
+ const handleDeleteInBatch = (tagNames: string[]) => {
+ return new Promise((resolve) => {
+ Dialog.warning({
+ title: t("core.post_tag.operations.delete_in_batch.title"),
+ description: t("core.common.dialog.descriptions.cannot_be_recovered"),
+ confirmType: "danger",
+ confirmText: t("core.common.buttons.confirm"),
+ cancelText: t("core.common.buttons.cancel"),
+ onConfirm: async () => {
+ try {
+ await Promise.all(
+ tagNames.map((tagName) => {
+ coreApiClient.content.tag.deleteTag({
+ name: tagName,
+ });
+ })
+ );
+
+ Toast.success(t("core.common.toast.delete_success"));
+ resolve();
+ } catch (e) {
+ console.error("Failed to delete tags in batch", e);
+ } finally {
+ await refetch();
+ }
+ },
+ });
+ });
+ };
+
+ watch(
+ () => [sort?.value, keyword?.value],
+ () => {
+ if (page?.value) {
+ page.value = 1;
+ }
+ }
+ );
+
+ return {
+ tags,
+ total,
+ hasPrevious,
+ hasNext,
+ isLoading,
+ isFetching,
+ handleFetchTags: refetch,
+ handleDelete,
+ handleDeleteInBatch,
+ };
+}
diff --git a/console-src/modules/contents/posts/widgets/PostStatsWidget.vue b/console-src/modules/contents/posts/widgets/PostStatsWidget.vue
new file mode 100644
index 0000000..ac86c01
--- /dev/null
+++ b/console-src/modules/contents/posts/widgets/PostStatsWidget.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.dashboard.widgets.presets.post_stats.title") }}
+
+
+ {{ stats?.posts || 0 }}
+
+
+
+
+
+
diff --git a/console-src/modules/contents/posts/widgets/RecentPublishedWidget.vue b/console-src/modules/contents/posts/widgets/RecentPublishedWidget.vue
new file mode 100644
index 0000000..50fe26a
--- /dev/null
+++ b/console-src/modules/contents/posts/widgets/RecentPublishedWidget.vue
@@ -0,0 +1,105 @@
+
+
+
+
+
+ -
+
+
+
+
+
+
+ {{
+ $t(
+ "core.dashboard.widgets.presets.recent_published.visits",
+ { visits: post.stats.visit || 0 }
+ )
+ }}
+
+
+ {{
+ $t(
+ "core.dashboard.widgets.presets.recent_published.comments",
+ { comments: post.stats.totalComment || 0 }
+ )
+ }}
+
+
+ {{
+ $t(
+ "core.dashboard.widgets.presets.recent_published.publishTime",
+ {
+ publishTime: formatDatetime(
+ post.post.spec.publishTime
+ ),
+ }
+ )
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/dashboard/Dashboard.vue b/console-src/modules/dashboard/Dashboard.vue
new file mode 100644
index 0000000..eb201d3
--- /dev/null
+++ b/console-src/modules/dashboard/Dashboard.vue
@@ -0,0 +1,288 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.dashboard.actions.add_widget") }}
+
+
+
+
+
+
+ {{
+ settings
+ ? $t("core.dashboard.actions.done")
+ : $t("core.dashboard.actions.setting")
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/dashboard/module.ts b/console-src/modules/dashboard/module.ts
new file mode 100644
index 0000000..a33977d
--- /dev/null
+++ b/console-src/modules/dashboard/module.ts
@@ -0,0 +1,41 @@
+import BasicLayout from "@console/layouts/BasicLayout.vue";
+import { IconDashboard } from "@halo-dev/components";
+import { definePlugin } from "@halo-dev/console-shared";
+import Dashboard from "./Dashboard.vue";
+
+import { markRaw } from "vue";
+import QuickLinkWidget from "./widgets/QuickLinkWidget.vue";
+import ViewsStatsWidget from "./widgets/ViewsStatsWidget.vue";
+
+export default definePlugin({
+ components: {
+ QuickLinkWidget,
+ ViewsStatsWidget,
+ },
+ routes: [
+ {
+ path: "/",
+ component: BasicLayout,
+ name: "Root",
+ redirect: "/dashboard",
+ children: [
+ {
+ path: "dashboard",
+ name: "Dashboard",
+ component: Dashboard,
+ meta: {
+ title: "core.dashboard.title",
+ searchable: true,
+ menu: {
+ name: "core.sidebar.menu.items.dashboard",
+ group: "dashboard",
+ icon: markRaw(IconDashboard),
+ priority: 0,
+ mobile: true,
+ },
+ },
+ },
+ ],
+ },
+ ],
+});
diff --git a/console-src/modules/dashboard/widgets/QuickLinkWidget.vue b/console-src/modules/dashboard/widgets/QuickLinkWidget.vue
new file mode 100644
index 0000000..f1530c1
--- /dev/null
+++ b/console-src/modules/dashboard/widgets/QuickLinkWidget.vue
@@ -0,0 +1,210 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ action.title }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/dashboard/widgets/ViewsStatsWidget.vue b/console-src/modules/dashboard/widgets/ViewsStatsWidget.vue
new file mode 100644
index 0000000..7fc3da2
--- /dev/null
+++ b/console-src/modules/dashboard/widgets/ViewsStatsWidget.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.dashboard.widgets.presets.views_stats.title") }}
+
+
+ {{ stats?.visits || 0 }}
+
+
+
+
+
+
diff --git a/console-src/modules/index.ts b/console-src/modules/index.ts
new file mode 100644
index 0000000..9e8177a
--- /dev/null
+++ b/console-src/modules/index.ts
@@ -0,0 +1,10 @@
+import type { PluginModule } from "@halo-dev/console-shared";
+
+const modules = Object.values(
+ import.meta.glob("./**/module.ts", {
+ eager: true,
+ import: "default",
+ })
+) as PluginModule[];
+
+export default modules;
diff --git a/console-src/modules/interface/menus/Menus.vue b/console-src/modules/interface/menus/Menus.vue
new file mode 100644
index 0000000..40e7b2a
--- /dev/null
+++ b/console-src/modules/interface/menus/Menus.vue
@@ -0,0 +1,290 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ selectedMenu?.spec.displayName }}
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.refresh") }}
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/console-src/modules/interface/menus/components/MenuEditingModal.vue b/console-src/modules/interface/menus/components/MenuEditingModal.vue
new file mode 100644
index 0000000..5f487f1
--- /dev/null
+++ b/console-src/modules/interface/menus/components/MenuEditingModal.vue
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.cancel_and_shortcut") }}
+
+
+
+
+
diff --git a/console-src/modules/interface/menus/components/MenuItemEditingModal.vue b/console-src/modules/interface/menus/components/MenuItemEditingModal.vue
new file mode 100644
index 0000000..b9d74e8
--- /dev/null
+++ b/console-src/modules/interface/menus/components/MenuItemEditingModal.vue
@@ -0,0 +1,392 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.menu.menu_item_editing_modal.groups.annotations") }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.cancel_and_shortcut") }}
+
+
+
+
+
diff --git a/console-src/modules/interface/menus/components/MenuItemListItem.vue b/console-src/modules/interface/menus/components/MenuItemListItem.vue
new file mode 100644
index 0000000..9d0543a
--- /dev/null
+++ b/console-src/modules/interface/menus/components/MenuItemListItem.vue
@@ -0,0 +1,144 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ getMenuItemRefDisplayName(menuItem) }}
+
+
+
+
+ {{ menuItem.status.href }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.edit") }}
+
+
+ {{ $t("core.menu.operations.add_sub_menu_item.button") }}
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
+
+
+
+
+
diff --git a/console-src/modules/interface/menus/components/MenuList.vue b/console-src/modules/interface/menus/components/MenuList.vue
new file mode 100644
index 0000000..cd4fb1e
--- /dev/null
+++ b/console-src/modules/interface/menus/components/MenuList.vue
@@ -0,0 +1,266 @@
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.refresh") }}
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+ {{ $t("core.menu.list.fields.primary") }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("core.menu.operations.set_primary.button") }}
+
+
+ {{ $t("core.common.buttons.edit") }}
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.new") }}
+
+
+
+
diff --git a/console-src/modules/interface/menus/module.ts b/console-src/modules/interface/menus/module.ts
new file mode 100644
index 0000000..ddfbddd
--- /dev/null
+++ b/console-src/modules/interface/menus/module.ts
@@ -0,0 +1,34 @@
+import BasicLayout from "@console/layouts/BasicLayout.vue";
+import { IconListSettings } from "@halo-dev/components";
+import { definePlugin } from "@halo-dev/console-shared";
+import { markRaw } from "vue";
+import Menus from "./Menus.vue";
+
+export default definePlugin({
+ components: {},
+ routes: [
+ {
+ path: "/menus",
+ name: "MenusRoot",
+ component: BasicLayout,
+ meta: {
+ title: "core.menu.title",
+ searchable: true,
+ permissions: ["system:menus:view"],
+ menu: {
+ name: "core.sidebar.menu.items.menus",
+ group: "interface",
+ icon: markRaw(IconListSettings),
+ priority: 1,
+ },
+ },
+ children: [
+ {
+ path: "",
+ name: "Menus",
+ component: Menus,
+ },
+ ],
+ },
+ ],
+});
diff --git a/console-src/modules/interface/menus/utils/__tests__/__snapshots__/index.spec.ts.snap b/console-src/modules/interface/menus/utils/__tests__/__snapshots__/index.spec.ts.snap
new file mode 100644
index 0000000..9a55b39
--- /dev/null
+++ b/console-src/modules/interface/menus/utils/__tests__/__snapshots__/index.spec.ts.snap
@@ -0,0 +1,342 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`buildMenuItemsTree > should match snapshot 1`] = `
+[
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:22:03.377364Z",
+ "name": "411a3639-bf0d-4266-9cfb-14184259dab5",
+ "version": 1,
+ },
+ "spec": {
+ "children": [],
+ "displayName": "首页",
+ "href": "https://ryanc.cc/",
+ "priority": 0,
+ },
+ },
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:19:37.252228Z",
+ "name": "40e4ba86-5c7e-43c2-b4c0-cee376d26564",
+ "version": 12,
+ },
+ "spec": {
+ "children": [
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-07-28T06:50:32.777556Z",
+ "name": "caeef383-3828-4039-9114-6f9ad3b4a37e",
+ "version": 4,
+ },
+ "spec": {
+ "children": [],
+ "displayName": "Halo",
+ "href": "https://ryanc.cc/categories/halo",
+ "priority": 0,
+ },
+ },
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:22:03.377364Z",
+ "name": "ded1943d-9fdb-4563-83ee-2f04364872e0",
+ "version": 1,
+ },
+ "spec": {
+ "children": [
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:22:03.377364Z",
+ "name": "96b636bb-3e4a-44d1-8ea7-f9da9e876f45",
+ "version": 1,
+ },
+ "spec": {
+ "children": [],
+ "displayName": "Spring Boot",
+ "href": "https://ryanc.cc/categories/spring-boot",
+ "priority": 0,
+ },
+ },
+ ],
+ "displayName": "Java",
+ "href": "https://ryanc.cc/categories/java",
+ "priority": 1,
+ },
+ },
+ ],
+ "displayName": "文章分类",
+ "href": "https://ryanc.cc/categories",
+ "priority": 1,
+ },
+ },
+]
+`;
+
+exports[`convertMenuTreeItemToMenuItem > should match snapshot 1`] = `
+{
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:19:37.252228Z",
+ "name": "40e4ba86-5c7e-43c2-b4c0-cee376d26564",
+ "version": 12,
+ },
+ "spec": {
+ "children": [
+ "caeef383-3828-4039-9114-6f9ad3b4a37e",
+ "ded1943d-9fdb-4563-83ee-2f04364872e0",
+ ],
+ "displayName": "文章分类",
+ "href": "https://ryanc.cc/categories",
+ "priority": 1,
+ },
+}
+`;
+
+exports[`convertMenuTreeItemToMenuItem > should match snapshot 2`] = `
+{
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:22:03.377364Z",
+ "name": "ded1943d-9fdb-4563-83ee-2f04364872e0",
+ "version": 1,
+ },
+ "spec": {
+ "children": [
+ "96b636bb-3e4a-44d1-8ea7-f9da9e876f45",
+ ],
+ "displayName": "Java",
+ "href": "https://ryanc.cc/categories/java",
+ "priority": 1,
+ },
+}
+`;
+
+exports[`convertTreeToMenuItems > will match snapshot 1`] = `
+[
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:22:03.377364Z",
+ "name": "411a3639-bf0d-4266-9cfb-14184259dab5",
+ "version": 1,
+ },
+ "spec": {
+ "children": [],
+ "displayName": "首页",
+ "href": "https://ryanc.cc/",
+ "priority": 0,
+ },
+ },
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:19:37.252228Z",
+ "name": "40e4ba86-5c7e-43c2-b4c0-cee376d26564",
+ "version": 12,
+ },
+ "spec": {
+ "children": [
+ "caeef383-3828-4039-9114-6f9ad3b4a37e",
+ "ded1943d-9fdb-4563-83ee-2f04364872e0",
+ ],
+ "displayName": "文章分类",
+ "href": "https://ryanc.cc/categories",
+ "priority": 1,
+ },
+ },
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-07-28T06:50:32.777556Z",
+ "name": "caeef383-3828-4039-9114-6f9ad3b4a37e",
+ "version": 4,
+ },
+ "spec": {
+ "children": [],
+ "displayName": "Halo",
+ "href": "https://ryanc.cc/categories/halo",
+ "priority": 0,
+ },
+ },
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:22:03.377364Z",
+ "name": "ded1943d-9fdb-4563-83ee-2f04364872e0",
+ "version": 1,
+ },
+ "spec": {
+ "children": [
+ "96b636bb-3e4a-44d1-8ea7-f9da9e876f45",
+ ],
+ "displayName": "Java",
+ "href": "https://ryanc.cc/categories/java",
+ "priority": 1,
+ },
+ },
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:22:03.377364Z",
+ "name": "96b636bb-3e4a-44d1-8ea7-f9da9e876f45",
+ "version": 1,
+ },
+ "spec": {
+ "children": [],
+ "displayName": "Spring Boot",
+ "href": "https://ryanc.cc/categories/spring-boot",
+ "priority": 0,
+ },
+ },
+]
+`;
+
+exports[`resetMenuItemsTreePriority > should match snapshot 1`] = `
+[
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:22:03.377364Z",
+ "name": "411a3639-bf0d-4266-9cfb-14184259dab5",
+ "version": 1,
+ },
+ "spec": {
+ "children": [],
+ "displayName": "首页",
+ "href": "https://ryanc.cc/",
+ "priority": 0,
+ },
+ },
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:19:37.252228Z",
+ "name": "40e4ba86-5c7e-43c2-b4c0-cee376d26564",
+ "version": 12,
+ },
+ "spec": {
+ "children": [
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-07-28T06:50:32.777556Z",
+ "name": "caeef383-3828-4039-9114-6f9ad3b4a37e",
+ "version": 4,
+ },
+ "spec": {
+ "children": [],
+ "displayName": "Halo",
+ "href": "https://ryanc.cc/categories/halo",
+ "priority": 0,
+ },
+ },
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:22:03.377364Z",
+ "name": "ded1943d-9fdb-4563-83ee-2f04364872e0",
+ "version": 1,
+ },
+ "spec": {
+ "children": [
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:22:03.377364Z",
+ "name": "96b636bb-3e4a-44d1-8ea7-f9da9e876f45",
+ "version": 1,
+ },
+ "spec": {
+ "children": [],
+ "displayName": "Spring Boot",
+ "href": "https://ryanc.cc/categories/spring-boot",
+ "priority": 0,
+ },
+ },
+ ],
+ "displayName": "Java",
+ "href": "https://ryanc.cc/categories/java",
+ "priority": 1,
+ },
+ },
+ ],
+ "displayName": "文章分类",
+ "href": "https://ryanc.cc/categories",
+ "priority": 1,
+ },
+ },
+]
+`;
+
+exports[`sortMenuItemsTree > will match snapshot 1`] = `
+[
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:19:37.252228Z",
+ "name": "40e4ba86-5c7e-43c2-b4c0-cee376d26564",
+ "version": 12,
+ },
+ "spec": {
+ "children": [
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-08-05T04:22:03.377364Z",
+ "name": "ded1943d-9fdb-4563-83ee-2f04364872e0",
+ "version": 0,
+ },
+ "spec": {
+ "children": [],
+ "displayName": "Java",
+ "href": "https://ryanc.cc/categories/java",
+ "priority": 0,
+ },
+ },
+ {
+ "apiVersion": "v1alpha1",
+ "kind": "MenuItem",
+ "metadata": {
+ "creationTimestamp": "2022-07-28T06:50:32.777556Z",
+ "name": "caeef383-3828-4039-9114-6f9ad3b4a37e",
+ "version": 4,
+ },
+ "spec": {
+ "children": [],
+ "displayName": "Halo",
+ "href": "https://ryanc.cc/categories/halo",
+ "priority": 1,
+ },
+ },
+ ],
+ "displayName": "文章分类",
+ "href": "https://ryanc.cc/categories",
+ "priority": 0,
+ },
+ },
+]
+`;
diff --git a/console-src/modules/interface/menus/utils/__tests__/index.spec.ts b/console-src/modules/interface/menus/utils/__tests__/index.spec.ts
new file mode 100644
index 0000000..833edf8
--- /dev/null
+++ b/console-src/modules/interface/menus/utils/__tests__/index.spec.ts
@@ -0,0 +1,226 @@
+import type { MenuItem } from "@halo-dev/api-client";
+import { describe, expect, it } from "vitest";
+import type { MenuTreeItem } from "../index";
+import {
+ buildMenuItemsTree,
+ convertMenuTreeItemToMenuItem,
+ convertTreeToMenuItems,
+ getChildrenNames,
+ resetMenuItemsTreePriority,
+ sortMenuItemsTree,
+} from "../index";
+
+const rawMenuItems: MenuItem[] = [
+ {
+ spec: {
+ displayName: "文章分类",
+ href: "https://ryanc.cc/categories",
+ children: [
+ "caeef383-3828-4039-9114-6f9ad3b4a37e",
+ "ded1943d-9fdb-4563-83ee-2f04364872e0",
+ ],
+ priority: 1,
+ },
+ apiVersion: "v1alpha1",
+ kind: "MenuItem",
+ metadata: {
+ name: "40e4ba86-5c7e-43c2-b4c0-cee376d26564",
+ version: 12,
+ creationTimestamp: "2022-08-05T04:19:37.252228Z",
+ },
+ },
+ {
+ spec: {
+ displayName: "Halo",
+ href: "https://ryanc.cc/categories/halo",
+ children: [],
+ priority: 0,
+ },
+ apiVersion: "v1alpha1",
+ kind: "MenuItem",
+ metadata: {
+ name: "caeef383-3828-4039-9114-6f9ad3b4a37e",
+ version: 4,
+ creationTimestamp: "2022-07-28T06:50:32.777556Z",
+ },
+ },
+ {
+ spec: {
+ displayName: "Java",
+ href: "https://ryanc.cc/categories/java",
+ children: ["96b636bb-3e4a-44d1-8ea7-f9da9e876f45"],
+ priority: 1,
+ },
+ apiVersion: "v1alpha1",
+ kind: "MenuItem",
+ metadata: {
+ name: "ded1943d-9fdb-4563-83ee-2f04364872e0",
+ version: 1,
+ creationTimestamp: "2022-08-05T04:22:03.377364Z",
+ },
+ },
+ {
+ spec: {
+ displayName: "Spring Boot",
+ href: "https://ryanc.cc/categories/spring-boot",
+ children: [],
+ priority: 0,
+ },
+ apiVersion: "v1alpha1",
+ kind: "MenuItem",
+ metadata: {
+ name: "96b636bb-3e4a-44d1-8ea7-f9da9e876f45",
+ version: 1,
+ creationTimestamp: "2022-08-05T04:22:03.377364Z",
+ },
+ },
+ {
+ spec: {
+ displayName: "首页",
+ href: "https://ryanc.cc/",
+ children: [],
+ priority: 0,
+ },
+ apiVersion: "v1alpha1",
+ kind: "MenuItem",
+ metadata: {
+ name: "411a3639-bf0d-4266-9cfb-14184259dab5",
+ version: 1,
+ creationTimestamp: "2022-08-05T04:22:03.377364Z",
+ },
+ },
+];
+
+describe("buildMenuItemsTree", () => {
+ it("should match snapshot", () => {
+ expect(buildMenuItemsTree(rawMenuItems)).toMatchSnapshot();
+ });
+
+ it("should be sorted correctly", () => {
+ const menuItems = buildMenuItemsTree(rawMenuItems);
+ expect(menuItems[0].spec.priority).toBe(0);
+ expect(menuItems[1].spec.priority).toBe(1);
+
+ // children should be sorted
+ expect(menuItems[1].spec.children[0].spec.priority).toBe(0);
+ expect(menuItems[1].spec.children[1].spec.priority).toBe(1);
+ expect(menuItems[1].spec.children[1].spec.children[0].spec.priority).toBe(
+ 0
+ );
+
+ expect(menuItems[0].spec.displayName).toBe("首页");
+ expect(menuItems[1].spec.displayName).toBe("文章分类");
+ expect(menuItems[1].spec.children[0].spec.displayName).toBe("Halo");
+ expect(menuItems[1].spec.children[1].spec.displayName).toBe("Java");
+ expect(
+ menuItems[1].spec.children[1].spec.children[0].spec.displayName
+ ).toBe("Spring Boot");
+ });
+});
+
+describe("convertTreeToMenuItems", () => {
+ it("will match snapshot", function () {
+ const menuTreeItems = buildMenuItemsTree(rawMenuItems);
+ expect(convertTreeToMenuItems(menuTreeItems)).toMatchSnapshot();
+ });
+});
+
+describe("sortMenuItemsTree", () => {
+ it("will match snapshot", () => {
+ const tree: MenuTreeItem[] = [
+ {
+ apiVersion: "v1alpha1",
+ kind: "MenuItem",
+ metadata: {
+ creationTimestamp: "2022-08-05T04:19:37.252228Z",
+ name: "40e4ba86-5c7e-43c2-b4c0-cee376d26564",
+ version: 12,
+ },
+ spec: {
+ children: [
+ {
+ apiVersion: "v1alpha1",
+ kind: "MenuItem",
+ metadata: {
+ creationTimestamp: "2022-07-28T06:50:32.777556Z",
+ name: "caeef383-3828-4039-9114-6f9ad3b4a37e",
+ version: 4,
+ },
+ spec: {
+ children: [],
+ priority: 1,
+ displayName: "Halo",
+ href: "https://ryanc.cc/categories/halo",
+ },
+ },
+ {
+ apiVersion: "v1alpha1",
+ kind: "MenuItem",
+ metadata: {
+ creationTimestamp: "2022-08-05T04:22:03.377364Z",
+ name: "ded1943d-9fdb-4563-83ee-2f04364872e0",
+ version: 0,
+ },
+ spec: {
+ children: [],
+ priority: 0,
+ displayName: "Java",
+ href: "https://ryanc.cc/categories/java",
+ },
+ },
+ ],
+ priority: 0,
+ displayName: "文章分类",
+ href: "https://ryanc.cc/categories",
+ },
+ },
+ ];
+
+ expect(sortMenuItemsTree(tree)).toMatchSnapshot();
+ });
+});
+
+describe("resetMenuItemsTreePriority", () => {
+ it("should match snapshot", function () {
+ expect(
+ resetMenuItemsTreePriority(buildMenuItemsTree(rawMenuItems))
+ ).toMatchSnapshot();
+ });
+});
+
+describe("getChildrenNames", () => {
+ it("should return correct names", () => {
+ const menuTreeItems = buildMenuItemsTree(rawMenuItems);
+ expect(getChildrenNames(menuTreeItems[0])).toEqual([]);
+ expect(getChildrenNames(menuTreeItems[1])).toEqual([
+ "caeef383-3828-4039-9114-6f9ad3b4a37e",
+ "ded1943d-9fdb-4563-83ee-2f04364872e0",
+ "96b636bb-3e4a-44d1-8ea7-f9da9e876f45",
+ ]);
+
+ expect(getChildrenNames(menuTreeItems[1].spec.children[0])).toEqual([]);
+ });
+});
+
+describe("convertMenuTreeItemToMenuItem", () => {
+ it("should match snapshot", () => {
+ const menuTreeItems = buildMenuItemsTree(rawMenuItems);
+ expect(convertMenuTreeItemToMenuItem(menuTreeItems[1])).toMatchSnapshot();
+ expect(
+ convertMenuTreeItemToMenuItem(menuTreeItems[1].spec.children[1])
+ ).toMatchSnapshot();
+ });
+
+ it("should return correct MenuItem", () => {
+ const menuTreeItems = buildMenuItemsTree(rawMenuItems);
+ expect(
+ convertMenuTreeItemToMenuItem(menuTreeItems[1]).spec.displayName
+ ).toBe("文章分类");
+ expect(
+ convertMenuTreeItemToMenuItem(menuTreeItems[1]).spec.children
+ ).toStrictEqual([
+ "caeef383-3828-4039-9114-6f9ad3b4a37e",
+ "ded1943d-9fdb-4563-83ee-2f04364872e0",
+ ]);
+ });
+});
diff --git a/console-src/modules/interface/menus/utils/index.ts b/console-src/modules/interface/menus/utils/index.ts
new file mode 100644
index 0000000..01b963d
--- /dev/null
+++ b/console-src/modules/interface/menus/utils/index.ts
@@ -0,0 +1,265 @@
+import type { MenuItem, MenuItemSpec } from "@halo-dev/api-client";
+import { cloneDeep } from "lodash-es";
+
+export interface MenuTreeItemSpec extends Omit {
+ children: MenuTreeItem[];
+}
+
+export interface MenuTreeItem extends Omit