diff --git a/packages/components/src/icons/icons.ts b/packages/components/src/icons/icons.ts
index 02d54e4b..af47f56c 100644
--- a/packages/components/src/icons/icons.ts
+++ b/packages/components/src/icons/icons.ts
@@ -14,6 +14,7 @@ import IconUserSettings from "~icons/ri/user-settings-line";
import IconSettings from "~icons/ri/settings-4-line";
import IconPlug from "~icons/ri/plug-2-line";
import IconEye from "~icons/ri/eye-line";
+import IconEyeOff from "~icons/ri/eye-off-line";
import IconFolder from "~icons/ri/folder-2-line";
import IconMore from "~icons/ri/more-line";
import IconClose from "~icons/ri/close-line";
@@ -41,6 +42,7 @@ import IconStopCircle from "~icons/ri/stop-circle-line";
import IconForbidLine from "~icons/ri/forbid-line";
import IconCodeBoxLine from "~icons/ri/code-box-line";
import IconDatabase2Line from "~icons/ri/database-2-line";
+import IconTeam from "~icons/ri/team-fill";
export {
IconDashboard,
@@ -57,6 +59,7 @@ export {
IconSettings,
IconPlug,
IconEye,
+ IconEyeOff,
IconFolder,
IconMore,
IconClose,
@@ -86,4 +89,5 @@ export {
IconForbidLine,
IconCodeBoxLine,
IconDatabase2Line,
+ IconTeam,
};
diff --git a/packages/shared/package.json b/packages/shared/package.json
index 9f40f999..ab385170 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -36,7 +36,7 @@
"homepage": "https://github.com/halo-dev/halo-admin/tree/next/shared/components#readme",
"license": "MIT",
"dependencies": {
- "@halo-dev/api-client": "^0.0.10",
+ "@halo-dev/api-client": "^0.0.12",
"@halo-dev/components": "workspace:*",
"axios": "^0.27.2"
},
diff --git a/packages/shared/src/utils/api-client.ts b/packages/shared/src/utils/api-client.ts
index f195c3de..8b5abd25 100644
--- a/packages/shared/src/utils/api-client.ts
+++ b/packages/shared/src/utils/api-client.ts
@@ -1,18 +1,26 @@
import {
+ ApiHaloRunV1alpha1ContentApi,
ApiHaloRunV1alpha1PluginApi,
+ ApiHaloRunV1alpha1PostApi,
+ ApiHaloRunV1alpha1ThemeApi,
ApiHaloRunV1alpha1UserApi,
+ ContentHaloRunV1alpha1CategoryApi,
+ ContentHaloRunV1alpha1CommentApi,
+ ContentHaloRunV1alpha1PostApi,
+ ContentHaloRunV1alpha1ReplyApi,
+ ContentHaloRunV1alpha1SnapshotApi,
+ ContentHaloRunV1alpha1TagApi,
PluginHaloRunV1alpha1PluginApi,
PluginHaloRunV1alpha1ReverseProxyApi,
+ ThemeHaloRunV1alpha1ThemeApi,
V1alpha1ConfigMapApi,
+ V1alpha1MenuApi,
+ V1alpha1MenuItemApi,
V1alpha1PersonalAccessTokenApi,
V1alpha1RoleApi,
V1alpha1RoleBindingApi,
V1alpha1SettingApi,
V1alpha1UserApi,
- V1alpha1MenuApi,
- V1alpha1MenuItemApi,
- ThemeHaloRunV1alpha1ThemeApi,
- ApiHaloRunV1alpha1ThemeApi,
} from "@halo-dev/api-client";
import type { AxiosInstance } from "axios";
import axios from "axios";
@@ -65,10 +73,19 @@ function setupApiClient(axios: AxiosInstance) {
theme: new ThemeHaloRunV1alpha1ThemeApi(undefined, apiUrl, axios),
menu: new V1alpha1MenuApi(undefined, apiUrl, axios),
menuItem: new V1alpha1MenuItemApi(undefined, apiUrl, axios),
+ post: new ContentHaloRunV1alpha1PostApi(undefined, apiUrl, axios),
+ category: new ContentHaloRunV1alpha1CategoryApi(undefined, apiUrl, axios),
+ tag: new ContentHaloRunV1alpha1TagApi(undefined, apiUrl, axios),
+ snapshot: new ContentHaloRunV1alpha1SnapshotApi(undefined, apiUrl, axios),
+ comment: new ContentHaloRunV1alpha1CommentApi(undefined, apiUrl, axios),
+ reply: new ContentHaloRunV1alpha1ReplyApi(undefined, apiUrl, axios),
},
+ // custom endpoints
user: new ApiHaloRunV1alpha1UserApi(undefined, apiUrl, axios),
plugin: new ApiHaloRunV1alpha1PluginApi(undefined, apiUrl, axios),
theme: new ApiHaloRunV1alpha1ThemeApi(undefined, apiUrl, axios),
+ post: new ApiHaloRunV1alpha1PostApi(undefined, apiUrl, axios),
+ content: new ApiHaloRunV1alpha1ContentApi(undefined, apiUrl, axios),
};
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7333db17..d5187383 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -12,9 +12,8 @@ importers:
'@formkit/inputs': 1.0.0-beta.10
'@formkit/themes': 1.0.0-beta.10
'@formkit/vue': 1.0.0-beta.10
- '@halo-dev/admin-api': ^1.1.0
'@halo-dev/admin-shared': workspace:*
- '@halo-dev/api-client': ^0.0.10
+ '@halo-dev/api-client': ^0.0.12
'@halo-dev/components': workspace:*
'@halo-dev/richtext-editor': 0.0.0-alpha.3
'@rushstack/eslint-patch': ^1.1.4
@@ -39,7 +38,9 @@ importers:
autoprefixer: ^10.4.8
axios: ^0.27.2
c8: ^7.12.0
+ colorjs.io: ^0.4.0
cypress: ^9.7.0
+ dayjs: ^1.11.5
eslint: ^8.22.0
eslint-plugin-cypress: ^2.12.1
eslint-plugin-vue: ^9.3.0
@@ -85,15 +86,16 @@ importers:
'@formkit/inputs': 1.0.0-beta.10
'@formkit/themes': 1.0.0-beta.10_tailwindcss@3.1.8
'@formkit/vue': 1.0.0-beta.10_wwmyxdjqen5bmh3tr2meig5lki
- '@halo-dev/admin-api': 1.1.0
'@halo-dev/admin-shared': link:packages/shared
- '@halo-dev/api-client': 0.0.10
+ '@halo-dev/api-client': 0.0.12
'@halo-dev/components': link:packages/components
'@halo-dev/richtext-editor': 0.0.0-alpha.3_vue@3.2.37
'@vueuse/components': 8.9.4_vue@3.2.37
'@vueuse/core': 8.9.4_vue@3.2.37
'@vueuse/router': 9.1.0_26a4nhf5pwzjzqc5ckt7ohj5zi
axios: 0.27.2
+ colorjs.io: 0.4.0
+ dayjs: 1.11.5
filepond: 4.30.4
filepond-plugin-image-preview: 4.6.11_filepond@4.30.4
floating-vue: 2.0.0-beta.19_vue@3.2.37
@@ -182,12 +184,12 @@ importers:
packages/shared:
specifiers:
- '@halo-dev/api-client': ^0.0.10
+ '@halo-dev/api-client': ^0.0.12
'@halo-dev/components': workspace:*
axios: ^0.27.2
vite-plugin-dts: ^1.4.1
dependencies:
- '@halo-dev/api-client': 0.0.10
+ '@halo-dev/api-client': 0.0.12
'@halo-dev/components': link:../components
axios: 0.27.2
devDependencies:
@@ -2109,39 +2111,8 @@ packages:
- windicss
dev: false
- /@halo-dev/admin-api/1.1.0:
- resolution: {integrity: sha512-2K8ulSucPudWfBo8SWz92hLtlu8C7hVVfYNejlns0BZfMxFyivvGyTgeYiMYHgNR5n4K6tF903Zn1DkS7jnv0g==}
- engines: {node: '>=12'}
- dependencies:
- '@halo-dev/rest-api-client': 1.1.0
- tslib: 2.4.0
- transitivePeerDependencies:
- - debug
- dev: false
-
- /@halo-dev/api-client/0.0.10:
- resolution: {integrity: sha512-DKQKkEAKMR/rbopI6jbjbzLiYUZeY6dOcgqGoDGG8MAcwkWOI6iWaZnuR5z+X8vd51XjiPhnekAphfcO6PaWEQ==}
- dev: false
-
- /@halo-dev/logger/1.1.0:
- resolution: {integrity: sha512-y0jVivYwF8MCVi/OdW2D0LN+GTM5rzMsR/ZmQVfgmKQw7Q7Q+EXPijxON6iCMZnWANGa4NaAcOO9k3ggG8oRwg==}
- engines: {node: '>=12.0.0'}
- dependencies:
- tslib: 2.4.0
- dev: false
-
- /@halo-dev/rest-api-client/1.1.0:
- resolution: {integrity: sha512-zoAzaswdgBpkAw8A6zs4N+n03sSwF/YKnLnj9p7PNQoiix1Z1GK+Tc/WV59A+wRB9Xw5W3JZhzvuaOPOe7RpEw==}
- engines: {node: '>=12'}
- dependencies:
- '@halo-dev/logger': 1.1.0
- axios: 0.24.0
- form-data: 4.0.0
- js-base64: 3.7.2
- qs: 6.11.0
- tslib: 2.4.0
- transitivePeerDependencies:
- - debug
+ /@halo-dev/api-client/0.0.12:
+ resolution: {integrity: sha512-fOI3DB9rOA1Z+h1aKiEQ+2kWkNSmdWIDvd+39dR5b3X0DmKH+zWrNmygA5Qe2gBPX28TNt/zr2qCKUjGjb99CA==}
dev: false
/@halo-dev/richtext-editor/0.0.0-alpha.3_vue@3.2.37:
@@ -3950,14 +3921,6 @@ packages:
- debug
dev: true
- /axios/0.24.0:
- resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==}
- dependencies:
- follow-redirects: 1.14.9
- transitivePeerDependencies:
- - debug
- dev: false
-
/axios/0.27.2:
resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
dependencies:
@@ -4379,6 +4342,10 @@ packages:
resolution: {integrity: sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==}
dev: true
+ /colorjs.io/0.4.0:
+ resolution: {integrity: sha512-AUKG9GCDSHsFRUnxGrEMCm6nq6lxddnDvD0avmsy/klCEk68htpqgl9IERGtGoxaGJlr7uP5wmD381gY2uG8hw==}
+ dev: false
+
/colors/1.2.5:
resolution: {integrity: sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==}
engines: {node: '>=0.1.90'}
@@ -4579,7 +4546,7 @@ packages:
cli-table3: 0.6.1
commander: 5.1.0
common-tags: 1.8.2
- dayjs: 1.11.3
+ dayjs: 1.11.5
debug: 4.3.4_supports-color@8.1.1
enquirer: 2.3.6
eventemitter2: 6.4.5
@@ -4623,9 +4590,8 @@ packages:
whatwg-url: 11.0.0
dev: true
- /dayjs/1.11.3:
- resolution: {integrity: sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A==}
- dev: true
+ /dayjs/1.11.5:
+ resolution: {integrity: sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==}
/debug/2.6.9:
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
@@ -6461,10 +6427,6 @@ packages:
'@sideway/pinpoint': 2.0.0
dev: true
- /js-base64/3.7.2:
- resolution: {integrity: sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==}
- dev: false
-
/js-tokens/4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
dev: true
@@ -8560,6 +8522,7 @@ packages:
/tslib/2.4.0:
resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
+ dev: true
/tsutils/3.21.0_typescript@4.7.4:
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
diff --git a/src/constants/labels.ts b/src/constants/labels.ts
index e8844916..a903d811 100644
--- a/src/constants/labels.ts
+++ b/src/constants/labels.ts
@@ -7,3 +7,11 @@ export enum pluginLabels {
export enum roleLabels {
TEMPLATE = "halo.run/role-template",
}
+
+// post
+export enum postLabels {
+ DELETED = "content.halo.run/deleted",
+ OWNER = "content.halo.run/owner",
+ VISIBLE = "content.halo.run/visible",
+ PHASE = "content.halo.run/phase",
+}
diff --git a/src/formkit/theme.ts b/src/formkit/theme.ts
index fd990f68..cddacb07 100644
--- a/src/formkit/theme.ts
+++ b/src/formkit/theme.ts
@@ -2,14 +2,14 @@ const textClassification = {
label: "block font-bold text-sm formkit-invalid:text-red-500 w-56",
wrapper: "flex flex-col sm:flex-row items-start sm:items-center",
inner:
- "inline-flex items-center w-full relative box-border border border-gray-300 formkit-invalid:border-red-500 rounded-base overflow-hidden focus-within:border-primary mt-2 sm:mt-0",
+ "inline-flex items-center w-full relative box-border border border-gray-300 formkit-invalid:border-red-500 h-9 rounded-base overflow-hidden focus-within:border-primary mt-2 sm:mt-0",
input:
"outline-0 bg-white antialiased resize-none w-full text-black block transition-all appearance-none h-9 px-3 text-sm",
};
const boxClassification = {
fieldset:
- "border border-gray-300 rounded-base px-2 pb-1 focus-within:border-primary",
+ "border border-gray-300 rounded-base px-2 py-2 focus-within:border-primary",
legend: "font-bold text-sm",
wrapper: "flex items-center mb-1 cursor-pointer",
help: "mb-2",
diff --git a/src/modules/contents/posts/PostEditor.vue b/src/modules/contents/posts/PostEditor.vue
index 41a45217..245cb58b 100644
--- a/src/modules/contents/posts/PostEditor.vue
+++ b/src/modules/contents/posts/PostEditor.vue
@@ -4,24 +4,141 @@ import {
IconSave,
VButton,
VPageHeader,
+ VSpace,
} from "@halo-dev/components";
+import PostSettingModal from "./components/PostSettingModal.vue";
+import type { PostRequest } from "@halo-dev/api-client";
+import { computed, onMounted, ref } from "vue";
+import cloneDeep from "lodash.clonedeep";
+import { apiClient } from "@halo-dev/admin-shared";
+import { useRouteQuery } from "@vueuse/router";
+import { v4 as uuid } from "uuid";
+
+const name = useRouteQuery("name");
+
+const initialFormState: PostRequest = {
+ post: {
+ spec: {
+ title: "",
+ slug: "",
+ template: "",
+ cover: "",
+ deleted: false,
+ published: false,
+ publishTime: undefined,
+ pinned: false,
+ allowComment: true,
+ visible: "PUBLIC",
+ version: 1,
+ priority: 0,
+ excerpt: {
+ autoGenerate: true,
+ raw: "",
+ },
+ categories: [],
+ tags: [],
+ htmlMetas: [],
+ },
+ apiVersion: "content.halo.run/v1alpha1",
+ kind: "Post",
+ metadata: {
+ name: uuid(),
+ },
+ },
+ content: {
+ raw: "",
+ content: "",
+ rawType: "HTML",
+ },
+};
+
+const formState = ref
(cloneDeep(initialFormState));
+const settingModal = ref(false);
+const saving = ref(false);
+
+const isUpdateMode = computed(() => {
+ return !!formState.value.post.metadata.creationTimestamp;
+});
+
+const handleSavePost = async () => {
+ try {
+ saving.value = true;
+ formState.value.content.content = formState.value.content.raw;
+ if (isUpdateMode.value) {
+ const { data } = await apiClient.post.updateDraftPost(
+ formState.value.post.metadata.name,
+ formState.value
+ );
+ formState.value.post = data;
+ } else {
+ const { data } = await apiClient.post.draftPost(formState.value);
+ formState.value.post = data;
+ name.value = data.metadata.name;
+ }
+ } catch (e) {
+ alert(`保存异常: ${e}`);
+ console.error("Failed to save post", e);
+ } finally {
+ saving.value = false;
+ }
+};
+
+const onSettingSaved = (post: PostRequest) => {
+ formState.value = post;
+ settingModal.value = false;
+ handleSavePost();
+};
+
+onMounted(async () => {
+ if (name.value) {
+ // fetch post
+ const { data: post } =
+ await apiClient.extension.post.getcontentHaloRunV1alpha1Post(
+ name.value as string
+ );
+ formState.value.post = post;
+
+ if (formState.value.post.spec.headSnapshot) {
+ const { data: content } = await apiClient.content.obtainSnapshotContent(
+ formState.value.post.spec.headSnapshot
+ );
+ formState.value.content = content;
+ }
+ }
+});
+
-
-
-
-
- 发布
-
+
+
+ 保存
+
+
+
+
+
+ 发布
+
+
-
+
diff --git a/src/modules/contents/posts/PostList.vue b/src/modules/contents/posts/PostList.vue
index ae64e556..06153f20 100644
--- a/src/modules/contents/posts/PostList.vue
+++ b/src/modules/contents/posts/PostList.vue
@@ -2,104 +2,284 @@
import {
IconAddCircle,
IconArrowDown,
+ IconArrowLeft,
+ IconArrowRight,
IconBookRead,
+ IconEye,
+ IconEyeOff,
IconSettings,
+ IconTeam,
+ useDialog,
VButton,
VCard,
+ VEmpty,
VPageHeader,
VPagination,
VSpace,
VTag,
} from "@halo-dev/components";
import PostSettingModal from "./components/PostSettingModal.vue";
-import { posts } from "./posts-mock";
-import { computed, onMounted, ref } from "vue";
-import { useRouter } from "vue-router";
-import type { Post } from "@halo-dev/admin-api";
+import PostTag from "../posts/tags/components/PostTag.vue";
+import { onMounted, ref, watch, watchEffect } from "vue";
+import type { ListedPostList, Post, PostRequest } from "@halo-dev/api-client";
import { apiClient } from "@halo-dev/admin-shared";
-import type { User } from "@halo-dev/api-client";
+import { formatDatetime } from "@/utils/date";
+import { useUserFetch } from "@/modules/system/users/composables/use-user";
+import { usePostCategory } from "@/modules/contents/posts/categories/composables/use-post-category";
+import { usePostTag } from "@/modules/contents/posts/tags/composables/use-post-tag";
+import cloneDeep from "lodash.clonedeep";
+import { postLabels } from "@/constants/labels";
-const postsRef = ref(
- // eslint-disable-next-line
- posts.map((item: any) => {
- return {
- ...item,
- checked: false,
- };
- })
-);
+enum PostPhase {
+ DRAFT = "未发布",
+ PENDING_APPROVAL = "待审核",
+ PUBLISHED = "已发布",
+}
-const router = useRouter();
-const checkAll = ref(false);
-const postSettings = ref(false);
-// eslint-disable-next-line
-const selected = ref | null>({});
-const users = ref([]);
-
-const checkedCount = computed(() => {
- return postsRef.value.filter((post) => post.checked).length;
+const posts = ref({
+ page: 1,
+ size: 20,
+ total: 0,
+ items: [],
+ first: true,
+ last: false,
+ hasNext: false,
+ hasPrevious: false,
});
+const loading = ref(false);
+const settingModal = ref(false);
+const selectedPost = ref(null);
+const selectedPostWithContent = ref(null);
+const checkedAll = ref(false);
+const selectedPostNames = ref([]);
-const handleFetchUsers = async () => {
+const { users } = useUserFetch();
+const { categories } = usePostCategory();
+const { tags } = usePostTag();
+const dialog = useDialog();
+
+const handleFetchPosts = async () => {
try {
- const { data } = await apiClient.extension.user.listv1alpha1User();
- users.value = data.items;
+ loading.value = true;
+
+ const labelSelector: string[] = [];
+
+ if (selectedVisibleFilterItem.value.value) {
+ labelSelector.push(
+ `${postLabels.VISIBLE}=${selectedVisibleFilterItem.value.value}`
+ );
+ }
+
+ if (selectedPhaseFilterItem.value.value) {
+ labelSelector.push(
+ `${postLabels.PHASE}=${selectedPhaseFilterItem.value.value}`
+ );
+ }
+
+ const { data } = await apiClient.post.listPosts(
+ posts.value.page,
+ posts.value.size,
+ labelSelector
+ );
+ posts.value = data;
} catch (e) {
- console.error(e);
+ console.error("Failed to fetch posts", e);
+ } finally {
+ loading.value = false;
}
};
-const handleCheckAll = () => {
- postsRef.value.forEach((item) => {
- item.checked = checkAll.value;
- });
+const handlePaginationChange = ({
+ page,
+ size,
+}: {
+ page: number;
+ size: number;
+}) => {
+ posts.value.page = page;
+ posts.value.size = size;
+ handleFetchPosts();
};
-// eslint-disable-next-line
-const handleSelect = (post: any) => {
- selected.value = post;
- postSettings.value = true;
+const handleOpenSettingModal = (post: Post) => {
+ selectedPost.value = post;
+ settingModal.value = true;
};
-const handleSelectPrevious = () => {
- const currentIndex = posts.findIndex(
- (post) => post.id === selected.value?.id
+const onSettingModalClose = () => {
+ selectedPost.value = null;
+ handleFetchPosts();
+};
+
+const handleSelectPrevious = async () => {
+ const { items, hasPrevious } = posts.value;
+ const index = items.findIndex(
+ (post) => post.post.metadata.name === selectedPost.value?.metadata.name
);
- if (currentIndex > 0) {
- selected.value = posts[currentIndex - 1];
+ if (index > 0) {
+ selectedPost.value = items[index - 1].post;
+ return;
+ }
+ if (index === 0 && hasPrevious) {
+ posts.value.page--;
+ await handleFetchPosts();
+ selectedPost.value = posts.value.items[posts.value.items.length - 1].post;
}
};
-const handleSelectNext = () => {
- const currentIndex = posts.findIndex(
- (post) => post.id === selected.value?.id
+const handleSelectNext = async () => {
+ const { items, hasNext } = posts.value;
+ const index = items.findIndex(
+ (post) => post.post.metadata.name === selectedPost.value?.metadata.name
);
- if (currentIndex < posts.length - 1) {
- selected.value = posts[currentIndex + 1];
+ if (index < items.length - 1) {
+ selectedPost.value = items[index + 1].post;
+ return;
+ }
+ if (index === items.length - 1 && hasNext) {
+ posts.value.page++;
+ await handleFetchPosts();
+ selectedPost.value = posts.value.items[0].post;
}
};
-// eslint-disable-next-line
-const handleRouteToEditor = (post: any) => {
- router.push({
- name: "PostEditor",
- params: {
- id: post.id,
+const checkSelection = (post: Post) => {
+ return (
+ post.metadata.name === selectedPost.value?.metadata.name ||
+ selectedPostNames.value.includes(post.metadata.name)
+ );
+};
+
+const finalStatus = (post: Post) => {
+ if (post.status?.phase) {
+ return PostPhase[post.status.phase];
+ }
+ return "";
+};
+
+const handleCheckAllChange = (e: Event) => {
+ const { checked } = e.target as HTMLInputElement;
+
+ if (checked) {
+ selectedPostNames.value =
+ posts.value.items.map((post) => {
+ return post.post.metadata.name;
+ }) || [];
+ } else {
+ selectedPostNames.value.length = 0;
+ }
+};
+
+const handleDelete = async (post: Post) => {
+ dialog.warning({
+ title: "是否确认删除该文章?",
+ confirmType: "danger",
+ onConfirm: async () => {
+ const postToUpdate = cloneDeep(post);
+ postToUpdate.spec.deleted = true;
+ await apiClient.extension.post.updatecontentHaloRunV1alpha1Post(
+ postToUpdate.metadata.name,
+ postToUpdate
+ );
+ await handleFetchPosts();
},
});
};
-onMounted(() => {
- handleFetchUsers();
+watch(selectedPostNames, (newValue) => {
+ checkedAll.value = newValue.length === posts.value.items?.length;
});
+
+watchEffect(async () => {
+ if (!selectedPost.value || !selectedPost.value.spec.headSnapshot) {
+ return;
+ }
+
+ const { data: content } = await apiClient.content.obtainSnapshotContent(
+ selectedPost.value.spec.headSnapshot
+ );
+
+ selectedPostWithContent.value = {
+ post: selectedPost.value,
+ content: content,
+ };
+});
+
+onMounted(() => {
+ handleFetchPosts();
+});
+
+interface FilterItem {
+ label: string;
+ value: string | undefined;
+}
+
+const VisibleFilterItems: FilterItem[] = [
+ {
+ label: "全部",
+ value: "",
+ },
+ {
+ label: "公开",
+ value: "PUBLIC",
+ },
+ {
+ label: "内部成员可访问",
+ value: "INTERNAL",
+ },
+ {
+ label: "私有",
+ value: "PRIVATE",
+ },
+];
+
+const PhaseFilterItems: FilterItem[] = [
+ {
+ label: "全部",
+ value: "",
+ },
+ {
+ label: "已发布",
+ value: "PUBLISHED",
+ },
+ {
+ label: "未发布",
+ value: "DRAFT",
+ },
+ {
+ label: "待审核",
+ value: "PENDING_APPROVAL",
+ },
+];
+
+const selectedVisibleFilterItem = ref(VisibleFilterItems[0]);
+const selectedPhaseFilterItem = ref(PhaseFilterItems[0]);
+
+function handleVisibleFilterItemChange(filterItem: FilterItem) {
+ selectedVisibleFilterItem.value = filterItem;
+ handleFetchPosts();
+}
+
+function handlePhaseFilterItemChange(filterItem: FilterItem) {
+ selectedPhaseFilterItem.value = filterItem;
+ handleFetchPosts();
+}
+ v-model:visible="settingModal"
+ :post="selectedPostWithContent"
+ @close="onSettingModalClose"
+ >
+
+
+
+
+
@@ -107,6 +287,7 @@ onMounted(() => {
分类
+ 标签
@@ -126,18 +307,16 @@ onMounted(() => {
>
-
+
+
+
设置
删除
@@ -158,24 +337,50 @@ onMounted(() => {
-
- 全部
+ {{ filterItem.label }}
+
+
+
+
+
+
+ 可见性
+
+
+
+
+
+
+
-
- 已发布
-
- -
- 草稿
-
- -
- 未审核
+
+ {{ filterItem.label }}
+
@@ -191,11 +396,57 @@ onMounted(() => {
-
-
+
+
+
+
+
+
+ -
+
+
+
+
+
+ {{ category.spec.displayName }}
+
+
+
+
+
+ /categories/{{ category.spec.slug }}
+
+
+
+
+
+
+
+
+
@@ -209,11 +460,54 @@ onMounted(() => {
-
-
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ /tags/{{ tag.spec.slug }}
+
+
+
+
+
+
+
+
+
@@ -227,8 +521,8 @@ onMounted(() => {
-
-
+
+
{
-
-
-
-
+
{
-
- -
+
+
+
+
+ 刷新
+
+
+
+
+ 新建文章
+
+
+
+
+
+ -
-
- {{ post.title }}
-
+
+ {{ post.post.spec.title }}
+
+
-
- {{ tag.name }}
-
+
+
+
- 访问量 {{ post.visits }}
- 评论 {{ post.commentCount }}
+ 分类:
+ {{ category.spec.displayName }}
+
+
+ 访问量 0
+ 评论 0
@@ -376,15 +710,73 @@ onMounted(() => {
-
-
@@ -395,7 +787,12 @@ onMounted(() => {
-
+
diff --git a/src/modules/contents/posts/categories/CategoryList.vue b/src/modules/contents/posts/categories/CategoryList.vue
index e7ec07d5..8e0d892d 100644
--- a/src/modules/contents/posts/categories/CategoryList.vue
+++ b/src/modules/contents/posts/categories/CategoryList.vue
@@ -1,25 +1,96 @@
-
+
+
+
+
+
+
+
+ 新建
+
+
@@ -29,81 +100,37 @@ const editingModal = ref(false);
class="relative flex flex-col items-start sm:flex-row sm:items-center"
>
- {{ 10 }} 个分类
-
-
-
-
- 新增
-
-
+
+ {{ categories.length }} 个分类
+
-
- -
-
-
-
-
-
-
-
-
- 主题
-
-
-
-
-
- https://halo.run/categories/themes
-
-
-
-
-
-
- 20 篇文章
-
-
- 2020-01-07
-
-
-
-
-
-
-
-
- 修改
-
-
- 删除
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ 刷新
+
+
+
+
+ 新建分类
+
+
+
+
+
diff --git a/src/modules/contents/posts/categories/components/CategoryEditingModal.vue b/src/modules/contents/posts/categories/components/CategoryEditingModal.vue
index 7c44c9ac..0433a3ed 100644
--- a/src/modules/contents/posts/categories/components/CategoryEditingModal.vue
+++ b/src/modules/contents/posts/categories/components/CategoryEditingModal.vue
@@ -1,14 +1,28 @@
-
-
+
+
-
-
+
+
- 提交 ⌘ + ↵
+ 保存 ⌘ + ↵
取消 Esc
diff --git a/src/modules/contents/posts/categories/components/CategoryListItem.vue b/src/modules/contents/posts/categories/components/CategoryListItem.vue
new file mode 100644
index 00000000..eab5d0fa
--- /dev/null
+++ b/src/modules/contents/posts/categories/components/CategoryListItem.vue
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ category.spec.displayName }}
+
+
+
+
+
+ /categories/{{ category.spec.slug }}
+
+
+
+
+
+
+
+
+
+ 删除中
+
+
+ 20 篇文章
+
+
+ {{ formatDatetime(category.metadata.creationTimestamp) }}
+
+
+
+
+
+
+
+
+ 修改
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/contents/posts/categories/components/__tests__/CategoryEditingModal.spec.ts b/src/modules/contents/posts/categories/components/__tests__/CategoryEditingModal.spec.ts
new file mode 100644
index 00000000..e994cf1e
--- /dev/null
+++ b/src/modules/contents/posts/categories/components/__tests__/CategoryEditingModal.spec.ts
@@ -0,0 +1,9 @@
+import { describe, expect, it } from "vitest";
+import { mount } from "@vue/test-utils";
+import CategoryEditingModal from "../CategoryEditingModal.vue";
+
+describe("CategoryEditingModal", function () {
+ it("should render", function () {
+ expect(mount(CategoryEditingModal)).toBeDefined();
+ });
+});
diff --git a/src/modules/contents/posts/categories/composables/use-post-category.ts b/src/modules/contents/posts/categories/composables/use-post-category.ts
new file mode 100644
index 00000000..02958215
--- /dev/null
+++ b/src/modules/contents/posts/categories/composables/use-post-category.ts
@@ -0,0 +1,69 @@
+import { apiClient } from "@halo-dev/admin-shared";
+import type { Category } from "@halo-dev/api-client";
+import type { Ref } from "vue";
+import { onMounted, ref } from "vue";
+import type { CategoryTree } from "@/modules/contents/posts/categories/utils";
+import { buildCategoriesTree } from "@/modules/contents/posts/categories/utils";
+import { useDialog } from "@halo-dev/components";
+
+interface usePostCategoryReturn {
+ categories: Ref;
+ categoriesTree: Ref;
+ loading: Ref;
+ handleFetchCategories: () => void;
+ handleDelete: (category: CategoryTree) => void;
+}
+
+export function usePostCategory(): usePostCategoryReturn {
+ const categories = ref([] as Category[]);
+ const categoriesTree = ref([] as CategoryTree[]);
+ const loading = ref(false);
+
+ const dialog = useDialog();
+
+ const handleFetchCategories = async () => {
+ try {
+ loading.value = true;
+ const { data } =
+ await apiClient.extension.category.listcontentHaloRunV1alpha1Category(
+ 0,
+ 0
+ );
+ categories.value = data.items;
+ categoriesTree.value = buildCategoriesTree(data.items);
+ } catch (e) {
+ console.error("Failed to fetch categories", e);
+ } finally {
+ loading.value = false;
+ }
+ };
+
+ const handleDelete = async (category: CategoryTree) => {
+ dialog.warning({
+ title: "确定要删除该分类吗?",
+ description: "删除此分类之后,对应文章的关联将被解除。该操作不可恢复。",
+ confirmType: "danger",
+ onConfirm: async () => {
+ try {
+ await apiClient.extension.category.deletecontentHaloRunV1alpha1Category(
+ category.metadata.name
+ );
+ } catch (e) {
+ console.error("Failed to delete tag", e);
+ } finally {
+ await handleFetchCategories();
+ }
+ },
+ });
+ };
+
+ onMounted(handleFetchCategories);
+
+ return {
+ categories,
+ categoriesTree,
+ loading,
+ handleFetchCategories,
+ handleDelete,
+ };
+}
diff --git a/src/modules/contents/posts/categories/utils/index.ts b/src/modules/contents/posts/categories/utils/index.ts
new file mode 100644
index 00000000..0a552d6d
--- /dev/null
+++ b/src/modules/contents/posts/categories/utils/index.ts
@@ -0,0 +1,123 @@
+import type { Category, CategorySpec } from "@halo-dev/api-client";
+import cloneDeep from "lodash.clonedeep";
+
+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,
+ },
+ };
+}
diff --git a/src/modules/contents/posts/components/PostSettingModal.vue b/src/modules/contents/posts/components/PostSettingModal.vue
index e2461954..92795cbd 100644
--- a/src/modules/contents/posts/components/PostSettingModal.vue
+++ b/src/modules/contents/posts/components/PostSettingModal.vue
@@ -1,47 +1,96 @@
-
-
+
-
+
-
+
-
-
-
+
+
+
+
+
-
+
+
-
-
+
+
-
+
{
-
+
@@ -166,14 +314,32 @@ const handleVisibleChange = (visible: boolean) => {
- 保存
+ 取消发布
- 取消
+
+ 发布
+
+
+ 仅保存
+
+
+ 关闭
diff --git a/src/modules/contents/posts/components/__tests__/PostSettingModal.spec.ts b/src/modules/contents/posts/components/__tests__/PostSettingModal.spec.ts
new file mode 100644
index 00000000..8eaf79cd
--- /dev/null
+++ b/src/modules/contents/posts/components/__tests__/PostSettingModal.spec.ts
@@ -0,0 +1,20 @@
+import { describe, expect, it } from "vitest";
+import { mount } from "@vue/test-utils";
+import PostSettingModal from "../PostSettingModal.vue";
+import { VDialogProvider } from "@halo-dev/components";
+
+describe("PostSettingModal", () => {
+ it("should render", () => {
+ const wrapper = mount({
+ components: {
+ VDialogProvider,
+ PostSettingModal,
+ },
+ template: `
+
+
+ `,
+ });
+ expect(wrapper).toBeDefined();
+ });
+});
diff --git a/src/modules/contents/posts/posts-mock.ts b/src/modules/contents/posts/posts-mock.ts
deleted file mode 100644
index 1376fb6e..00000000
--- a/src/modules/contents/posts/posts-mock.ts
+++ /dev/null
@@ -1,1036 +0,0 @@
-// eslint-disable-next-line
-export const posts: any[] = [
- {
- id: 514,
- title: "Halo 1.5.3 发布了",
- status: "PUBLISHED",
- slug: "halo-153-released",
- editorType: "MARKDOWN",
- updateTime: 1652087983741,
- createTime: 1652087543478,
- editTime: 1652087983738,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/halo-153-released.html",
- summary:
- "Improvements优化邮件发送异常信息处理。 halo-dev/halo#1860 @ntdgy @superdgy优化静态存储的资源映射处理逻辑,支持手动操作 .halo/static 目录后,在后台通过刷新按钮更新资源映射。 halo-dev/halo#1907 @Yhcrown @muy",
- thumbnail: "https://halo.run/upload/2022/05/1.5.3.png",
- visits: 156,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 1128,
- inProgress: false,
- commentCount: 3,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
- {
- id: 511,
- title: "Halo 1.5.2 发布",
- status: "PUBLISHED",
- slug: "halo-152-released",
- editorType: "MARKDOWN",
- updateTime: 1649582521222,
- createTime: 1649580837197,
- editTime: 1649582521202,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/halo-152-released.html",
- summary:
- "Improvements保存文章的时候不再保存内容到 posts 表,originalContent 和 formatContent 已经是废弃字段。 halo-dev/halo#1797 @guqing优化 Markdown 文档导入的 FrontMatter 的解析规则。 halo-dev/ha",
- thumbnail: "https://halo.run/upload/2022/03/1.5.0.png",
- visits: 1700,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 1483,
- inProgress: false,
- commentCount: 5,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
- {
- id: 479,
- title: "Halo 1.5.1 发布",
- status: "PUBLISHED",
- slug: "halo-151-released",
- editorType: "MARKDOWN",
- updateTime: 1648634917957,
- createTime: 1648634014575,
- editTime: 1648634917948,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/halo-151-released.html",
- summary:
- "Bug Fixes修复文章加密和分类加密功能的逻辑问题。 halo-dev/halo#1781 halo-dev/halo#1785 @guqing @qiany-sui修复文章和分类的链接带有中文时从密码页重定向到详情页面提示 404 的问题。 halo-dev/halo#1786 @guqing",
- thumbnail: "https://halo.run/upload/2022/03/1.5.0.png",
- visits: 1007,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 1050,
- inProgress: false,
- commentCount: 7,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
- {
- id: 447,
- title: "Halo 1.5.0 发布",
- status: "PUBLISHED",
- slug: "halo-150-released",
- editorType: "MARKDOWN",
- updateTime: 1648025027976,
- createTime: 1648007074766,
- editTime: 1648025027971,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/halo-150-released.html",
- summary:
- "距离我们 2020 年 9 月 24 号发布 1.4.0 已经过去了 545 天了,期间虽然有一些版本更新,但大多数都是 patch 修复版本。终于,在今天正式发布 1.5.0 版本。其中带来了大量的优化更新,下面为大家简单介绍一些亮点功能,详细更新日志可在本文末尾查阅。版本亮点文章表拆分在此版本中",
- thumbnail: "https://halo.run/upload/2022/03/1.5.0.png",
- visits: 1085,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 5852,
- inProgress: false,
- commentCount: 1,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
- {
- id: 415,
- title: "Butterfly",
- status: "PUBLISHED",
- slug: "butterfly",
- editorType: "MARKDOWN",
- updateTime: 1649680073984,
- createTime: 1642992439970,
- editTime: 1649680073975,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/butterfly.html",
- summary:
- "Butterfly 主题 Halo 移植版Butterfly 是Hexo社区hexo-theme-Butterfly 此次移植Halo由原版的 pug模板引擎 调整为 freemarker, 保持灵活性的同时最大限度的移植了原版功能,希望大家喜欢 ❤️ \uD83C\uDF38 同时也要在此感谢原作者 Jerry 欢迎",
- thumbnail: "https://halo.run/upload/2022/04/Butterfly.png",
- visits: 26609,
- disallowComment: false,
- password: "",
- template: "theme",
- topPriority: 0,
- likes: 0,
- wordCount: 773,
- inProgress: false,
- commentCount: 33,
- tags: [
- {
- id: 1,
- name: "主题",
- slug: "themes",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1580914893201,
- fullPath: "https://halo.run/tags/themes.html",
- },
- {
- id: 3,
- name: "模板",
- slug: "templates",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1584502785043,
- fullPath: "https://halo.run/tags/templates.html",
- },
- {
- id: 4,
- name: "Halo 主题",
- slug: "halo-themes",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1584502798309,
- fullPath: "https://halo.run/tags/halo-themes.html",
- },
- {
- id: 5,
- name: "Halo 模板",
- slug: "halo-templates",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1584502800759,
- fullPath: "https://halo.run/tags/halo-templates.html",
- },
- ],
- categories: [
- {
- id: 2,
- name: "主题仓库",
- slug: "themes",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1580904752816,
- fullPath: "https://halo.run/categories/themes.html",
- priority: 1,
- },
- ],
- metas: {
- theme_repo: "https://github.com/dhjddcn/halo-theme-butterfly",
- theme_require: "1.4.11",
- theme_id: "butterfly",
- theme_author: "dhjddcn",
- theme_description: "Butterfly 主题 Halo 移植版",
- theme_version: "1.0.0",
- },
- topped: false,
- },
- {
- id: 383,
- title: "Joe 2.0",
- status: "PUBLISHED",
- slug: "joe20",
- editorType: "MARKDOWN",
- updateTime: 1649678688968,
- createTime: 1636638704064,
- editTime: 1649678688959,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/joe20.html",
- summary:
- " Joe 主题 Halo 移植版 Joe2.0 为 Typecho Themes Joe 主题的 Halo 版,由于 社区 之前有兄弟移植过,这次就叫 Joe2.0 吧,此次在原版主题上做了不少修改和适配,由原版的 php ",
- thumbnail: "https://halo.run/upload/2022/04/joe2.0.png",
- visits: 26444,
- disallowComment: false,
- password: "",
- template: "theme",
- topPriority: 0,
- likes: 0,
- wordCount: 8819,
- inProgress: false,
- commentCount: 59,
- tags: [
- {
- id: 1,
- name: "主题",
- slug: "themes",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1580914893201,
- fullPath: "https://halo.run/tags/themes.html",
- },
- {
- id: 3,
- name: "模板",
- slug: "templates",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1584502785043,
- fullPath: "https://halo.run/tags/templates.html",
- },
- {
- id: 4,
- name: "Halo 主题",
- slug: "halo-themes",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1584502798309,
- fullPath: "https://halo.run/tags/halo-themes.html",
- },
- {
- id: 5,
- name: "Halo 模板",
- slug: "halo-templates",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1584502800759,
- fullPath: "https://halo.run/tags/halo-templates.html",
- },
- ],
- categories: [
- {
- id: 2,
- name: "主题仓库",
- slug: "themes",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1580904752816,
- fullPath: "https://halo.run/categories/themes.html",
- priority: 1,
- },
- ],
- metas: {
- theme_repo: "https://github.com/qinhua/halo-theme-joe2.0",
- theme_require: "1.4.11",
- theme_id: "joe2.0",
- theme_author: "qinhua",
- theme_description: "Joe 主题 Halo 移植版",
- theme_version: "1.0.8",
- },
- topped: false,
- },
- {
- id: 351,
- title: "Halo 1.4.12 发布",
- status: "PUBLISHED",
- slug: "halo-1412-released",
- editorType: "MARKDOWN",
- updateTime: 1631798064866,
- createTime: 1631798054886,
- editTime: 1631798064862,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/halo-1412-released.html",
- summary:
- "FeaturesMinio 存储提供 Region 设置项。#1440 halo-dev/halo-admin#346Improvements后台菜单列表提供拖动按钮,优化拖动排序的体验。halo-dev/halo-admin#350后台升级 CodeMirror 的版本,降低最终构建体积。halo",
- thumbnail:
- "https://halo.run/upload/2021/09/1.4.12-9ed1f548565f482f8ae0d1db89c07087.png",
- visits: 9306,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 467,
- inProgress: false,
- commentCount: 11,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
- {
- id: 320,
- title: "Halo 1.4.10 发布",
- status: "PUBLISHED",
- slug: "halo-1410-released",
- editorType: "MARKDOWN",
- updateTime: 1626780018282,
- createTime: 1626625488791,
- editTime: 1626780018280,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/halo-1410-released.html",
- summary:
- "Features编辑器支持脚注语法。halo-dev/halo#1406 halo-dev/halo-admin#341Improvements优化文章字数计算。halo-dev/halo#1354Content Api 中的获取文章列表支持传入关键字和分类 id 筛选项。halo-dev/halo",
- thumbnail:
- "https://halo.run/upload/2021/07/1.4.10-d530fa68c73048a4926da0e98ddb5f21.png",
- visits: 4804,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 1737,
- inProgress: false,
- commentCount: 7,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
- {
- id: 319,
- title: "Reflect Halo 小程序",
- status: "PUBLISHED",
- slug: "mp-reflect",
- editorType: "MARKDOWN",
- updateTime: 1649679428953,
- createTime: 1623470692679,
- editTime: 1649679428942,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/mp-reflect.html",
- summary:
- "介绍Reflect Halo 是一个基于 Halo 后端的小程序项目,使用它可以非常便捷的搭建一个属于自己的博客小程序。主要特性符合大众审美的 UI 设计,我们拒绝使用一些花哨的界面或者动效,博客最重要的是阅读,我们力求打造更佳的阅读访问体验。同时我们执着于对细节的把控,力求不让强迫症患者感到不适。",
- thumbnail: "https://halo.run/upload/2022/04/reflect-mp-halo.jpeg",
- visits: 26117,
- disallowComment: false,
- password: "",
- template: "theme",
- topPriority: 0,
- likes: 0,
- wordCount: 1531,
- inProgress: false,
- commentCount: 10,
- tags: [],
- categories: [
- {
- id: 2,
- name: "主题仓库",
- slug: "themes",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1580904752816,
- fullPath: "https://halo.run/categories/themes.html",
- priority: 1,
- },
- ],
- metas: {
- theme_repo: "https://mianbaoduo.com/o/bread/mbd-YZqVl5pv",
- theme_require: "1.4.8",
- theme_id: "reflect.mp.halo",
- theme_author: "Reflect Team",
- theme_description: "Reflect Team 开发的 Halo 小程序",
- theme_version: "1.0.0",
- },
- topped: false,
- },
- {
- id: 289,
- title: "Iwtwin",
- status: "PUBLISHED",
- slug: "theme-iwtwin",
- editorType: "MARKDOWN",
- updateTime: 1649679443407,
- createTime: 1622203962900,
- editTime: 1649679443403,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/theme-iwtwin.html",
- summary:
- "IwtwinA Simple Halo Themes 预览在线预览https://www.kuya123.com图片预览安装压缩包上传方式(推荐)下载最新发行版zip压缩包到本地进入博客后台-外观-主题-安装选择本地上传,将压缩包上传上去等待提示安装完成在线拉取方式进入博客后台-外观-主题-安装选择",
- thumbnail: "https://halo.run/upload/2022/04/Iwtwin.png",
- visits: 22856,
- disallowComment: false,
- password: "",
- template: "theme",
- topPriority: 0,
- likes: 0,
- wordCount: 275,
- inProgress: false,
- commentCount: 17,
- tags: [
- {
- id: 1,
- name: "主题",
- slug: "themes",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1580914893201,
- fullPath: "https://halo.run/tags/themes.html",
- },
- {
- id: 3,
- name: "模板",
- slug: "templates",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1584502785043,
- fullPath: "https://halo.run/tags/templates.html",
- },
- {
- id: 4,
- name: "Halo 主题",
- slug: "halo-themes",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1584502798309,
- fullPath: "https://halo.run/tags/halo-themes.html",
- },
- {
- id: 5,
- name: "Halo 模板",
- slug: "halo-templates",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1584502800759,
- fullPath: "https://halo.run/tags/halo-templates.html",
- },
- ],
- categories: [
- {
- id: 2,
- name: "主题仓库",
- slug: "themes",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1580904752816,
- fullPath: "https://halo.run/categories/themes.html",
- priority: 1,
- },
- ],
- metas: {
- theme_repo: "https://github.com/cross266/halo-theme-iwtwin",
- theme_require: "1.4.8",
- theme_id: "iwtwin",
- theme_author: "CROSS",
- theme_description: "A Simple Halo Theme",
- theme_version: "1.0",
- },
- topped: false,
- },
- {
- id: 287,
- title: "使用腾讯云·云托管一键搭建 Halo",
- status: "PUBLISHED",
- slug: "tencent-cloudbase-halo",
- editorType: "MARKDOWN",
- updateTime: 1649580513991,
- createTime: 1620562541772,
- editTime: 1649580513973,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/tencent-cloudbase-halo.html",
- summary:
- "腾讯云·云开发简介云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为开发者提供高可用、自动弹性扩缩的后端云服务,包含计算、存储、托管等 Serverless 化能力,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用、Flutter 客户端",
- thumbnail:
- "https://halo.run/upload/2021/05/tencent-cloudbase-halo-61d41c4c35784df293f6b7dc675254a4.png",
- visits: 8992,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 951,
- inProgress: false,
- commentCount: 8,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
- {
- id: 255,
- title: "Halo 1.4.8 发布",
- status: "PUBLISHED",
- slug: "halo-148-released",
- editorType: "MARKDOWN",
- updateTime: 1617953751589,
- createTime: 1617952738451,
- editTime: 1617953751588,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/halo-148-released.html",
- summary:
- "Features系统设置中支持设置 Gravatar 源地址。#1331 halo-dev/halo-admin#314Improvements优化 RSS 订阅最后更新时间字段。#1342Bug Fixes修复在 1.4.7 中,导入 Markdown 提示格式不正确的问题。halo-dev/ha",
- thumbnail:
- "https://halo.run/upload/2021/04/1.4.8-186dddabdfd24be68fbd43c24dc91c22.png",
- visits: 7031,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 810,
- inProgress: false,
- commentCount: 20,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
- {
- id: 223,
- title: "Halo 1.4.7 发布",
- status: "PUBLISHED",
- slug: "halo-147-released",
- editorType: "MARKDOWN",
- updateTime: 1615698491995,
- createTime: 1615698392006,
- editTime: 1615698491993,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/halo-147-released.html",
- summary:
- "Bug Fixes修复在 Windows 平台下,上传主题压缩包提示不支持当前文件格式的问题。halo-dev/halo-admin#309Upgrade guidehttps://docs.halo.run/install/upgrade",
- thumbnail:
- "https://halo.run/upload/2021/03/1.4.7-b0e7be88d7ae4a7888f3750e92a105e9.png",
- visits: 4269,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 228,
- inProgress: false,
- commentCount: 5,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
- {
- id: 191,
- title: "Halo 1.4.6 发布",
- status: "PUBLISHED",
- slug: "halo-146-released",
- editorType: "MARKDOWN",
- updateTime: 1615128686265,
- createTime: 1615121994495,
- editTime: 1615128686264,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/halo-146-released.html",
- summary:
- "Features主题编写支持继承以及 block 特性。halo-dev/halo#1295Improvements重构初始化页面,目前分为全新安装和数据导入。从这个版本开始,数据导入将导入所有数据。halo-dev/halo-admin#296优化后台菜单管理的排序拖动体验。halo-dev/ha",
- thumbnail:
- "https://halo.run/upload/2021/03/1.4.6-df507013b5c74201b85ff19d99b6be27.png",
- visits: 2620,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 1208,
- inProgress: false,
- commentCount: 1,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
- {
- id: 160,
- title: "Halo 1.4.5 发布",
- status: "PUBLISHED",
- slug: "halo-145-released",
- editorType: "MARKDOWN",
- updateTime: 1612787764475,
- createTime: 1612787374387,
- editTime: 1612787764473,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/halo-145-released.html",
- summary:
- "Improvementssitemap.xml 新增所有分类和标签的链接。#1267Bug Fixes修复后台因为垂直滚动条导致界面抖动的问题。halo-dev/halo-admin#293修复加密分类页面输入密码后重定向的页面链接不正确的问题。#1264Upgrade guidehttps://d",
- thumbnail:
- "https://halo.run/upload/2021/02/1.4.5-543af0f9550e4d88ac94264aff4785a9.png",
- visits: 3480,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 205,
- inProgress: false,
- commentCount: 10,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
- {
- id: 159,
- title: "Halo 1.4.4 发布",
- status: "PUBLISHED",
- slug: "halo-144-released",
- editorType: "MARKDOWN",
- updateTime: 1612710237186,
- createTime: 1612706024825,
- editTime: 1612710237183,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/halo-144-released.html",
- summary:
- "Features支持对分类目录进行加密。#1235Bug Fixes修复在保存博客设置的时候,主题被恢复为上一个激活主题的问题。#1256修复后台在某些场景下,顶部加载条无法关闭的问题。",
- thumbnail:
- "https://halo.run/upload/2021/02/1.4.4-1a9b3c29f37045b19764fcccc732f7be.png",
- visits: 1626,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 170,
- inProgress: false,
- commentCount: 0,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
- {
- id: 127,
- title: "Halo 1.4.3 发布",
- status: "PUBLISHED",
- slug: "halo-143-released",
- editorType: "MARKDOWN",
- updateTime: 1611768562878,
- createTime: 1611768239977,
- editTime: 1611768562872,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/halo-143-released.html",
- summary:
- "随着这个版本的发布,我们的官网和文档也发生了一些变更。变更之后和 Halo 相关的站点:官网:https://halo.run文档:https://docs.halo.run论坛:https://bbs.halo.run主题仓库:https://halo.run/themes.htmlBreakin",
- thumbnail:
- "https://halo.run/upload/2021/01/1.4.3-b5fc5c3adfe543e5a2f01b53be241c5d.png",
- visits: 2438,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 793,
- inProgress: false,
- commentCount: 2,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
- {
- id: 95,
- title: "Xue",
- status: "PUBLISHED",
- slug: "theme-xue",
- editorType: "MARKDOWN",
- updateTime: 1649679459916,
- createTime: 1609208959025,
- editTime: 1649679459907,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/theme-xue.html",
- summary:
- "Halo Theme Xue追求个性与至简说明开始预览示例站点文档主题特性更新日志预览Preview | 个人网站我的个人网站由于存在自定义,会和当前主题会有不同示例站点微服务驿站南国薏米通往体面生活的路上pengzhenjin's blognEo.一只从代码中寻找乐趣的猿千与千寻Symon's B",
- thumbnail: "https://halo.run/upload/2022/04/xue.png",
- visits: 48675,
- disallowComment: false,
- password: "",
- template: "theme",
- topPriority: 0,
- likes: 0,
- wordCount: 546,
- inProgress: false,
- commentCount: 40,
- tags: [
- {
- id: 1,
- name: "主题",
- slug: "themes",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1580914893201,
- fullPath: "https://halo.run/tags/themes.html",
- },
- {
- id: 3,
- name: "模板",
- slug: "templates",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1584502785043,
- fullPath: "https://halo.run/tags/templates.html",
- },
- {
- id: 4,
- name: "Halo 主题",
- slug: "halo-themes",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1584502798309,
- fullPath: "https://halo.run/tags/halo-themes.html",
- },
- {
- id: 5,
- name: "Halo 模板",
- slug: "halo-templates",
- color: "#cfd3d7",
- thumbnail: "",
- createTime: 1584502800759,
- fullPath: "https://halo.run/tags/halo-templates.html",
- },
- ],
- categories: [
- {
- id: 2,
- name: "主题仓库",
- slug: "themes",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1580904752816,
- fullPath: "https://halo.run/categories/themes.html",
- priority: 1,
- },
- ],
- metas: {
- theme_repo: "https://github.com/xzhuz/halo-theme-xue",
- theme_id: "bao",
- theme_author: "xzhuz",
- theme_description: "追求个性与至简",
- theme_version: "1.4.0",
- },
- topped: false,
- },
- {
- id: 94,
- title: "Halo 1.4.2 发布",
- status: "PUBLISHED",
- slug: "halo-142-released",
- editorType: "MARKDOWN",
- updateTime: 1611477431760,
- createTime: 1604368978521,
- editTime: 1611477431757,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/halo-142-released.html",
- summary:
- "Feature文章固定链接支持年份型。#1095支持添加或修改友链时校验 name 和 url 是否重复。#1079支持 MinIO 云存储。#1097支持删除主题时,可选是否删除相关配置。#1105支持设置定时清理回收站的文章。#1114重构后台菜单管理,更方便的管理菜单分组以及排序。#1120F",
- thumbnail:
- "https://halo.run/upload/2021/01/1.4.2-54891c9f84f34ff98897b8320b666d8e.png",
- visits: 6432,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 792,
- inProgress: false,
- commentCount: 0,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
- {
- id: 93,
- title: "Halo 1.4.0 发布",
- status: "PUBLISHED",
- slug: "halo-140-released",
- editorType: "MARKDOWN",
- updateTime: 1611477443335,
- createTime: 1600878496716,
- editTime: 1611477443334,
- metaKeywords: "",
- metaDescription: "",
- fullPath: "https://halo.run/archives/halo-140-released.html",
- summary:
- "Feature支持静态存储重命名和修改文件内容。#819所有附件列表均支持右键复制图片链接。halo-dev/halo-admin#180开发者选项中的实时日志支持自动滚动到最新的日志。在线下载主题支持选择分支和 release。#515 #592 #835评论内容支持显示 html 文本。halo",
- thumbnail:
- "https://halo.run/upload/2021/01/1.4.0-e31ee60919d4441eb5a0c2c306fdfe48.png",
- visits: 3660,
- disallowComment: false,
- password: "",
- template: "",
- topPriority: 0,
- likes: 0,
- wordCount: 2270,
- inProgress: false,
- commentCount: 0,
- tags: [],
- categories: [
- {
- id: 9,
- name: "博客",
- slug: "blog",
- description: "",
- thumbnail: "",
- parentId: 0,
- password: "",
- createTime: 1581682496632,
- fullPath: "https://halo.run/categories/blog.html",
- priority: 8,
- },
- ],
- metas: [],
- topped: false,
- },
-];
diff --git a/src/modules/contents/posts/tags/TagList.vue b/src/modules/contents/posts/tags/TagList.vue
index 241bc3dc..c877ad13 100644
--- a/src/modules/contents/posts/tags/TagList.vue
+++ b/src/modules/contents/posts/tags/TagList.vue
@@ -1,5 +1,8 @@
-
+
-
-
-
-
-
- 新建
-
-
+
+
+
+
+ 新建
+
@@ -53,7 +125,9 @@ const viewType = ref("list");
class="relative flex flex-col items-start sm:flex-row sm:items-center"
>
- {{ 10 }} 个标签
+
+ {{ tags.length }} 个标签
+
-
- -
-
-
-
-
- 主题
-
-
-
- https://halo.run/tags/themes
-
-
-
-
-
-
- 20 篇文章
+
+
+ 刷新
+
+
+
+
+ 新建标签
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+ /tags/{{ tag.spec.slug }}
+
+
+
+
+
+
+
+
+
+ 删除中
+
+
+ 20 篇文章
+
+
+ {{ formatDatetime(tag.metadata.creationTimestamp) }}
+
+
+
+
+
+
+
+
+ 修改
+
+
+ 删除
+
+
+
+
+
+
-
- 2020-01-07
-
-
-
-
-
-
-
-
- 修改
-
-
- 删除
-
-
-
-
-
-
-
-
-
+
+
-
diff --git a/src/modules/contents/posts/tags/components/PostTag.vue b/src/modules/contents/posts/tags/components/PostTag.vue
new file mode 100644
index 00000000..537bbf67
--- /dev/null
+++ b/src/modules/contents/posts/tags/components/PostTag.vue
@@ -0,0 +1,26 @@
+
+
+
+ {{ tag.spec.displayName }}
+
+
diff --git a/src/modules/contents/posts/tags/components/TagEditingModal.vue b/src/modules/contents/posts/tags/components/TagEditingModal.vue
index cd992f3f..bf939cd0 100644
--- a/src/modules/contents/posts/tags/components/TagEditingModal.vue
+++ b/src/modules/contents/posts/tags/components/TagEditingModal.vue
@@ -1,51 +1,181 @@
-
-
+
+
+
+
+
+
-
-
+
+
-
- 提交 ⌘ + ↵
+
+ 保存
取消 Esc
diff --git a/src/modules/contents/posts/tags/composables/use-post-tag.ts b/src/modules/contents/posts/tags/composables/use-post-tag.ts
new file mode 100644
index 00000000..20db1641
--- /dev/null
+++ b/src/modules/contents/posts/tags/composables/use-post-tag.ts
@@ -0,0 +1,61 @@
+import { apiClient } from "@halo-dev/admin-shared";
+import type { Tag } from "@halo-dev/api-client";
+import type { Ref } from "vue";
+import { onMounted, ref } from "vue";
+import { useDialog } from "@halo-dev/components";
+
+interface usePostTagReturn {
+ tags: Ref;
+ loading: Ref;
+ handleFetchTags: () => void;
+ handleDelete: (tag: Tag) => void;
+}
+
+export function usePostTag(): usePostTagReturn {
+ const tags = ref([] as Tag[]);
+ const loading = ref(false);
+
+ const dialog = useDialog();
+
+ const handleFetchTags = async () => {
+ try {
+ loading.value = true;
+ const { data } =
+ await apiClient.extension.tag.listcontentHaloRunV1alpha1Tag(0, 0);
+
+ tags.value = data.items;
+ } catch (e) {
+ console.error("Failed to fetch tags", e);
+ } finally {
+ loading.value = false;
+ }
+ };
+
+ const handleDelete = async (tag: Tag) => {
+ dialog.warning({
+ title: "确定要删除该标签吗?",
+ description: "删除此标签之后,对应文章的关联将被解除。该操作不可恢复。",
+ confirmType: "danger",
+ onConfirm: async () => {
+ try {
+ await apiClient.extension.tag.deletecontentHaloRunV1alpha1Tag(
+ tag.metadata.name
+ );
+ } catch (e) {
+ console.error("Failed to delete tag", e);
+ } finally {
+ await handleFetchTags();
+ }
+ },
+ });
+ };
+
+ onMounted(handleFetchTags);
+
+ return {
+ tags,
+ loading,
+ handleFetchTags,
+ handleDelete,
+ };
+}
diff --git a/src/modules/dashboard/widgets/RecentPublishedWidget.vue b/src/modules/dashboard/widgets/RecentPublishedWidget.vue
index 0769b6ce..3c67dc33 100644
--- a/src/modules/dashboard/widgets/RecentPublishedWidget.vue
+++ b/src/modules/dashboard/widgets/RecentPublishedWidget.vue
@@ -1,6 +1,22 @@
- {{ post.title }}
+ {{ post.spec.title }}
-
- 阅读 {{ post.visits }}
-
-
- 评论 {{ post.commentCount }}
-
+ 阅读 0
+ 评论 0
-
- 2020-01-07 20:00
+
+ {{ post.metadata.creationTimestamp }}
diff --git a/src/utils/__tests__/date.spec.ts b/src/utils/__tests__/date.spec.ts
new file mode 100644
index 00000000..19e01702
--- /dev/null
+++ b/src/utils/__tests__/date.spec.ts
@@ -0,0 +1,10 @@
+import { describe, expect, it } from "vitest";
+import { formatDatetime } from "../date";
+
+describe.skip("date#formatDatetime", () => {
+ it("should return formatted datetime", () => {
+ const formattedDatetime = formatDatetime("2022-08-17T06:01:16.511575Z");
+
+ expect(formattedDatetime).toEqual("2022-08-17 14:01");
+ });
+});
diff --git a/src/utils/date.ts b/src/utils/date.ts
new file mode 100644
index 00000000..24810285
--- /dev/null
+++ b/src/utils/date.ts
@@ -0,0 +1,14 @@
+import dayjs from "dayjs";
+import "dayjs/locale/zh-cn";
+import timezone from "dayjs/plugin/timezone";
+
+dayjs.extend(timezone);
+
+dayjs.locale("zh-cn");
+
+export function formatDatetime(date: string | Date | undefined | null): string {
+ if (!date) {
+ return "";
+ }
+ return dayjs(date).format("YYYY-MM-DD HH:mm");
+}