mirror of https://github.com/halo-dev/halo-admin
fix: splash screen issue caused by refreshing the list at regular intervals (#708)
#### What type of PR is this? /kind bug /milestone 2.0 #### What this PR does / why we need it: 修复因为 https://github.com/halo-dev/console/pull/703 中添加了 Loading 动画但是没有考虑到定时刷新列表导致页面闪动的问题。 #### Special notes for your reviewer: /cc @halo-dev/sig-halo-console 测试方式:删除任意资源,检查列表定时刷新的时候是否出现 loading 状态。 #### Does this PR introduce a user-facing change? ```release-note None ```pull/709/head^2
parent
becafc2cbd
commit
db187f14eb
|
@ -93,17 +93,17 @@ const selectedSortItemValue = computed(() => {
|
||||||
|
|
||||||
function handleSelectPolicy(policy: Policy | undefined) {
|
function handleSelectPolicy(policy: Policy | undefined) {
|
||||||
selectedPolicy.value = policy;
|
selectedPolicy.value = policy;
|
||||||
handleFetchAttachments(1);
|
handleFetchAttachments({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSelectUser(user: User | undefined) {
|
function handleSelectUser(user: User | undefined) {
|
||||||
selectedUser.value = user;
|
selectedUser.value = user;
|
||||||
handleFetchAttachments(1);
|
handleFetchAttachments({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSortItemChange(sortItem?: SortItem) {
|
function handleSortItemChange(sortItem?: SortItem) {
|
||||||
selectedSortItem.value = sortItem;
|
selectedSortItem.value = sortItem;
|
||||||
handleFetchAttachments(1);
|
handleFetchAttachments({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeywordChange() {
|
function handleKeywordChange() {
|
||||||
|
@ -111,12 +111,12 @@ function handleKeywordChange() {
|
||||||
if (keywordNode) {
|
if (keywordNode) {
|
||||||
keyword.value = keywordNode._value as string;
|
keyword.value = keywordNode._value as string;
|
||||||
}
|
}
|
||||||
handleFetchAttachments(1);
|
handleFetchAttachments({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClearKeyword() {
|
function handleClearKeyword() {
|
||||||
keyword.value = "";
|
keyword.value = "";
|
||||||
handleFetchAttachments(1);
|
handleFetchAttachments({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasFilters = computed(() => {
|
const hasFilters = computed(() => {
|
||||||
|
@ -133,7 +133,7 @@ function handleClearFilters() {
|
||||||
selectedUser.value = undefined;
|
selectedUser.value = undefined;
|
||||||
selectedSortItem.value = undefined;
|
selectedSortItem.value = undefined;
|
||||||
keyword.value = "";
|
keyword.value = "";
|
||||||
handleFetchAttachments(1);
|
handleFetchAttachments({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -208,16 +208,12 @@ const onDetailModalClose = () => {
|
||||||
selectedAttachment.value = undefined;
|
selectedAttachment.value = undefined;
|
||||||
nameQuery.value = undefined;
|
nameQuery.value = undefined;
|
||||||
nameQueryAttachment.value = undefined;
|
nameQueryAttachment.value = undefined;
|
||||||
setTimeout(() => {
|
handleFetchAttachments({ mute: true });
|
||||||
handleFetchAttachments();
|
|
||||||
}, 200);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onUploadModalClose = () => {
|
const onUploadModalClose = () => {
|
||||||
routeQueryAction.value = undefined;
|
routeQueryAction.value = undefined;
|
||||||
setTimeout(() => {
|
handleFetchAttachments({ mute: true });
|
||||||
handleFetchAttachments();
|
|
||||||
}, 200);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onGroupChange = () => {
|
const onGroupChange = () => {
|
||||||
|
|
|
@ -19,7 +19,7 @@ interface useAttachmentControlReturn {
|
||||||
selectedAttachment: Ref<Attachment | undefined>;
|
selectedAttachment: Ref<Attachment | undefined>;
|
||||||
selectedAttachments: Ref<Set<Attachment>>;
|
selectedAttachments: Ref<Set<Attachment>>;
|
||||||
checkedAll: Ref<boolean>;
|
checkedAll: Ref<boolean>;
|
||||||
handleFetchAttachments: (page?: number) => void;
|
handleFetchAttachments: (options?: { mute?: boolean; page?: number }) => void;
|
||||||
handlePaginationChange: ({
|
handlePaginationChange: ({
|
||||||
page,
|
page,
|
||||||
size,
|
size,
|
||||||
|
@ -68,14 +68,19 @@ export function useAttachmentControl(filterOptions?: {
|
||||||
const checkedAll = ref(false);
|
const checkedAll = ref(false);
|
||||||
const refreshInterval = ref();
|
const refreshInterval = ref();
|
||||||
|
|
||||||
const handleFetchAttachments = async (page?: number) => {
|
const handleFetchAttachments = async (options?: {
|
||||||
|
mute?: boolean;
|
||||||
|
page?: number;
|
||||||
|
}) => {
|
||||||
try {
|
try {
|
||||||
clearInterval(refreshInterval.value);
|
clearInterval(refreshInterval.value);
|
||||||
|
|
||||||
loading.value = true;
|
if (!options?.mute) {
|
||||||
|
loading.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (page) {
|
if (options?.page) {
|
||||||
attachments.value.page = page;
|
attachments.value.page = options.page;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = await apiClient.attachment.searchAttachments({
|
const { data } = await apiClient.attachment.searchAttachments({
|
||||||
|
@ -96,7 +101,7 @@ export function useAttachmentControl(filterOptions?: {
|
||||||
|
|
||||||
if (deletedAttachments.length) {
|
if (deletedAttachments.length) {
|
||||||
refreshInterval.value = setInterval(() => {
|
refreshInterval.value = setInterval(() => {
|
||||||
handleFetchAttachments();
|
handleFetchAttachments({ mute: true });
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -44,14 +44,19 @@ const selectedCommentNames = ref<string[]>([]);
|
||||||
const keyword = ref("");
|
const keyword = ref("");
|
||||||
const refreshInterval = ref();
|
const refreshInterval = ref();
|
||||||
|
|
||||||
const handleFetchComments = async (page?: number) => {
|
const handleFetchComments = async (options?: {
|
||||||
|
mute?: boolean;
|
||||||
|
page?: number;
|
||||||
|
}) => {
|
||||||
try {
|
try {
|
||||||
clearInterval(refreshInterval.value);
|
clearInterval(refreshInterval.value);
|
||||||
|
|
||||||
loading.value = true;
|
if (!options?.mute) {
|
||||||
|
loading.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (page) {
|
if (options?.page) {
|
||||||
comments.value.page = page;
|
comments.value.page = options.page;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = await apiClient.comment.listComments({
|
const { data } = await apiClient.comment.listComments({
|
||||||
|
@ -70,7 +75,7 @@ const handleFetchComments = async (page?: number) => {
|
||||||
|
|
||||||
if (deletedComments.length) {
|
if (deletedComments.length) {
|
||||||
refreshInterval.value = setInterval(() => {
|
refreshInterval.value = setInterval(() => {
|
||||||
handleFetchComments();
|
handleFetchComments({ mute: true });
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -238,7 +243,7 @@ const handleApprovedFilterItemChange = (filterItem: {
|
||||||
}) => {
|
}) => {
|
||||||
selectedApprovedFilterItem.value = filterItem;
|
selectedApprovedFilterItem.value = filterItem;
|
||||||
selectedCommentNames.value = [];
|
selectedCommentNames.value = [];
|
||||||
handleFetchComments(1);
|
handleFetchComments({ page: 1 });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSortFilterItemChange = (filterItem: {
|
const handleSortFilterItemChange = (filterItem: {
|
||||||
|
@ -247,12 +252,12 @@ const handleSortFilterItemChange = (filterItem: {
|
||||||
}) => {
|
}) => {
|
||||||
selectedSortFilterItem.value = filterItem;
|
selectedSortFilterItem.value = filterItem;
|
||||||
selectedCommentNames.value = [];
|
selectedCommentNames.value = [];
|
||||||
handleFetchComments(1);
|
handleFetchComments({ page: 1 });
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleSelectUser(user: User | undefined) {
|
function handleSelectUser(user: User | undefined) {
|
||||||
selectedUser.value = user;
|
selectedUser.value = user;
|
||||||
handleFetchComments(1);
|
handleFetchComments({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeywordChange() {
|
function handleKeywordChange() {
|
||||||
|
@ -260,12 +265,12 @@ function handleKeywordChange() {
|
||||||
if (keywordNode) {
|
if (keywordNode) {
|
||||||
keyword.value = keywordNode._value as string;
|
keyword.value = keywordNode._value as string;
|
||||||
}
|
}
|
||||||
handleFetchComments(1);
|
handleFetchComments({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClearKeyword() {
|
function handleClearKeyword() {
|
||||||
keyword.value = "";
|
keyword.value = "";
|
||||||
handleFetchComments(1);
|
handleFetchComments({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasFilters = computed(() => {
|
const hasFilters = computed(() => {
|
||||||
|
@ -282,7 +287,7 @@ function handleClearFilters() {
|
||||||
selectedSortFilterItem.value = SortFilterItems[0];
|
selectedSortFilterItem.value = SortFilterItems[0];
|
||||||
selectedUser.value = undefined;
|
selectedUser.value = undefined;
|
||||||
keyword.value = "";
|
keyword.value = "";
|
||||||
handleFetchComments(1);
|
handleFetchComments({ page: 1 });
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
|
@ -479,7 +484,7 @@ function handleClearFilters() {
|
||||||
<CommentListItem
|
<CommentListItem
|
||||||
:comment="comment"
|
:comment="comment"
|
||||||
:is-selected="checkSelection(comment)"
|
:is-selected="checkSelection(comment)"
|
||||||
@reload="handleFetchComments"
|
@reload="handleFetchComments({ mute: true })"
|
||||||
>
|
>
|
||||||
<template #checkbox>
|
<template #checkbox>
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
VEmpty,
|
VEmpty,
|
||||||
IconAddCircle,
|
IconAddCircle,
|
||||||
IconExternalLinkLine,
|
IconExternalLinkLine,
|
||||||
|
VLoading,
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
import ReplyCreationModal from "./ReplyCreationModal.vue";
|
import ReplyCreationModal from "./ReplyCreationModal.vue";
|
||||||
import type {
|
import type {
|
||||||
|
@ -114,13 +115,18 @@ const handleApprove = async () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFetchReplies = async () => {
|
const handleFetchReplies = async (options?: { mute?: boolean }) => {
|
||||||
try {
|
try {
|
||||||
clearInterval(refreshInterval.value);
|
clearInterval(refreshInterval.value);
|
||||||
|
|
||||||
loading.value = true;
|
if (!options?.mute) {
|
||||||
|
loading.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
const { data } = await apiClient.reply.listReplies({
|
const { data } = await apiClient.reply.listReplies({
|
||||||
commentName: props.comment.comment.metadata.name,
|
commentName: props.comment.comment.metadata.name,
|
||||||
|
page: 0,
|
||||||
|
size: 0,
|
||||||
});
|
});
|
||||||
replies.value = data.items;
|
replies.value = data.items;
|
||||||
|
|
||||||
|
@ -130,7 +136,7 @@ const handleFetchReplies = async () => {
|
||||||
|
|
||||||
if (deletedReplies.length) {
|
if (deletedReplies.length) {
|
||||||
refreshInterval.value = setInterval(() => {
|
refreshInterval.value = setInterval(() => {
|
||||||
handleFetchReplies();
|
handleFetchReplies({ mute: true });
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -185,7 +191,7 @@ const onTriggerReply = (reply: ListedReply) => {
|
||||||
|
|
||||||
const onReplyCreationModalClose = () => {
|
const onReplyCreationModalClose = () => {
|
||||||
selectedReply.value = undefined;
|
selectedReply.value = undefined;
|
||||||
handleFetchReplies();
|
handleFetchReplies({ mute: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
// Subject ref processing
|
// Subject ref processing
|
||||||
|
@ -391,33 +397,35 @@ const subjectRefResult = computed(() => {
|
||||||
<div
|
<div
|
||||||
class="ml-8 mt-3 divide-y divide-gray-100 rounded-base border-t border-gray-100 pt-3"
|
class="ml-8 mt-3 divide-y divide-gray-100 rounded-base border-t border-gray-100 pt-3"
|
||||||
>
|
>
|
||||||
<VEmpty
|
<VLoading v-if="loading" />
|
||||||
v-if="!replies.length && !loading"
|
<Transition v-else-if="!replies.length" appear name="fade">
|
||||||
message="你可以尝试刷新或者创建新回复"
|
<VEmpty message="你可以尝试刷新或者创建新回复" title="当前没有回复">
|
||||||
title="当前没有回复"
|
<template #actions>
|
||||||
>
|
<VSpace>
|
||||||
<template #actions>
|
<VButton @click="handleFetchReplies">刷新</VButton>
|
||||||
<VSpace>
|
<VButton type="secondary" @click="replyModal = true">
|
||||||
<VButton @click="handleFetchReplies">刷新</VButton>
|
<template #icon>
|
||||||
<VButton type="secondary" @click="replyModal = true">
|
<IconAddCircle class="h-full w-full" />
|
||||||
<template #icon>
|
</template>
|
||||||
<IconAddCircle class="h-full w-full" />
|
创建新回复
|
||||||
</template>
|
</VButton>
|
||||||
创建新回复
|
</VSpace>
|
||||||
</VButton>
|
</template>
|
||||||
</VSpace>
|
</VEmpty>
|
||||||
</template>
|
</Transition>
|
||||||
</VEmpty>
|
<Transition v-else appear name="fade">
|
||||||
<ReplyListItem
|
<div>
|
||||||
v-for="reply in replies"
|
<ReplyListItem
|
||||||
v-else
|
v-for="reply in replies"
|
||||||
:key="reply.reply.metadata.name"
|
:key="reply.reply.metadata.name"
|
||||||
:class="{ 'hover:bg-white': showReplies }"
|
:class="{ 'hover:bg-white': showReplies }"
|
||||||
:reply="reply"
|
:reply="reply"
|
||||||
:replies="replies"
|
:replies="replies"
|
||||||
@reload="handleFetchReplies"
|
@reload="handleFetchReplies({ mute: true })"
|
||||||
@reply="onTriggerReply"
|
@reply="onTriggerReply"
|
||||||
></ReplyListItem>
|
></ReplyListItem>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</VEntity>
|
</VEntity>
|
||||||
|
|
|
@ -45,14 +45,19 @@ const checkedAll = ref(false);
|
||||||
const refreshInterval = ref();
|
const refreshInterval = ref();
|
||||||
const keyword = ref("");
|
const keyword = ref("");
|
||||||
|
|
||||||
const handleFetchSinglePages = async (page?: number) => {
|
const handleFetchSinglePages = async (options?: {
|
||||||
|
mute?: boolean;
|
||||||
|
page?: number;
|
||||||
|
}) => {
|
||||||
try {
|
try {
|
||||||
clearInterval(refreshInterval.value);
|
clearInterval(refreshInterval.value);
|
||||||
|
|
||||||
loading.value = true;
|
if (!options?.mute) {
|
||||||
|
loading.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (page) {
|
if (options?.page) {
|
||||||
singlePages.value.page = page;
|
singlePages.value.page = options.page;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = await apiClient.singlePage.listSinglePages({
|
const { data } = await apiClient.singlePage.listSinglePages({
|
||||||
|
@ -72,7 +77,7 @@ const handleFetchSinglePages = async (page?: number) => {
|
||||||
|
|
||||||
if (deletedSinglePages.length) {
|
if (deletedSinglePages.length) {
|
||||||
refreshInterval.value = setInterval(() => {
|
refreshInterval.value = setInterval(() => {
|
||||||
handleFetchSinglePages();
|
handleFetchSinglePages({ mute: true });
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -212,12 +217,12 @@ function handleKeywordChange() {
|
||||||
if (keywordNode) {
|
if (keywordNode) {
|
||||||
keyword.value = keywordNode._value as string;
|
keyword.value = keywordNode._value as string;
|
||||||
}
|
}
|
||||||
handleFetchSinglePages(1);
|
handleFetchSinglePages({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClearKeyword() {
|
function handleClearKeyword() {
|
||||||
keyword.value = "";
|
keyword.value = "";
|
||||||
handleFetchSinglePages(1);
|
handleFetchSinglePages({ page: 1 });
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -58,11 +58,16 @@ const selectedPageNames = ref<string[]>([]);
|
||||||
const checkedAll = ref(false);
|
const checkedAll = ref(false);
|
||||||
const refreshInterval = ref();
|
const refreshInterval = ref();
|
||||||
|
|
||||||
const handleFetchSinglePages = async (page?: number) => {
|
const handleFetchSinglePages = async (options?: {
|
||||||
|
mute?: boolean;
|
||||||
|
page?: number;
|
||||||
|
}) => {
|
||||||
try {
|
try {
|
||||||
clearInterval(refreshInterval.value);
|
clearInterval(refreshInterval.value);
|
||||||
|
|
||||||
loading.value = true;
|
if (!options?.mute) {
|
||||||
|
loading.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
let contributors: string[] | undefined;
|
let contributors: string[] | undefined;
|
||||||
const labelSelector: string[] = ["content.halo.run/deleted=false"];
|
const labelSelector: string[] = ["content.halo.run/deleted=false"];
|
||||||
|
@ -77,8 +82,8 @@ const handleFetchSinglePages = async (page?: number) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page) {
|
if (options?.page) {
|
||||||
singlePages.value.page = page;
|
singlePages.value.page = options.page;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = await apiClient.singlePage.listSinglePages({
|
const { data } = await apiClient.singlePage.listSinglePages({
|
||||||
|
@ -105,8 +110,8 @@ const handleFetchSinglePages = async (page?: number) => {
|
||||||
|
|
||||||
if (abnormalSinglePages.length) {
|
if (abnormalSinglePages.length) {
|
||||||
refreshInterval.value = setInterval(() => {
|
refreshInterval.value = setInterval(() => {
|
||||||
handleFetchSinglePages();
|
handleFetchSinglePages({ mute: true });
|
||||||
}, 1000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch single pages", error);
|
console.error("Failed to fetch single pages", error);
|
||||||
|
@ -142,7 +147,7 @@ const handleOpenSettingModal = async (singlePage: SinglePage) => {
|
||||||
|
|
||||||
const onSettingModalClose = () => {
|
const onSettingModalClose = () => {
|
||||||
selectedSinglePage.value = undefined;
|
selectedSinglePage.value = undefined;
|
||||||
handleFetchSinglePages();
|
handleFetchSinglePages({ mute: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectPrevious = async () => {
|
const handleSelectPrevious = async () => {
|
||||||
|
@ -361,22 +366,22 @@ const keyword = ref("");
|
||||||
|
|
||||||
function handleVisibleItemChange(visibleItem: VisibleItem) {
|
function handleVisibleItemChange(visibleItem: VisibleItem) {
|
||||||
selectedVisibleItem.value = visibleItem;
|
selectedVisibleItem.value = visibleItem;
|
||||||
handleFetchSinglePages(1);
|
handleFetchSinglePages({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSelectUser = (user?: User) => {
|
const handleSelectUser = (user?: User) => {
|
||||||
selectedContributor.value = user;
|
selectedContributor.value = user;
|
||||||
handleFetchSinglePages(1);
|
handleFetchSinglePages({ page: 1 });
|
||||||
};
|
};
|
||||||
|
|
||||||
function handlePublishStatusItemChange(publishStatusItem: PublishStatusItem) {
|
function handlePublishStatusItemChange(publishStatusItem: PublishStatusItem) {
|
||||||
selectedPublishStatusItem.value = publishStatusItem;
|
selectedPublishStatusItem.value = publishStatusItem;
|
||||||
handleFetchSinglePages(1);
|
handleFetchSinglePages({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSortItemChange(sortItem?: SortItem) {
|
function handleSortItemChange(sortItem?: SortItem) {
|
||||||
selectedSortItem.value = sortItem;
|
selectedSortItem.value = sortItem;
|
||||||
handleFetchSinglePages(1);
|
handleFetchSinglePages({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeywordChange() {
|
function handleKeywordChange() {
|
||||||
|
@ -384,12 +389,12 @@ function handleKeywordChange() {
|
||||||
if (keywordNode) {
|
if (keywordNode) {
|
||||||
keyword.value = keywordNode._value as string;
|
keyword.value = keywordNode._value as string;
|
||||||
}
|
}
|
||||||
handleFetchSinglePages(1);
|
handleFetchSinglePages({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClearKeyword() {
|
function handleClearKeyword() {
|
||||||
keyword.value = "";
|
keyword.value = "";
|
||||||
handleFetchSinglePages(1);
|
handleFetchSinglePages({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasFilters = computed(() => {
|
const hasFilters = computed(() => {
|
||||||
|
@ -408,7 +413,7 @@ function handleClearFilters() {
|
||||||
selectedPublishStatusItem.value = PublishStatusItems[0];
|
selectedPublishStatusItem.value = PublishStatusItems[0];
|
||||||
selectedSortItem.value = undefined;
|
selectedSortItem.value = undefined;
|
||||||
keyword.value = "";
|
keyword.value = "";
|
||||||
handleFetchSinglePages(1);
|
handleFetchSinglePages({ page: 1 });
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -64,11 +64,16 @@ const checkedAll = ref(false);
|
||||||
const selectedPostNames = ref<string[]>([]);
|
const selectedPostNames = ref<string[]>([]);
|
||||||
const refreshInterval = ref();
|
const refreshInterval = ref();
|
||||||
|
|
||||||
const handleFetchPosts = async (page?: number) => {
|
const handleFetchPosts = async (options?: {
|
||||||
|
mute?: boolean;
|
||||||
|
page?: number;
|
||||||
|
}) => {
|
||||||
try {
|
try {
|
||||||
clearInterval(refreshInterval.value);
|
clearInterval(refreshInterval.value);
|
||||||
|
|
||||||
loading.value = true;
|
if (!options?.mute) {
|
||||||
|
loading.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
let categories: string[] | undefined;
|
let categories: string[] | undefined;
|
||||||
let tags: string[] | undefined;
|
let tags: string[] | undefined;
|
||||||
|
@ -96,8 +101,8 @@ const handleFetchPosts = async (page?: number) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page) {
|
if (options?.page) {
|
||||||
posts.value.page = page;
|
posts.value.page = options.page;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = await apiClient.post.listPosts({
|
const { data } = await apiClient.post.listPosts({
|
||||||
|
@ -126,8 +131,8 @@ const handleFetchPosts = async (page?: number) => {
|
||||||
|
|
||||||
if (abnormalPosts.length) {
|
if (abnormalPosts.length) {
|
||||||
refreshInterval.value = setInterval(() => {
|
refreshInterval.value = setInterval(() => {
|
||||||
handleFetchPosts();
|
handleFetchPosts({ mute: true });
|
||||||
}, 1000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to fetch posts", e);
|
console.error("Failed to fetch posts", e);
|
||||||
|
@ -164,7 +169,7 @@ const handleOpenSettingModal = async (post: Post) => {
|
||||||
|
|
||||||
const onSettingModalClose = () => {
|
const onSettingModalClose = () => {
|
||||||
selectedPost.value = undefined;
|
selectedPost.value = undefined;
|
||||||
handleFetchPosts();
|
handleFetchPosts({ mute: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectPrevious = async () => {
|
const handleSelectPrevious = async () => {
|
||||||
|
@ -376,32 +381,32 @@ const keyword = ref("");
|
||||||
|
|
||||||
function handleVisibleItemChange(visibleItem: VisibleItem) {
|
function handleVisibleItemChange(visibleItem: VisibleItem) {
|
||||||
selectedVisibleItem.value = visibleItem;
|
selectedVisibleItem.value = visibleItem;
|
||||||
handleFetchPosts(1);
|
handleFetchPosts({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePublishStatusItemChange(publishStatusItem: PublishStatuItem) {
|
function handlePublishStatusItemChange(publishStatusItem: PublishStatuItem) {
|
||||||
selectedPublishStatusItem.value = publishStatusItem;
|
selectedPublishStatusItem.value = publishStatusItem;
|
||||||
handleFetchPosts(1);
|
handleFetchPosts({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSortItemChange(sortItem?: SortItem) {
|
function handleSortItemChange(sortItem?: SortItem) {
|
||||||
selectedSortItem.value = sortItem;
|
selectedSortItem.value = sortItem;
|
||||||
handleFetchPosts(1);
|
handleFetchPosts({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCategoryChange(category?: Category) {
|
function handleCategoryChange(category?: Category) {
|
||||||
selectedCategory.value = category;
|
selectedCategory.value = category;
|
||||||
handleFetchPosts(1);
|
handleFetchPosts({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTagChange(tag?: Tag) {
|
function handleTagChange(tag?: Tag) {
|
||||||
selectedTag.value = tag;
|
selectedTag.value = tag;
|
||||||
handleFetchPosts(1);
|
handleFetchPosts({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleContributorChange(user?: User) {
|
function handleContributorChange(user?: User) {
|
||||||
selectedContributor.value = user;
|
selectedContributor.value = user;
|
||||||
handleFetchPosts(1);
|
handleFetchPosts({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeywordChange() {
|
function handleKeywordChange() {
|
||||||
|
@ -409,12 +414,12 @@ function handleKeywordChange() {
|
||||||
if (keywordNode) {
|
if (keywordNode) {
|
||||||
keyword.value = keywordNode._value as string;
|
keyword.value = keywordNode._value as string;
|
||||||
}
|
}
|
||||||
handleFetchPosts(1);
|
handleFetchPosts({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClearKeyword() {
|
function handleClearKeyword() {
|
||||||
keyword.value = "";
|
keyword.value = "";
|
||||||
handleFetchPosts(1);
|
handleFetchPosts({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClearFilters() {
|
function handleClearFilters() {
|
||||||
|
@ -425,7 +430,7 @@ function handleClearFilters() {
|
||||||
selectedTag.value = undefined;
|
selectedTag.value = undefined;
|
||||||
selectedContributor.value = undefined;
|
selectedContributor.value = undefined;
|
||||||
keyword.value = "";
|
keyword.value = "";
|
||||||
handleFetchPosts(1);
|
handleFetchPosts({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasFilters = computed(() => {
|
const hasFilters = computed(() => {
|
||||||
|
|
|
@ -11,7 +11,7 @@ interface usePostCategoryReturn {
|
||||||
categories: Ref<Category[]>;
|
categories: Ref<Category[]>;
|
||||||
categoriesTree: Ref<CategoryTree[]>;
|
categoriesTree: Ref<CategoryTree[]>;
|
||||||
loading: Ref<boolean>;
|
loading: Ref<boolean>;
|
||||||
handleFetchCategories: () => void;
|
handleFetchCategories: (fetchOptions?: { mute?: boolean }) => void;
|
||||||
handleDelete: (category: CategoryTree) => void;
|
handleDelete: (category: CategoryTree) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,11 +25,13 @@ export function usePostCategory(options?: {
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const refreshInterval = ref();
|
const refreshInterval = ref();
|
||||||
|
|
||||||
const handleFetchCategories = async () => {
|
const handleFetchCategories = async (fetchOptions?: { mute?: boolean }) => {
|
||||||
try {
|
try {
|
||||||
clearInterval(refreshInterval.value);
|
clearInterval(refreshInterval.value);
|
||||||
|
|
||||||
loading.value = true;
|
if (!fetchOptions?.mute) {
|
||||||
|
loading.value = true;
|
||||||
|
}
|
||||||
const { data } =
|
const { data } =
|
||||||
await apiClient.extension.category.listcontentHaloRunV1alpha1Category({
|
await apiClient.extension.category.listcontentHaloRunV1alpha1Category({
|
||||||
page: 0,
|
page: 0,
|
||||||
|
@ -44,7 +46,7 @@ export function usePostCategory(options?: {
|
||||||
|
|
||||||
if (deletedCategories.length) {
|
if (deletedCategories.length) {
|
||||||
refreshInterval.value = setInterval(() => {
|
refreshInterval.value = setInterval(() => {
|
||||||
handleFetchCategories();
|
handleFetchCategories({ mute: true });
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { onBeforeRouteLeave } from "vue-router";
|
||||||
interface usePostTagReturn {
|
interface usePostTagReturn {
|
||||||
tags: Ref<Tag[]>;
|
tags: Ref<Tag[]>;
|
||||||
loading: Ref<boolean>;
|
loading: Ref<boolean>;
|
||||||
handleFetchTags: () => void;
|
handleFetchTags: (fetchOptions?: { mute?: boolean }) => void;
|
||||||
handleDelete: (tag: Tag) => void;
|
handleDelete: (tag: Tag) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,11 +21,13 @@ export function usePostTag(options?: {
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const refreshInterval = ref();
|
const refreshInterval = ref();
|
||||||
|
|
||||||
const handleFetchTags = async () => {
|
const handleFetchTags = async (fetchOptions?: { mute?: boolean }) => {
|
||||||
try {
|
try {
|
||||||
clearInterval(refreshInterval.value);
|
clearInterval(refreshInterval.value);
|
||||||
|
|
||||||
loading.value = true;
|
if (!fetchOptions?.mute) {
|
||||||
|
loading.value = true;
|
||||||
|
}
|
||||||
const { data } =
|
const { data } =
|
||||||
await apiClient.extension.tag.listcontentHaloRunV1alpha1Tag({
|
await apiClient.extension.tag.listcontentHaloRunV1alpha1Tag({
|
||||||
page: 0,
|
page: 0,
|
||||||
|
@ -40,7 +42,7 @@ export function usePostTag(options?: {
|
||||||
|
|
||||||
if (deletedTags.length) {
|
if (deletedTags.length) {
|
||||||
refreshInterval.value = setInterval(() => {
|
refreshInterval.value = setInterval(() => {
|
||||||
handleFetchTags();
|
handleFetchTags({ mute: true });
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -38,11 +38,13 @@ const menuListRef = ref();
|
||||||
const menuItemEditingModal = ref();
|
const menuItemEditingModal = ref();
|
||||||
const refreshInterval = ref();
|
const refreshInterval = ref();
|
||||||
|
|
||||||
const handleFetchMenuItems = async () => {
|
const handleFetchMenuItems = async (options?: { mute?: boolean }) => {
|
||||||
try {
|
try {
|
||||||
clearInterval(refreshInterval.value);
|
clearInterval(refreshInterval.value);
|
||||||
|
|
||||||
loading.value = true;
|
if (!options?.mute) {
|
||||||
|
loading.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!selectedMenu.value?.spec.menuItems) {
|
if (!selectedMenu.value?.spec.menuItems) {
|
||||||
return;
|
return;
|
||||||
|
@ -65,7 +67,7 @@ const handleFetchMenuItems = async () => {
|
||||||
|
|
||||||
if (deletedMenuItems.length) {
|
if (deletedMenuItems.length) {
|
||||||
refreshInterval.value = setInterval(() => {
|
refreshInterval.value = setInterval(() => {
|
||||||
handleFetchMenuItems();
|
handleFetchMenuItems({ mute: true });
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -113,7 +115,7 @@ const onMenuItemSaved = async (menuItem: MenuItem) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
await menuListRef.value.handleFetchMenus();
|
await menuListRef.value.handleFetchMenus();
|
||||||
await handleFetchMenuItems();
|
await handleFetchMenuItems({ mute: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUpdateInBatch = useDebounceFn(async () => {
|
const handleUpdateInBatch = useDebounceFn(async () => {
|
||||||
|
@ -131,7 +133,7 @@ const handleUpdateInBatch = useDebounceFn(async () => {
|
||||||
console.log("Failed to update menu items", e);
|
console.log("Failed to update menu items", e);
|
||||||
} finally {
|
} finally {
|
||||||
await menuListRef.value.handleFetchMenus();
|
await menuListRef.value.handleFetchMenus();
|
||||||
await handleFetchMenuItems();
|
await handleFetchMenuItems({ mute: true });
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
|
@ -191,7 +193,7 @@ const handleDelete = async (menuItem: MenuTreeItem) => {
|
||||||
<MenuList
|
<MenuList
|
||||||
ref="menuListRef"
|
ref="menuListRef"
|
||||||
v-model:selected-menu="selectedMenu"
|
v-model:selected-menu="selectedMenu"
|
||||||
@select="handleFetchMenuItems"
|
@select="handleFetchMenuItems()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
|
@ -229,7 +231,7 @@ const handleDelete = async (menuItem: MenuTreeItem) => {
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
<VButton @click="handleFetchMenuItems"> 刷新</VButton>
|
<VButton @click="handleFetchMenuItems()"> 刷新</VButton>
|
||||||
<VButton
|
<VButton
|
||||||
v-permission="['system:menus:manage']"
|
v-permission="['system:menus:manage']"
|
||||||
type="primary"
|
type="primary"
|
||||||
|
|
|
@ -41,12 +41,14 @@ const selectedMenuToUpdate = ref<Menu>();
|
||||||
const menuEditingModal = ref<boolean>(false);
|
const menuEditingModal = ref<boolean>(false);
|
||||||
const refreshInterval = ref();
|
const refreshInterval = ref();
|
||||||
|
|
||||||
const handleFetchMenus = async () => {
|
const handleFetchMenus = async (options?: { mute?: boolean }) => {
|
||||||
selectedMenuToUpdate.value = undefined;
|
selectedMenuToUpdate.value = undefined;
|
||||||
try {
|
try {
|
||||||
clearInterval(refreshInterval.value);
|
clearInterval(refreshInterval.value);
|
||||||
|
|
||||||
loading.value = true;
|
if (!options?.mute) {
|
||||||
|
loading.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
const { data } = await apiClient.extension.menu.listv1alpha1Menu();
|
const { data } = await apiClient.extension.menu.listv1alpha1Menu();
|
||||||
menus.value = data.items;
|
menus.value = data.items;
|
||||||
|
@ -67,7 +69,7 @@ const handleFetchMenus = async () => {
|
||||||
|
|
||||||
if (deletedMenus.length) {
|
if (deletedMenus.length) {
|
||||||
refreshInterval.value = setInterval(() => {
|
refreshInterval.value = setInterval(() => {
|
||||||
handleFetchMenus();
|
handleFetchMenus({ mute: true });
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -187,7 +189,7 @@ onMounted(handleFetchPrimaryMenuName);
|
||||||
<MenuEditingModal
|
<MenuEditingModal
|
||||||
v-model:visible="menuEditingModal"
|
v-model:visible="menuEditingModal"
|
||||||
:menu="selectedMenuToUpdate"
|
:menu="selectedMenuToUpdate"
|
||||||
@close="handleFetchMenus"
|
@close="handleFetchMenus()"
|
||||||
@created="handleSelect"
|
@created="handleSelect"
|
||||||
/>
|
/>
|
||||||
<VCard :body-class="['!p-0']" title="菜单">
|
<VCard :body-class="['!p-0']" title="菜单">
|
||||||
|
@ -196,7 +198,7 @@ onMounted(handleFetchPrimaryMenuName);
|
||||||
<VEmpty message="你可以尝试刷新或者新建菜单" title="当前没有菜单">
|
<VEmpty message="你可以尝试刷新或者新建菜单" title="当前没有菜单">
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
<VButton size="sm" @click="handleFetchMenus"> 刷新</VButton>
|
<VButton size="sm" @click="handleFetchMenus()"> 刷新</VButton>
|
||||||
</VSpace>
|
</VSpace>
|
||||||
</template>
|
</template>
|
||||||
</VEmpty>
|
</VEmpty>
|
||||||
|
|
|
@ -59,11 +59,13 @@ const modalTitle = computed(() => {
|
||||||
|
|
||||||
const { activatedTheme } = storeToRefs(useThemeStore());
|
const { activatedTheme } = storeToRefs(useThemeStore());
|
||||||
|
|
||||||
const handleFetchThemes = async () => {
|
const handleFetchThemes = async (options?: { mute?: boolean }) => {
|
||||||
try {
|
try {
|
||||||
clearInterval(refreshInterval.value);
|
clearInterval(refreshInterval.value);
|
||||||
|
|
||||||
loading.value = true;
|
if (!options?.mute) {
|
||||||
|
loading.value = true;
|
||||||
|
}
|
||||||
const { data } = await apiClient.theme.listThemes({
|
const { data } = await apiClient.theme.listThemes({
|
||||||
page: 0,
|
page: 0,
|
||||||
size: 0,
|
size: 0,
|
||||||
|
@ -81,7 +83,7 @@ const handleFetchThemes = async () => {
|
||||||
|
|
||||||
if (deletedThemes.length) {
|
if (deletedThemes.length) {
|
||||||
refreshInterval.value = setInterval(() => {
|
refreshInterval.value = setInterval(() => {
|
||||||
handleFetchThemes();
|
handleFetchThemes({ mute: true });
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -223,7 +225,7 @@ const handleOpenPreview = (theme: Theme) => {
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
<VButton :loading="loading" @click="handleFetchThemes">
|
<VButton :loading="loading" @click="handleFetchThemes()">
|
||||||
刷新
|
刷新
|
||||||
</VButton>
|
</VButton>
|
||||||
<VButton
|
<VButton
|
||||||
|
|
|
@ -41,14 +41,19 @@ const pluginInstall = ref(false);
|
||||||
const keyword = ref("");
|
const keyword = ref("");
|
||||||
const refreshInterval = ref();
|
const refreshInterval = ref();
|
||||||
|
|
||||||
const handleFetchPlugins = async (page?: number) => {
|
const handleFetchPlugins = async (options?: {
|
||||||
|
mute?: boolean;
|
||||||
|
page?: number;
|
||||||
|
}) => {
|
||||||
try {
|
try {
|
||||||
clearInterval(refreshInterval.value);
|
clearInterval(refreshInterval.value);
|
||||||
|
|
||||||
loading.value = true;
|
if (!options?.mute) {
|
||||||
|
loading.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (page) {
|
if (options?.page) {
|
||||||
plugins.value.page = page;
|
plugins.value.page = options.page;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = await apiClient.plugin.listPlugins({
|
const { data } = await apiClient.plugin.listPlugins({
|
||||||
|
@ -69,7 +74,7 @@ const handleFetchPlugins = async (page?: number) => {
|
||||||
|
|
||||||
if (deletedPlugins.length) {
|
if (deletedPlugins.length) {
|
||||||
refreshInterval.value = setInterval(() => {
|
refreshInterval.value = setInterval(() => {
|
||||||
handleFetchPlugins();
|
handleFetchPlugins({ mute: true });
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -139,12 +144,12 @@ const selectedSortItem = ref<SortItem>();
|
||||||
|
|
||||||
function handleEnabledItemChange(enabledItem: EnabledItem) {
|
function handleEnabledItemChange(enabledItem: EnabledItem) {
|
||||||
selectedEnabledItem.value = enabledItem;
|
selectedEnabledItem.value = enabledItem;
|
||||||
handleFetchPlugins(1);
|
handleFetchPlugins({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSortItemChange(sortItem?: SortItem) {
|
function handleSortItemChange(sortItem?: SortItem) {
|
||||||
selectedSortItem.value = sortItem;
|
selectedSortItem.value = sortItem;
|
||||||
handleFetchPlugins(1);
|
handleFetchPlugins({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeywordChange() {
|
function handleKeywordChange() {
|
||||||
|
@ -152,12 +157,12 @@ function handleKeywordChange() {
|
||||||
if (keywordNode) {
|
if (keywordNode) {
|
||||||
keyword.value = keywordNode._value as string;
|
keyword.value = keywordNode._value as string;
|
||||||
}
|
}
|
||||||
handleFetchPlugins(1);
|
handleFetchPlugins({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleClearKeyword() {
|
function handleClearKeyword() {
|
||||||
keyword.value = "";
|
keyword.value = "";
|
||||||
handleFetchPlugins(1);
|
handleFetchPlugins({ page: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasFilters = computed(() => {
|
const hasFilters = computed(() => {
|
||||||
|
@ -172,14 +177,14 @@ function handleClearFilters() {
|
||||||
selectedEnabledItem.value = undefined;
|
selectedEnabledItem.value = undefined;
|
||||||
selectedSortItem.value = undefined;
|
selectedSortItem.value = undefined;
|
||||||
keyword.value = "";
|
keyword.value = "";
|
||||||
handleFetchPlugins(1);
|
handleFetchPlugins({ page: 1 });
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<PluginUploadModal
|
<PluginUploadModal
|
||||||
v-if="currentUserHasPermission(['system:plugins:manage'])"
|
v-if="currentUserHasPermission(['system:plugins:manage'])"
|
||||||
v-model:visible="pluginInstall"
|
v-model:visible="pluginInstall"
|
||||||
@close="handleFetchPlugins"
|
@close="handleFetchPlugins()"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<VPageHeader title="插件">
|
<VPageHeader title="插件">
|
||||||
|
@ -320,7 +325,7 @@ function handleClearFilters() {
|
||||||
>
|
>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<VSpace>
|
<VSpace>
|
||||||
<VButton @click="handleFetchPlugins">刷新</VButton>
|
<VButton @click="handleFetchPlugins()">刷新</VButton>
|
||||||
<VButton
|
<VButton
|
||||||
v-permission="['system:plugins:manage']"
|
v-permission="['system:plugins:manage']"
|
||||||
type="secondary"
|
type="secondary"
|
||||||
|
@ -342,7 +347,7 @@ function handleClearFilters() {
|
||||||
role="list"
|
role="list"
|
||||||
>
|
>
|
||||||
<li v-for="(plugin, index) in plugins.items" :key="index">
|
<li v-for="(plugin, index) in plugins.items" :key="index">
|
||||||
<PluginListItem :plugin="plugin" @reload="handleFetchPlugins" />
|
<PluginListItem :plugin="plugin" @reload="handleFetchPlugins()" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
Loading…
Reference in New Issue