mirror of https://github.com/halo-dev/halo
feat: add search and filtering functions to post tags (#5710)
parent
06e0b63b5b
commit
a692c622c9
|
@ -3585,6 +3585,88 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"/apis/api.console.halo.run/v1alpha1/tags": {
|
||||
"get": {
|
||||
"description": "List Post Tags.",
|
||||
"operationId": "ListPostTags",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Page number. Default is 0.",
|
||||
"in": "query",
|
||||
"name": "page",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Size number. Default is 0.",
|
||||
"in": "query",
|
||||
"name": "size",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Label selector. e.g.: hidden!\u003dtrue",
|
||||
"in": "query",
|
||||
"name": "labelSelector",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Field selector. e.g.: metadata.name\u003d\u003dhalo",
|
||||
"in": "query",
|
||||
"name": "fieldSelector",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Sorting criteria in the format: property,(asc|desc). Default sort order is ascending. Multiple sort criteria are supported.",
|
||||
"in": "query",
|
||||
"name": "sort",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Post tags filtered by keyword.",
|
||||
"in": "query",
|
||||
"name": "keyword",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"default": {
|
||||
"content": {
|
||||
"*/*": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TagList"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "default response"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"api.console.halo.run/v1alpha1/Tag"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/apis/api.console.halo.run/v1alpha1/themes": {
|
||||
"get": {
|
||||
"description": "List themes.",
|
||||
|
@ -13467,6 +13549,10 @@
|
|||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"observedVersion": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"replyCount": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
|
@ -16437,8 +16523,7 @@
|
|||
"RESOLVED",
|
||||
"STARTED",
|
||||
"STOPPED",
|
||||
"FAILED",
|
||||
"UNLOADED"
|
||||
"FAILED"
|
||||
]
|
||||
},
|
||||
"lastStartTime": {
|
||||
|
@ -17469,7 +17554,8 @@
|
|||
"apiVersion",
|
||||
"kind",
|
||||
"metadata",
|
||||
"spec"
|
||||
"spec",
|
||||
"status"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -17484,6 +17570,9 @@
|
|||
},
|
||||
"spec": {
|
||||
"$ref": "#/components/schemas/ReplySpec"
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/components/schemas/ReplyStatus"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -17642,6 +17731,15 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"ReplyStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"observedVersion": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ReplyVo": {
|
||||
"required": [
|
||||
"metadata",
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
package run.halo.app.core.extension.endpoint;
|
||||
|
||||
import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder;
|
||||
import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder;
|
||||
import static run.halo.app.extension.index.query.QueryFactory.all;
|
||||
import static run.halo.app.extension.router.QueryParamBuildUtil.sortParameter;
|
||||
import static run.halo.app.extension.router.selector.SelectorUtil.labelAndFieldSelectorToListOptions;
|
||||
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.util.Optional;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springdoc.core.fn.builders.operation.Builder;
|
||||
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.core.extension.content.Tag;
|
||||
import run.halo.app.extension.ListOptions;
|
||||
import run.halo.app.extension.ListResult;
|
||||
import run.halo.app.extension.PageRequestImpl;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
import run.halo.app.extension.index.query.QueryFactory;
|
||||
import run.halo.app.extension.router.IListRequest;
|
||||
|
||||
/**
|
||||
* post tag endpoint.
|
||||
*
|
||||
* @author LIlGG
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class TagEndpoint implements CustomEndpoint {
|
||||
|
||||
private final ReactiveExtensionClient client;
|
||||
|
||||
@Override
|
||||
public RouterFunction<ServerResponse> endpoint() {
|
||||
final var tag = "api.console.halo.run/v1alpha1/Tag";
|
||||
return SpringdocRouteBuilder.route()
|
||||
.GET("tags", this::listTag, builder -> {
|
||||
builder.operationId("ListPostTags")
|
||||
.description("List Post Tags.")
|
||||
.tag(tag)
|
||||
.response(
|
||||
responseBuilder()
|
||||
.implementation(ListResult.generateGenericClass(Tag.class))
|
||||
);
|
||||
TagQuery.buildParameters(builder);
|
||||
}
|
||||
)
|
||||
.build();
|
||||
}
|
||||
|
||||
Mono<ServerResponse> listTag(ServerRequest request) {
|
||||
var tagQuery = new TagQuery(request);
|
||||
return client.listBy(Tag.class, tagQuery.toListOptions(),
|
||||
PageRequestImpl.of(tagQuery.getPage(), tagQuery.getSize(), tagQuery.getSort())
|
||||
)
|
||||
.flatMap(tags -> ServerResponse.ok().bodyValue(tags));
|
||||
}
|
||||
|
||||
public interface ITagQuery extends IListRequest {
|
||||
|
||||
@Schema(description = "Keyword for searching.")
|
||||
Optional<String> getKeyword();
|
||||
|
||||
@ArraySchema(uniqueItems = true,
|
||||
arraySchema = @Schema(name = "sort",
|
||||
description = "Sort property and direction of the list result. Supported fields: "
|
||||
+ "creationTimestamp, name"),
|
||||
schema = @Schema(description = "like field,asc or field,desc",
|
||||
implementation = String.class,
|
||||
example = "creationTimestamp,desc"))
|
||||
Sort getSort();
|
||||
}
|
||||
|
||||
public static class TagQuery extends IListRequest.QueryListRequest
|
||||
implements ITagQuery {
|
||||
|
||||
private final ServerWebExchange exchange;
|
||||
|
||||
public TagQuery(ServerRequest request) {
|
||||
super(request.queryParams());
|
||||
this.exchange = request.exchange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getKeyword() {
|
||||
return Optional.ofNullable(queryParams.getFirst("keyword"))
|
||||
.filter(StringUtils::hasText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sort getSort() {
|
||||
var sort = SortResolver.defaultInstance.resolve(exchange);
|
||||
sort = sort.and(Sort.by(
|
||||
Sort.Order.desc("metadata.creationTimestamp"),
|
||||
Sort.Order.asc("metadata.name")
|
||||
));
|
||||
return sort;
|
||||
}
|
||||
|
||||
public ListOptions toListOptions() {
|
||||
final var listOptions =
|
||||
labelAndFieldSelectorToListOptions(getLabelSelector(), getFieldSelector());
|
||||
|
||||
var fieldQuery = all();
|
||||
if (getKeyword().isPresent()) {
|
||||
fieldQuery = QueryFactory.and(fieldQuery, QueryFactory.or(
|
||||
QueryFactory.contains("spec.displayName", getKeyword().get()),
|
||||
QueryFactory.contains("spec.slug", getKeyword().get())
|
||||
));
|
||||
}
|
||||
|
||||
listOptions.setFieldSelector(listOptions.getFieldSelector().andQuery(fieldQuery));
|
||||
return listOptions;
|
||||
}
|
||||
|
||||
public static void buildParameters(Builder builder) {
|
||||
IListRequest.buildParameters(builder);
|
||||
builder.parameter(sortParameter())
|
||||
.parameter(parameterBuilder()
|
||||
.in(ParameterIn.QUERY)
|
||||
.name("keyword")
|
||||
.description("Post tags filtered by keyword.")
|
||||
.implementation(String.class)
|
||||
.required(false));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -226,6 +226,9 @@ public class SchemeInitializer implements ApplicationListener<ApplicationContext
|
|||
category -> defaultIfNull(category.getSpec().getPriority(), 0).toString())));
|
||||
});
|
||||
schemeManager.register(Tag.class, indexSpecs -> {
|
||||
indexSpecs.add(new IndexSpec()
|
||||
.setName("spec.displayName")
|
||||
.setIndexFunc(simpleAttribute(Tag.class, tag -> tag.getSpec().getDisplayName())));
|
||||
indexSpecs.add(new IndexSpec()
|
||||
.setName("spec.slug")
|
||||
.setIndexFunc(simpleAttribute(Tag.class, tag -> tag.getSpec().getSlug()))
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
package run.halo.app.core.extension.endpoint;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;
|
||||
import static org.springframework.test.web.reactive.server.WebTestClient.bindToRouterFunction;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.core.extension.content.Tag;
|
||||
import run.halo.app.extension.ListResult;
|
||||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.extension.PageRequest;
|
||||
import run.halo.app.extension.ReactiveExtensionClient;
|
||||
|
||||
/**
|
||||
* Tag endpoint test.
|
||||
*
|
||||
* @author LIlGG
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class TagEndpointTest {
|
||||
@Mock
|
||||
ReactiveExtensionClient client;
|
||||
|
||||
@InjectMocks
|
||||
TagEndpoint tagEndpoint;
|
||||
|
||||
WebTestClient webClient;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
webClient = WebTestClient.bindToRouterFunction(tagEndpoint.endpoint())
|
||||
.apply(springSecurity())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Nested
|
||||
class TagListTest {
|
||||
|
||||
@Test
|
||||
void shouldListEmptyTagsWhenNoTags() {
|
||||
when(client.listBy(same(Tag.class), any(), any(PageRequest.class)))
|
||||
.thenReturn(Mono.just(ListResult.emptyResult()));
|
||||
|
||||
bindToRouterFunction(tagEndpoint.endpoint())
|
||||
.build()
|
||||
.get().uri("/tags")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.items.length()").isEqualTo(0)
|
||||
.jsonPath("$.total").isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldListTagsWhenTagPresent() {
|
||||
var tags = List.of(
|
||||
createTag("fake-tag-1"),
|
||||
createTag("fake-tag-2")
|
||||
);
|
||||
var expectResult = new ListResult<>(tags);
|
||||
when(client.listBy(same(Tag.class), any(), any(PageRequest.class)))
|
||||
.thenReturn(Mono.just(expectResult));
|
||||
|
||||
bindToRouterFunction(tagEndpoint.endpoint())
|
||||
.build()
|
||||
.get().uri("/tags")
|
||||
.exchange()
|
||||
.expectStatus().isOk()
|
||||
.expectBody()
|
||||
.jsonPath("$.items.length()").isEqualTo(2)
|
||||
.jsonPath("$.total").isEqualTo(2);
|
||||
}
|
||||
|
||||
Tag createTag(String name) {
|
||||
return createTag(name, "fake display name");
|
||||
}
|
||||
|
||||
Tag createTag(String name, String displayName) {
|
||||
var metadata = new Metadata();
|
||||
metadata.setName(name);
|
||||
metadata.setCreationTimestamp(Instant.now());
|
||||
var spec = new Tag.TagSpec();
|
||||
spec.setDisplayName(displayName);
|
||||
spec.setSlug(name);
|
||||
var tag = new Tag();
|
||||
tag.setMetadata(metadata);
|
||||
tag.setSpec(spec);
|
||||
return tag;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import {
|
|||
VSpace,
|
||||
VLoading,
|
||||
VPagination,
|
||||
IconRefreshLine,
|
||||
} from "@halo-dev/components";
|
||||
import HasPermission from "@/components/permission/HasPermission.vue";
|
||||
import TagEditingModal from "./components/TagEditingModal.vue";
|
||||
|
@ -18,6 +19,12 @@ import { useRouteQuery } from "@vueuse/router";
|
|||
import { apiClient } from "@/utils/api-client";
|
||||
import { usePostTag } from "./composables/use-post-tag";
|
||||
import TagListItem from "./components/TagListItem.vue";
|
||||
import SearchInput from "@/components/input/SearchInput.vue";
|
||||
import FilterCleanButton from "@/components/filter/FilterCleanButton.vue";
|
||||
import { computed } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const editingModal = ref(false);
|
||||
const selectedTag = ref<Tag | null>(null);
|
||||
|
@ -25,12 +32,22 @@ const selectedTag = ref<Tag | null>(null);
|
|||
const selectedTagNames = ref<string[]>([]);
|
||||
const checkedAll = ref(false);
|
||||
|
||||
const keyword = useRouteQuery<string>("keyword", "");
|
||||
const page = useRouteQuery<number>("page", 1, {
|
||||
transform: Number,
|
||||
});
|
||||
const size = useRouteQuery<number>("size", 20, {
|
||||
transform: Number,
|
||||
});
|
||||
const selectedSort = useRouteQuery<string | undefined>("sort");
|
||||
|
||||
const hasFilters = computed(() => {
|
||||
return !!selectedSort.value;
|
||||
});
|
||||
|
||||
const handleClearFilters = () => {
|
||||
selectedSort.value = undefined;
|
||||
};
|
||||
|
||||
const {
|
||||
tags,
|
||||
|
@ -38,12 +55,15 @@ const {
|
|||
hasNext,
|
||||
hasPrevious,
|
||||
isLoading,
|
||||
isFetching,
|
||||
handleFetchTags,
|
||||
handleDelete,
|
||||
handleDeleteInBatch,
|
||||
} = usePostTag({
|
||||
page,
|
||||
size,
|
||||
keyword,
|
||||
sort: selectedSort,
|
||||
});
|
||||
|
||||
const handleOpenEditingModal = (tag: Tag | null) => {
|
||||
|
@ -66,10 +86,6 @@ const handleCheckAllChange = () => {
|
|||
};
|
||||
|
||||
const handleSelectPrevious = async () => {
|
||||
if (!hasPrevious.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tags.value) return;
|
||||
|
||||
const currentIndex = tags.value.findIndex(
|
||||
|
@ -84,15 +100,14 @@ const handleSelectPrevious = async () => {
|
|||
if (currentIndex === 0 && hasPrevious.value) {
|
||||
page.value--;
|
||||
await handleFetchTags();
|
||||
selectedTag.value = tags.value[tags.value.length - 1];
|
||||
setTimeout(() => {
|
||||
if (!tags.value) return;
|
||||
selectedTag.value = tags.value[tags.value.length - 1];
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectNext = async () => {
|
||||
if (!hasNext.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tags.value) return;
|
||||
|
||||
if (!selectedTag.value) {
|
||||
|
@ -109,7 +124,10 @@ const handleSelectNext = async () => {
|
|||
if (currentIndex === tags.value.length - 1 && hasNext.value) {
|
||||
page.value++;
|
||||
await handleFetchTags();
|
||||
selectedTag.value = tags.value[0];
|
||||
setTimeout(() => {
|
||||
if (!tags.value) return;
|
||||
selectedTag.value = tags.value[0];
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -184,7 +202,59 @@ watch(selectedTagNames, (newVal) => {
|
|||
{{ $t("core.common.buttons.delete") }}
|
||||
</VButton>
|
||||
</VSpace>
|
||||
<SearchInput v-else v-model="keyword" />
|
||||
</div>
|
||||
<VSpace spacing="lg" class="flex-wrap">
|
||||
<FilterCleanButton
|
||||
v-if="hasFilters"
|
||||
@click="handleClearFilters"
|
||||
/>
|
||||
<FilterDropdown
|
||||
v-model="selectedSort"
|
||||
:label="$t('core.common.filters.labels.sort')"
|
||||
:items="[
|
||||
{
|
||||
label: t('core.common.filters.item_labels.default'),
|
||||
},
|
||||
{
|
||||
label: t(
|
||||
'core.post.tag.filters.sort.items.create_time_desc'
|
||||
),
|
||||
value: 'metadata.creationTimestamp,desc',
|
||||
},
|
||||
{
|
||||
label: t(
|
||||
'core.post.tag.filters.sort.items.create_time_asc'
|
||||
),
|
||||
value: 'metadata.creationTimestamp,asc',
|
||||
},
|
||||
{
|
||||
label: t(
|
||||
'core.post.tag.filters.sort.items.display_name_desc'
|
||||
),
|
||||
value: 'spec.displayName,desc',
|
||||
},
|
||||
{
|
||||
label: t(
|
||||
'core.post.tag.filters.sort.items.display_name_asc'
|
||||
),
|
||||
value: 'spec.displayName,asc',
|
||||
},
|
||||
]"
|
||||
/>
|
||||
<div class="flex flex-row gap-2">
|
||||
<div
|
||||
class="group cursor-pointer rounded p-1 hover:bg-gray-200"
|
||||
@click="handleFetchTags()"
|
||||
>
|
||||
<IconRefreshLine
|
||||
v-tooltip="$t('core.common.buttons.refresh')"
|
||||
:class="{ 'animate-spin text-gray-900': isFetching }"
|
||||
class="h-4 w-4 text-gray-600 group-hover:text-gray-900"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</VSpace>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { apiClient } from "@/utils/api-client";
|
||||
import type { Tag } from "@halo-dev/api-client";
|
||||
import { ref, type Ref } from "vue";
|
||||
import { ref, watch, type Ref } from "vue";
|
||||
import { Dialog, Toast } from "@halo-dev/components";
|
||||
import { useQuery, type QueryObserverResult } from "@tanstack/vue-query";
|
||||
import { useI18n } from "vue-i18n";
|
||||
|
@ -11,19 +11,21 @@ interface usePostTagReturn {
|
|||
hasPrevious: Ref<boolean>;
|
||||
hasNext: Ref<boolean>;
|
||||
isLoading: Ref<boolean>;
|
||||
isFetching: Ref<boolean>;
|
||||
handleFetchTags: () => Promise<QueryObserverResult<Tag[], unknown>>;
|
||||
handleDelete: (tag: Tag) => void;
|
||||
handleDeleteInBatch: (tagNames: string[]) => Promise<void>;
|
||||
}
|
||||
|
||||
export function usePostTag(filterOptions?: {
|
||||
sort?: Ref<string[]>;
|
||||
sort?: Ref<string | undefined>;
|
||||
page?: Ref<number>;
|
||||
size?: Ref<number>;
|
||||
keyword?: Ref<string>;
|
||||
}): usePostTagReturn {
|
||||
const { t } = useI18n();
|
||||
|
||||
const { sort, page, size } = filterOptions || {};
|
||||
const { sort, page, size, keyword } = filterOptions || {};
|
||||
|
||||
const total = ref(0);
|
||||
const hasPrevious = ref(false);
|
||||
|
@ -32,16 +34,19 @@ export function usePostTag(filterOptions?: {
|
|||
const {
|
||||
data: tags,
|
||||
isLoading,
|
||||
isFetching,
|
||||
refetch,
|
||||
} = useQuery({
|
||||
queryKey: ["post-tags", sort, page, size],
|
||||
queryKey: ["post-tags", sort, page, size, keyword],
|
||||
queryFn: async () => {
|
||||
const { data } =
|
||||
await apiClient.extension.tag.listcontentHaloRunV1alpha1Tag({
|
||||
page: page?.value || 0,
|
||||
size: size?.value || 0,
|
||||
sort: sort?.value || ["metadata.creationTimestamp,desc"],
|
||||
});
|
||||
const { data } = await apiClient.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;
|
||||
|
@ -110,12 +115,22 @@ export function usePostTag(filterOptions?: {
|
|||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => [sort?.value, keyword?.value],
|
||||
() => {
|
||||
if (page?.value) {
|
||||
page.value = 1;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
tags,
|
||||
total,
|
||||
hasPrevious,
|
||||
hasNext,
|
||||
isLoading,
|
||||
isFetching,
|
||||
handleFetchTags: refetch,
|
||||
handleDelete,
|
||||
handleDeleteInBatch,
|
||||
|
|
|
@ -12,6 +12,7 @@ api/api-console-halo-run-v1alpha1-reply-api.ts
|
|||
api/api-console-halo-run-v1alpha1-single-page-api.ts
|
||||
api/api-console-halo-run-v1alpha1-stats-api.ts
|
||||
api/api-console-halo-run-v1alpha1-system-api.ts
|
||||
api/api-console-halo-run-v1alpha1-tag-api.ts
|
||||
api/api-console-halo-run-v1alpha1-theme-api.ts
|
||||
api/api-console-halo-run-v1alpha1-user-api.ts
|
||||
api/api-console-migration-halo-run-v1alpha1-migration-api.ts
|
||||
|
|
|
@ -25,6 +25,7 @@ export * from './api/api-console-halo-run-v1alpha1-reply-api';
|
|||
export * from './api/api-console-halo-run-v1alpha1-single-page-api';
|
||||
export * from './api/api-console-halo-run-v1alpha1-stats-api';
|
||||
export * from './api/api-console-halo-run-v1alpha1-system-api';
|
||||
export * from './api/api-console-halo-run-v1alpha1-tag-api';
|
||||
export * from './api/api-console-halo-run-v1alpha1-theme-api';
|
||||
export * from './api/api-console-halo-run-v1alpha1-user-api';
|
||||
export * from './api/api-console-migration-halo-run-v1alpha1-migration-api';
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Halo Next API
|
||||
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
||||
*
|
||||
* The version of the OpenAPI document: 2.0.0
|
||||
*
|
||||
*
|
||||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
||||
* https://openapi-generator.tech
|
||||
* Do not edit the class manually.
|
||||
*/
|
||||
|
||||
|
||||
import type { Configuration } from '../configuration';
|
||||
import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios';
|
||||
import globalAxios from 'axios';
|
||||
// Some imports not used depending on template conditions
|
||||
// @ts-ignore
|
||||
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common';
|
||||
// @ts-ignore
|
||||
import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base';
|
||||
// @ts-ignore
|
||||
import { TagList } from '../models';
|
||||
/**
|
||||
* ApiConsoleHaloRunV1alpha1TagApi - axios parameter creator
|
||||
* @export
|
||||
*/
|
||||
export const ApiConsoleHaloRunV1alpha1TagApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
* List Post Tags.
|
||||
* @param {Array<string>} [fieldSelector] Field selector for filtering.
|
||||
* @param {string} [keyword] Keyword for searching.
|
||||
* @param {Array<string>} [labelSelector] Label selector for filtering.
|
||||
* @param {number} [page] The page number. Zero indicates no page.
|
||||
* @param {number} [size] Size of one page. Zero indicates no limit.
|
||||
* @param {Array<string>} [sort] Sort property and direction of the list result. Supported fields: creationTimestamp, name
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listPostTags: async (fieldSelector?: Array<string>, keyword?: string, labelSelector?: Array<string>, page?: number, size?: number, sort?: Array<string>, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/apis/api.console.halo.run/v1alpha1/tags`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication BasicAuth required
|
||||
// http basic authentication required
|
||||
setBasicAuthToObject(localVarRequestOptions, configuration)
|
||||
|
||||
// authentication BearerAuth required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
if (fieldSelector) {
|
||||
localVarQueryParameter['fieldSelector'] = fieldSelector;
|
||||
}
|
||||
|
||||
if (keyword !== undefined) {
|
||||
localVarQueryParameter['keyword'] = keyword;
|
||||
}
|
||||
|
||||
if (labelSelector) {
|
||||
localVarQueryParameter['labelSelector'] = labelSelector;
|
||||
}
|
||||
|
||||
if (page !== undefined) {
|
||||
localVarQueryParameter['page'] = page;
|
||||
}
|
||||
|
||||
if (size !== undefined) {
|
||||
localVarQueryParameter['size'] = size;
|
||||
}
|
||||
|
||||
if (sort) {
|
||||
localVarQueryParameter['sort'] = Array.from(sort);
|
||||
}
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* ApiConsoleHaloRunV1alpha1TagApi - functional programming interface
|
||||
* @export
|
||||
*/
|
||||
export const ApiConsoleHaloRunV1alpha1TagApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = ApiConsoleHaloRunV1alpha1TagApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
* List Post Tags.
|
||||
* @param {Array<string>} [fieldSelector] Field selector for filtering.
|
||||
* @param {string} [keyword] Keyword for searching.
|
||||
* @param {Array<string>} [labelSelector] Label selector for filtering.
|
||||
* @param {number} [page] The page number. Zero indicates no page.
|
||||
* @param {number} [size] Size of one page. Zero indicates no limit.
|
||||
* @param {Array<string>} [sort] Sort property and direction of the list result. Supported fields: creationTimestamp, name
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async listPostTags(fieldSelector?: Array<string>, keyword?: string, labelSelector?: Array<string>, page?: number, size?: number, sort?: Array<string>, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<TagList>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listPostTags(fieldSelector, keyword, labelSelector, page, size, sort, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['ApiConsoleHaloRunV1alpha1TagApi.listPostTags']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* ApiConsoleHaloRunV1alpha1TagApi - factory interface
|
||||
* @export
|
||||
*/
|
||||
export const ApiConsoleHaloRunV1alpha1TagApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = ApiConsoleHaloRunV1alpha1TagApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
* List Post Tags.
|
||||
* @param {ApiConsoleHaloRunV1alpha1TagApiListPostTagsRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listPostTags(requestParameters: ApiConsoleHaloRunV1alpha1TagApiListPostTagsRequest = {}, options?: RawAxiosRequestConfig): AxiosPromise<TagList> {
|
||||
return localVarFp.listPostTags(requestParameters.fieldSelector, requestParameters.keyword, requestParameters.labelSelector, requestParameters.page, requestParameters.size, requestParameters.sort, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Request parameters for listPostTags operation in ApiConsoleHaloRunV1alpha1TagApi.
|
||||
* @export
|
||||
* @interface ApiConsoleHaloRunV1alpha1TagApiListPostTagsRequest
|
||||
*/
|
||||
export interface ApiConsoleHaloRunV1alpha1TagApiListPostTagsRequest {
|
||||
/**
|
||||
* Field selector for filtering.
|
||||
* @type {Array<string>}
|
||||
* @memberof ApiConsoleHaloRunV1alpha1TagApiListPostTags
|
||||
*/
|
||||
readonly fieldSelector?: Array<string>
|
||||
|
||||
/**
|
||||
* Keyword for searching.
|
||||
* @type {string}
|
||||
* @memberof ApiConsoleHaloRunV1alpha1TagApiListPostTags
|
||||
*/
|
||||
readonly keyword?: string
|
||||
|
||||
/**
|
||||
* Label selector for filtering.
|
||||
* @type {Array<string>}
|
||||
* @memberof ApiConsoleHaloRunV1alpha1TagApiListPostTags
|
||||
*/
|
||||
readonly labelSelector?: Array<string>
|
||||
|
||||
/**
|
||||
* The page number. Zero indicates no page.
|
||||
* @type {number}
|
||||
* @memberof ApiConsoleHaloRunV1alpha1TagApiListPostTags
|
||||
*/
|
||||
readonly page?: number
|
||||
|
||||
/**
|
||||
* Size of one page. Zero indicates no limit.
|
||||
* @type {number}
|
||||
* @memberof ApiConsoleHaloRunV1alpha1TagApiListPostTags
|
||||
*/
|
||||
readonly size?: number
|
||||
|
||||
/**
|
||||
* Sort property and direction of the list result. Supported fields: creationTimestamp, name
|
||||
* @type {Array<string>}
|
||||
* @memberof ApiConsoleHaloRunV1alpha1TagApiListPostTags
|
||||
*/
|
||||
readonly sort?: Array<string>
|
||||
}
|
||||
|
||||
/**
|
||||
* ApiConsoleHaloRunV1alpha1TagApi - object-oriented interface
|
||||
* @export
|
||||
* @class ApiConsoleHaloRunV1alpha1TagApi
|
||||
* @extends {BaseAPI}
|
||||
*/
|
||||
export class ApiConsoleHaloRunV1alpha1TagApi extends BaseAPI {
|
||||
/**
|
||||
* List Post Tags.
|
||||
* @param {ApiConsoleHaloRunV1alpha1TagApiListPostTagsRequest} requestParameters Request parameters.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof ApiConsoleHaloRunV1alpha1TagApi
|
||||
*/
|
||||
public listPostTags(requestParameters: ApiConsoleHaloRunV1alpha1TagApiListPostTagsRequest = {}, options?: RawAxiosRequestConfig) {
|
||||
return ApiConsoleHaloRunV1alpha1TagApiFp(this.configuration).listPostTags(requestParameters.fieldSelector, requestParameters.keyword, requestParameters.labelSelector, requestParameters.page, requestParameters.size, requestParameters.sort, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
|
@ -260,6 +260,14 @@ core:
|
|||
label: Template
|
||||
cover:
|
||||
label: Cover
|
||||
tag:
|
||||
filters:
|
||||
sort:
|
||||
items:
|
||||
create_time_desc: Latest Created
|
||||
create_time_asc: Earliest Created
|
||||
display_name_desc: Descending order by tag name
|
||||
display_name_asc: Ascending order by tag name
|
||||
deleted_post:
|
||||
title: Deleted Posts
|
||||
empty:
|
||||
|
|
|
@ -252,6 +252,14 @@ core:
|
|||
label: 自定义模板
|
||||
cover:
|
||||
label: 封面图
|
||||
tag:
|
||||
filters:
|
||||
sort:
|
||||
items:
|
||||
create_time_desc: 较近创建
|
||||
create_time_asc: 较早创建
|
||||
display_name_desc: 标签名降序
|
||||
display_name_asc: 标签名升序
|
||||
deleted_post:
|
||||
title: 文章回收站
|
||||
empty:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
ApiConsoleHaloRunV1alpha1PluginApi,
|
||||
ApiConsoleHaloRunV1alpha1PostApi,
|
||||
ApiConsoleHaloRunV1alpha1TagApi,
|
||||
ApiConsoleHaloRunV1alpha1SinglePageApi,
|
||||
ApiConsoleHaloRunV1alpha1ThemeApi,
|
||||
ApiConsoleHaloRunV1alpha1UserApi,
|
||||
|
@ -207,6 +208,7 @@ function setupApiClient(axios: AxiosInstance) {
|
|||
plugin: new ApiConsoleHaloRunV1alpha1PluginApi(undefined, baseURL, axios),
|
||||
theme: new ApiConsoleHaloRunV1alpha1ThemeApi(undefined, baseURL, axios),
|
||||
post: new ApiConsoleHaloRunV1alpha1PostApi(undefined, baseURL, axios),
|
||||
tag: new ApiConsoleHaloRunV1alpha1TagApi(undefined, baseURL, axios),
|
||||
singlePage: new ApiConsoleHaloRunV1alpha1SinglePageApi(
|
||||
undefined,
|
||||
baseURL,
|
||||
|
|
Loading…
Reference in New Issue