-
-
- {{
- $t("core.post_tag.header.title", { count: tags?.length || 0 })
- }}
-
-
-
-
-
+
+
+
+
+
+
+
+ {{ $t("core.common.buttons.delete") }}
+
+
@@ -172,7 +196,7 @@ onMounted(async () => {
>
-
+ handleFetchTags">
{{ $t("core.common.buttons.refresh") }}
@@ -186,93 +210,42 @@ onMounted(async () => {
-
-
-
- -
-
-
-
-
-
-
-
-
- {{ tag.status.permalink }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ formatDatetime(tag.metadata.creationTimestamp) }}
-
-
-
-
-
-
- {{ $t("core.common.buttons.edit") }}
-
-
- {{ $t("core.common.buttons.delete") }}
-
-
-
-
-
-
-
-
-
-
-
+ :is-selected="selectedTag?.metadata.name === tag.metadata.name"
+ @editing="handleOpenEditingModal"
+ @delete="handleDelete"
+ >
+
+
+
+
+
+
+
+
+
+
diff --git a/ui/console-src/modules/contents/posts/tags/components/TagListItem.vue b/ui/console-src/modules/contents/posts/tags/components/TagListItem.vue
new file mode 100644
index 0000000000..479c1fab3e
--- /dev/null
+++ b/ui/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/ui/console-src/modules/contents/posts/tags/composables/use-post-tag.ts b/ui/console-src/modules/contents/posts/tags/composables/use-post-tag.ts
index 494e36ac61..7e73850e23 100644
--- a/ui/console-src/modules/contents/posts/tags/composables/use-post-tag.ts
+++ b/ui/console-src/modules/contents/posts/tags/composables/use-post-tag.ts
@@ -1,34 +1,52 @@
import { apiClient } from "@/utils/api-client";
import type { Tag } from "@halo-dev/api-client";
-import type { Ref } from "vue";
+import { ref, type Ref } from "vue";
import { Dialog, Toast } from "@halo-dev/components";
-import { useQuery } from "@tanstack/vue-query";
+import { useQuery, type QueryObserverResult } from "@tanstack/vue-query";
import { useI18n } from "vue-i18n";
interface usePostTagReturn {
tags: Ref
;
+ total: Ref;
+ hasPrevious: Ref;
+ hasNext: Ref;
isLoading: Ref;
- handleFetchTags: () => void;
+ handleFetchTags: () => Promise>;
handleDelete: (tag: Tag) => void;
+ handleDeleteInBatch: (tagNames: string[]) => Promise;
}
-export function usePostTag(): usePostTagReturn {
+export function usePostTag(filterOptions?: {
+ sort?: Ref;
+ page?: Ref;
+ size?: Ref;
+}): usePostTagReturn {
const { t } = useI18n();
+ const { sort, page, size } = filterOptions || {};
+
+ const total = ref(0);
+ const hasPrevious = ref(false);
+ const hasNext = ref(false);
+
const {
data: tags,
isLoading,
refetch,
} = useQuery({
- queryKey: ["post-tags"],
+ queryKey: ["post-tags", sort, page, size],
queryFn: async () => {
const { data } =
await apiClient.extension.tag.listcontentHaloRunV1alpha1Tag({
- page: 0,
- size: 0,
- sort: ["metadata.creationTimestamp,desc"],
+ page: page?.value || 0,
+ size: size?.value || 0,
+ sort: sort?.value || ["metadata.creationTimestamp,desc"],
});
+ total.value = data.total;
+ hasPrevious.value = data.hasPrevious;
+ hasNext.value = data.hasNext;
+
return data.items;
},
refetchInterval(data) {
@@ -62,10 +80,44 @@ export function usePostTag(): usePostTagReturn {
});
};
+ 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) => {
+ apiClient.extension.tag.deletecontentHaloRunV1alpha1Tag({
+ 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();
+ }
+ },
+ });
+ });
+ };
+
return {
tags,
+ total,
+ hasPrevious,
+ hasNext,
isLoading,
handleFetchTags: refetch,
handleDelete,
+ handleDeleteInBatch,
};
}
diff --git a/ui/src/locales/en.yaml b/ui/src/locales/en.yaml
index 05647d2705..37cd51cf38 100644
--- a/ui/src/locales/en.yaml
+++ b/ui/src/locales/en.yaml
@@ -290,6 +290,8 @@ core:
description: >-
After deleting this tag, the association with the corresponding
article will be removed. This operation cannot be undone.
+ delete_in_batch:
+ title: Delete the selected tags
editing_modal:
titles:
update: Update post tag
diff --git a/ui/src/locales/zh-CN.yaml b/ui/src/locales/zh-CN.yaml
index 4b3821e9e0..56e73346ab 100644
--- a/ui/src/locales/zh-CN.yaml
+++ b/ui/src/locales/zh-CN.yaml
@@ -292,6 +292,8 @@ core:
delete:
title: 删除标签
description: 删除此标签之后,对应文章的关联将被解除。该操作不可恢复。
+ delete_in_batch:
+ title: 删除所选标签
editing_modal:
titles:
update: 编辑文章标签
diff --git a/ui/src/locales/zh-TW.yaml b/ui/src/locales/zh-TW.yaml
index f2a77ee99b..2d41a1ceec 100644
--- a/ui/src/locales/zh-TW.yaml
+++ b/ui/src/locales/zh-TW.yaml
@@ -280,6 +280,8 @@ core:
delete:
title: 刪除標籤
description: 刪除此標籤之後,對應文章的關聯將被解除。該操作不可恢復。
+ delete_in_batch:
+ title: 刪除所選標籤
editing_modal:
titles:
update: 編輯文章標籤