mirror of https://github.com/halo-dev/halo
refactor: add loading states for data list (halo-dev/console#703)
#### What type of PR is this? /kind improvement /milestone 2.0 #### What this PR does / why we need it: 优化列表的加载,添加加载动画,解决加载完成之后界面闪动的问题。 #### Which issue(s) this PR fixes: Ref https://github.com/halo-dev/halo/issues/2370 #### Special notes for your reviewer: /cc @halo-dev/sig-halo-console #### Does this PR introduce a user-facing change? ```release-note 优化列表的加载,添加加载动画,解决加载完成之后界面闪动的问题。 ```pull/3445/head
parent
c0877e2a9c
commit
3f3147ce34
|
@ -21,3 +21,4 @@ export * from "./components/empty";
|
|||
export * from "./components/status";
|
||||
export * from "./components/entity";
|
||||
export * from "./components/toast";
|
||||
export * from "./components/loading";
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<script lang="ts" setup></script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center justify-center py-4">
|
||||
<svg
|
||||
class="h-5 w-5 animate-spin"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
|
@ -0,0 +1 @@
|
|||
export { default as VLoading } from "./Loading.vue";
|
|
@ -19,6 +19,7 @@ import {
|
|||
VStatusDot,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import LazyImage from "@/components/image/LazyImage.vue";
|
||||
import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue";
|
||||
|
@ -563,8 +564,10 @@ onMounted(() => {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<VLoading v-if="loading" />
|
||||
|
||||
<Transition v-else-if="!attachments.total" appear name="fade">
|
||||
<VEmpty
|
||||
v-if="!attachments.total && !loading"
|
||||
message="当前分组没有附件,你可以尝试刷新或者上传附件"
|
||||
title="当前分组没有附件"
|
||||
>
|
||||
|
@ -584,9 +587,10 @@ onMounted(() => {
|
|||
</VSpace>
|
||||
</template>
|
||||
</VEmpty>
|
||||
</Transition>
|
||||
|
||||
<div v-else>
|
||||
<div v-if="viewType === 'grid'">
|
||||
<Transition v-if="viewType === 'grid'" appear name="fade">
|
||||
<div
|
||||
class="mt-2 grid grid-cols-3 gap-x-2 gap-y-3 sm:grid-cols-3 md:grid-cols-6 xl:grid-cols-8 2xl:grid-cols-12"
|
||||
role="list"
|
||||
|
@ -666,14 +670,16 @@ onMounted(() => {
|
|||
</div>
|
||||
</VCard>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Transition>
|
||||
<Transition v-if="viewType === 'list'" appear name="fade">
|
||||
<ul
|
||||
v-if="viewType === 'list'"
|
||||
class="box-border h-full w-full divide-y divide-gray-100"
|
||||
role="list"
|
||||
>
|
||||
<li v-for="(attachment, index) in attachments.items" :key="index">
|
||||
<li
|
||||
v-for="(attachment, index) in attachments.items"
|
||||
:key="index"
|
||||
>
|
||||
<VEntity :is-selected="isChecked(attachment)">
|
||||
<template
|
||||
v-if="
|
||||
|
@ -730,7 +736,9 @@ onMounted(() => {
|
|||
<RouterLink
|
||||
:to="{
|
||||
name: 'UserDetail',
|
||||
params: { name: attachment.spec.uploadedBy?.name },
|
||||
params: {
|
||||
name: attachment.spec.uploadedBy?.name,
|
||||
},
|
||||
}"
|
||||
class="text-xs text-gray-500"
|
||||
>
|
||||
|
@ -738,7 +746,9 @@ onMounted(() => {
|
|||
</RouterLink>
|
||||
</template>
|
||||
</VEntityField>
|
||||
<VEntityField v-if="attachment.metadata.deletionTimestamp">
|
||||
<VEntityField
|
||||
v-if="attachment.metadata.deletionTimestamp"
|
||||
>
|
||||
<template #description>
|
||||
<VStatusDot
|
||||
v-tooltip="`删除中`"
|
||||
|
@ -779,6 +789,7 @@ onMounted(() => {
|
|||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
IconRefreshLine,
|
||||
VEmpty,
|
||||
Dialog,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import CommentListItem from "./components/CommentListItem.vue";
|
||||
import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue";
|
||||
|
@ -459,18 +460,21 @@ function handleClearFilters() {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<VEmpty
|
||||
v-if="!comments.items.length && !loading"
|
||||
message="你可以尝试刷新或者修改筛选条件"
|
||||
title="当前没有评论"
|
||||
>
|
||||
<VLoading v-if="loading" />
|
||||
<Transition v-else-if="!comments.items.length" appear name="fade">
|
||||
<VEmpty message="你可以尝试刷新或者修改筛选条件" title="当前没有评论">
|
||||
<template #actions>
|
||||
<VSpace>
|
||||
<VButton @click="handleFetchComments">刷新</VButton>
|
||||
</VSpace>
|
||||
</template>
|
||||
</VEmpty>
|
||||
<ul class="box-border h-full w-full divide-y divide-gray-100" role="list">
|
||||
</Transition>
|
||||
<Transition v-else appear name="fade">
|
||||
<ul
|
||||
class="box-border h-full w-full divide-y divide-gray-100"
|
||||
role="list"
|
||||
>
|
||||
<li v-for="(comment, index) in comments.items" :key="index">
|
||||
<CommentListItem
|
||||
:comment="comment"
|
||||
|
@ -489,6 +493,7 @@ function handleClearFilters() {
|
|||
</CommentListItem>
|
||||
</li>
|
||||
</ul>
|
||||
</Transition>
|
||||
|
||||
<template #footer>
|
||||
<div class="bg-white sm:flex sm:items-center sm:justify-end">
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
VEntityField,
|
||||
VPageHeader,
|
||||
VStatusDot,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import type { ListedSinglePageList, SinglePage } from "@halo-dev/api-client";
|
||||
|
@ -305,8 +306,9 @@ function handleClearKeyword() {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<VLoading v-if="loading" />
|
||||
<Transition v-else-if="!singlePages.items.length" appear name="fade">
|
||||
<VEmpty
|
||||
v-if="!singlePages.items.length && !loading"
|
||||
message="你可以尝试刷新或者返回自定义页面管理"
|
||||
title="没有自定义页面被放入回收站"
|
||||
>
|
||||
|
@ -323,8 +325,9 @@ function handleClearKeyword() {
|
|||
</VSpace>
|
||||
</template>
|
||||
</VEmpty>
|
||||
</Transition>
|
||||
<Transition v-else appear name="fade">
|
||||
<ul
|
||||
v-else
|
||||
class="box-border h-full w-full divide-y divide-gray-100"
|
||||
role="list"
|
||||
>
|
||||
|
@ -387,7 +390,9 @@ function handleClearKeyword() {
|
|||
<VStatusDot v-tooltip="`恢复中`" state="success" animate />
|
||||
</template>
|
||||
</VEntityField>
|
||||
<VEntityField v-if="singlePage?.page?.metadata.deletionTimestamp">
|
||||
<VEntityField
|
||||
v-if="singlePage?.page?.metadata.deletionTimestamp"
|
||||
>
|
||||
<template #description>
|
||||
<VStatusDot v-tooltip="`删除中`" state="warning" animate />
|
||||
</template>
|
||||
|
@ -424,6 +429,7 @@ function handleClearKeyword() {
|
|||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
</Transition>
|
||||
|
||||
<template #footer>
|
||||
<div class="bg-white sm:flex sm:items-center sm:justify-end">
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
VStatusDot,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import SinglePageSettingModal from "./components/SinglePageSettingModal.vue";
|
||||
import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue";
|
||||
|
@ -613,11 +614,9 @@ function handleClearFilters() {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<VEmpty
|
||||
v-if="!singlePages.items.length && !loading"
|
||||
message="你可以尝试刷新或者新建页面"
|
||||
title="当前没有页面"
|
||||
>
|
||||
<VLoading v-if="loading" />
|
||||
<Transition v-else-if="!singlePages.items.length" appear name="fade">
|
||||
<VEmpty message="你可以尝试刷新或者新建页面" title="当前没有页面">
|
||||
<template #actions>
|
||||
<VSpace>
|
||||
<VButton @click="handleFetchSinglePages">刷新</VButton>
|
||||
|
@ -634,11 +633,9 @@ function handleClearFilters() {
|
|||
</VSpace>
|
||||
</template>
|
||||
</VEmpty>
|
||||
<ul
|
||||
v-else
|
||||
class="box-border h-full w-full divide-y divide-gray-100"
|
||||
role="list"
|
||||
>
|
||||
</Transition>
|
||||
<Transition v-else appear name="fade">
|
||||
<ul class="box-border h-full w-full divide-y divide-gray-100" role="list">
|
||||
<li v-for="(singlePage, index) in singlePages.items" :key="index">
|
||||
<VEntity :is-selected="checkSelection(singlePage.page)">
|
||||
<template
|
||||
|
@ -777,6 +774,7 @@ function handleClearFilters() {
|
|||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
</Transition>
|
||||
|
||||
<template #footer>
|
||||
<div class="bg-white sm:flex sm:items-center sm:justify-end">
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
VStatusDot,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import PostTag from "./tags/components/PostTag.vue";
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
|
@ -298,21 +299,26 @@ function handleClearKeyword() {
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<VLoading v-if="loading" />
|
||||
|
||||
<Transition v-else-if="!posts.items.length" appear name="fade">
|
||||
<VEmpty
|
||||
v-if="!posts.items.length && !loading"
|
||||
message="你可以尝试刷新或者返回文章管理"
|
||||
title="没有文章被放入回收站"
|
||||
>
|
||||
<template #actions>
|
||||
<VSpace>
|
||||
<VButton @click="handleFetchPosts">刷新</VButton>
|
||||
<VButton :route="{ name: 'Posts' }" type="primary"> 返回 </VButton>
|
||||
<VButton :route="{ name: 'Posts' }" type="primary">
|
||||
返回
|
||||
</VButton>
|
||||
</VSpace>
|
||||
</template>
|
||||
</VEmpty>
|
||||
</Transition>
|
||||
|
||||
<Transition v-else appear name="fade">
|
||||
<ul
|
||||
v-else
|
||||
class="box-border h-full w-full divide-y divide-gray-100"
|
||||
role="list"
|
||||
>
|
||||
|
@ -370,7 +376,9 @@ function handleClearKeyword() {
|
|||
<VEntityField>
|
||||
<template #description>
|
||||
<RouterLink
|
||||
v-for="(contributor, contributorIndex) in post.contributors"
|
||||
v-for="(
|
||||
contributor, contributorIndex
|
||||
) in post.contributors"
|
||||
:key="contributorIndex"
|
||||
:to="{
|
||||
name: 'UserDetail',
|
||||
|
@ -430,6 +438,7 @@ function handleClearKeyword() {
|
|||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
</Transition>
|
||||
|
||||
<template #footer>
|
||||
<div class="bg-white sm:flex sm:items-center sm:justify-end">
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
VStatusDot,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import UserDropdownSelector from "@/components/dropdown-selector/UserDropdownSelector.vue";
|
||||
import PostSettingModal from "./components/PostSettingModal.vue";
|
||||
|
@ -823,12 +824,9 @@ const hasFilters = computed(() => {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<VEmpty
|
||||
v-if="!posts.items.length && !loading"
|
||||
message="你可以尝试刷新或者新建文章"
|
||||
title="当前没有文章"
|
||||
>
|
||||
<VLoading v-if="loading" />
|
||||
<Transition v-else-if="!posts.items.length" appear name="fade">
|
||||
<VEmpty message="你可以尝试刷新或者新建文章" title="当前没有文章">
|
||||
<template #actions>
|
||||
<VSpace>
|
||||
<VButton @click="handleFetchPosts">刷新</VButton>
|
||||
|
@ -845,8 +843,9 @@ const hasFilters = computed(() => {
|
|||
</VSpace>
|
||||
</template>
|
||||
</VEmpty>
|
||||
</Transition>
|
||||
<Transition v-else appear name="fade">
|
||||
<ul
|
||||
v-else
|
||||
class="box-border h-full w-full divide-y divide-gray-100"
|
||||
role="list"
|
||||
>
|
||||
|
@ -921,7 +920,9 @@ const hasFilters = computed(() => {
|
|||
<VEntityField>
|
||||
<template #description>
|
||||
<RouterLink
|
||||
v-for="(contributor, contributorIndex) in post.contributors"
|
||||
v-for="(
|
||||
contributor, contributorIndex
|
||||
) in post.contributors"
|
||||
:key="contributorIndex"
|
||||
:to="{
|
||||
name: 'UserDetail',
|
||||
|
@ -1000,6 +1001,7 @@ const hasFilters = computed(() => {
|
|||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
</Transition>
|
||||
|
||||
<template #footer>
|
||||
<div class="bg-white sm:flex sm:items-center sm:justify-end">
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
VEmpty,
|
||||
VPageHeader,
|
||||
VSpace,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import CategoryEditingModal from "./components/CategoryEditingModal.vue";
|
||||
import CategoryListItem from "./components/CategoryListItem.vue";
|
||||
|
@ -111,11 +112,9 @@ const onEditingModalClose = () => {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<VEmpty
|
||||
v-if="!categories.length && !loading"
|
||||
message="你可以尝试刷新或者新建分类"
|
||||
title="当前没有分类"
|
||||
>
|
||||
<VLoading v-if="loading" />
|
||||
<Transition v-else-if="!categories.length" appear name="fade">
|
||||
<VEmpty message="你可以尝试刷新或者新建分类" title="当前没有分类">
|
||||
<template #actions>
|
||||
<VSpace>
|
||||
<VButton @click="handleFetchCategories">刷新</VButton>
|
||||
|
@ -132,13 +131,15 @@ const onEditingModalClose = () => {
|
|||
</VSpace>
|
||||
</template>
|
||||
</VEmpty>
|
||||
</Transition>
|
||||
<Transition v-else appear name="fade">
|
||||
<CategoryListItem
|
||||
v-else
|
||||
:categories="categoriesTree"
|
||||
@change="handleUpdateInBatch"
|
||||
@delete="handleDelete"
|
||||
@open-editing="handleOpenEditingModal"
|
||||
/>
|
||||
</Transition>
|
||||
</VCard>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -16,6 +16,7 @@ import {
|
|||
VStatusDot,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import TagEditingModal from "./components/TagEditingModal.vue";
|
||||
import PostTag from "./components/PostTag.vue";
|
||||
|
@ -158,11 +159,9 @@ onMounted(async () => {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<VEmpty
|
||||
v-if="!tags.length && !loading"
|
||||
message="你可以尝试刷新或者新建标签"
|
||||
title="当前没有标签"
|
||||
>
|
||||
<VLoading v-if="loading" />
|
||||
<Transition v-else-if="!tags.length" appear name="fade">
|
||||
<VEmpty message="你可以尝试刷新或者新建标签" title="当前没有标签">
|
||||
<template #actions>
|
||||
<VSpace>
|
||||
<VButton @click="handleFetchTags">刷新</VButton>
|
||||
|
@ -175,9 +174,11 @@ onMounted(async () => {
|
|||
</VSpace>
|
||||
</template>
|
||||
</VEmpty>
|
||||
</Transition>
|
||||
|
||||
<div v-else>
|
||||
<Transition v-if="viewType === 'list'" appear name="fade">
|
||||
<ul
|
||||
v-if="viewType === 'list'"
|
||||
class="box-border h-full w-full divide-y divide-gray-100"
|
||||
role="list"
|
||||
>
|
||||
|
@ -195,7 +196,11 @@ onMounted(async () => {
|
|||
<template #end>
|
||||
<VEntityField v-if="tag.metadata.deletionTimestamp">
|
||||
<template #description>
|
||||
<VStatusDot v-tooltip="`删除中`" state="warning" animate />
|
||||
<VStatusDot
|
||||
v-tooltip="`删除中`"
|
||||
state="warning"
|
||||
animate
|
||||
/>
|
||||
</template>
|
||||
</VEntityField>
|
||||
<VEntityField
|
||||
|
@ -235,8 +240,10 @@ onMounted(async () => {
|
|||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
</Transition>
|
||||
|
||||
<div v-else class="flex flex-wrap gap-3 p-4" role="list">
|
||||
<Transition v-else appear name="fade">
|
||||
<div class="flex flex-wrap gap-3 p-4" role="list">
|
||||
<PostTag
|
||||
v-for="(tag, index) in tags"
|
||||
:key="index"
|
||||
|
@ -244,6 +251,7 @@ onMounted(async () => {
|
|||
@click="handleOpenEditingModal(tag)"
|
||||
/>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</VCard>
|
||||
</div>
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
VEmpty,
|
||||
VPageHeader,
|
||||
VSpace,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import MenuItemEditingModal from "./components/MenuItemEditingModal.vue";
|
||||
import MenuItemListItem from "./components/MenuItemListItem.vue";
|
||||
|
@ -220,8 +221,9 @@ const handleDelete = async (menuItem: MenuTreeItem) => {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<VLoading v-if="loading" />
|
||||
<Transition v-else-if="!menuItems.length" appear name="fade">
|
||||
<VEmpty
|
||||
v-if="!menuItems.length && !loading"
|
||||
message="你可以尝试刷新或者新建菜单项"
|
||||
title="当前没有菜单项"
|
||||
>
|
||||
|
@ -241,14 +243,16 @@ const handleDelete = async (menuItem: MenuTreeItem) => {
|
|||
</VSpace>
|
||||
</template>
|
||||
</VEmpty>
|
||||
</Transition>
|
||||
<Transition v-else appear name="fade">
|
||||
<MenuItemListItem
|
||||
v-else
|
||||
:menu-tree-items="menuTreeItems"
|
||||
@change="handleUpdateInBatch"
|
||||
@delete="handleDelete"
|
||||
@open-editing="handleOpenEditingModal"
|
||||
@open-create-by-parent="handleOpenCreateByParentModal"
|
||||
/>
|
||||
</Transition>
|
||||
</VCard>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
VEntity,
|
||||
VEntityField,
|
||||
VTag,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import MenuEditingModal from "./MenuEditingModal.vue";
|
||||
import { onMounted, onUnmounted, ref } from "vue";
|
||||
|
@ -190,17 +191,17 @@ onMounted(handleFetchPrimaryMenuName);
|
|||
@created="handleSelect"
|
||||
/>
|
||||
<VCard :body-class="['!p-0']" title="菜单">
|
||||
<VEmpty
|
||||
v-if="!menus.length && !loading"
|
||||
message="你可以尝试刷新或者新建菜单"
|
||||
title="当前没有菜单"
|
||||
>
|
||||
<VLoading v-if="loading" />
|
||||
<Transition v-else-if="!menus.length" appear name="fade">
|
||||
<VEmpty message="你可以尝试刷新或者新建菜单" title="当前没有菜单">
|
||||
<template #actions>
|
||||
<VSpace>
|
||||
<VButton size="sm" @click="handleFetchMenus"> 刷新</VButton>
|
||||
</VSpace>
|
||||
</template>
|
||||
</VEmpty>
|
||||
</Transition>
|
||||
<Transition v-else appear name="fade">
|
||||
<ul class="box-border h-full w-full divide-y divide-gray-100" role="list">
|
||||
<li
|
||||
v-for="(menu, index) in menus"
|
||||
|
@ -259,6 +260,7 @@ onMounted(handleFetchPrimaryMenuName);
|
|||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
</Transition>
|
||||
<template v-if="currentUserHasPermission(['system:menus:manage'])" #footer>
|
||||
<VButton block type="secondary" @click="handleOpenEditingModal()">
|
||||
新增
|
||||
|
|
|
@ -55,6 +55,8 @@ const onUpgradeModalClose = () => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Transition appear mode="out-in" name="fade">
|
||||
<div>
|
||||
<div class="bg-white px-4 py-4 sm:px-6">
|
||||
<div class="group flex items-center justify-between">
|
||||
<div class="flex flex-row items-center gap-3">
|
||||
|
@ -178,7 +180,10 @@ const onUpgradeModalClose = () => {
|
|||
>
|
||||
<dt class="text-sm font-medium text-gray-900">插件依赖</dt>
|
||||
<dd class="mt-1 text-sm sm:col-span-3 sm:mt-0">
|
||||
<VAlert description="当前有 1 个插件还未安装" title="提示"></VAlert>
|
||||
<VAlert
|
||||
description="当前有 1 个插件还未安装"
|
||||
title="提示"
|
||||
></VAlert>
|
||||
<ul class="mt-2 space-y-2">
|
||||
<li>
|
||||
<div
|
||||
|
@ -219,6 +224,8 @@ const onUpgradeModalClose = () => {
|
|||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
<ThemeUploadModal
|
||||
v-model:visible="upgradeModal"
|
||||
:upgrade-theme="selectedTheme"
|
||||
|
|
|
@ -53,6 +53,7 @@ watch(
|
|||
);
|
||||
</script>
|
||||
<template>
|
||||
<Transition appear mode="out-in" name="fade">
|
||||
<div class="bg-white p-4">
|
||||
<div>
|
||||
<FormKit
|
||||
|
@ -65,7 +66,10 @@ watch(
|
|||
type="form"
|
||||
@submit="handleSaveConfigMap"
|
||||
>
|
||||
<FormKitSchema :schema="formSchema" :data="configMapFormData[group]" />
|
||||
<FormKitSchema
|
||||
:schema="formSchema"
|
||||
:data="configMapFormData[group]"
|
||||
/>
|
||||
</FormKit>
|
||||
</div>
|
||||
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
||||
|
@ -80,4 +84,5 @@ watch(
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
VStatusDot,
|
||||
VTabItem,
|
||||
VTabs,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import LazyImage from "@/components/image/LazyImage.vue";
|
||||
import ThemePreviewModal from "./preview/ThemePreviewModal.vue";
|
||||
|
@ -214,8 +215,9 @@ const handleOpenPreview = (theme: Theme) => {
|
|||
class="my-[12px] mx-[16px]"
|
||||
>
|
||||
<VTabItem id="installed" label="已安装" class="-mx-[16px]">
|
||||
<VLoading v-if="loading" />
|
||||
<Transition v-else-if="!themes.length" appear name="fade">
|
||||
<VEmpty
|
||||
v-if="!themes.length && !loading"
|
||||
message="当前没有已安装的主题,你可以尝试刷新或者安装新主题"
|
||||
title="当前没有已安装的主题"
|
||||
>
|
||||
|
@ -237,9 +239,9 @@ const handleOpenPreview = (theme: Theme) => {
|
|||
</VSpace>
|
||||
</template>
|
||||
</VEmpty>
|
||||
|
||||
</Transition>
|
||||
<Transition v-else appear name="fade">
|
||||
<ul
|
||||
v-else
|
||||
class="box-border h-full w-full divide-y divide-gray-100"
|
||||
role="list"
|
||||
>
|
||||
|
@ -279,7 +281,9 @@ const handleOpenPreview = (theme: Theme) => {
|
|||
<div
|
||||
class="flex h-full items-center justify-center object-cover"
|
||||
>
|
||||
<span class="text-xs text-red-400">加载异常</span>
|
||||
<span class="text-xs text-red-400"
|
||||
>加载异常</span
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</LazyImage>
|
||||
|
@ -305,7 +309,11 @@ const handleOpenPreview = (theme: Theme) => {
|
|||
<template #end>
|
||||
<VEntityField v-if="theme.metadata.deletionTimestamp">
|
||||
<template #description>
|
||||
<VStatusDot v-tooltip="`删除中`" state="warning" animate />
|
||||
<VStatusDot
|
||||
v-tooltip="`删除中`"
|
||||
state="warning"
|
||||
animate
|
||||
/>
|
||||
</template>
|
||||
</VEntityField>
|
||||
<VEntityField>
|
||||
|
@ -367,9 +375,12 @@ const handleOpenPreview = (theme: Theme) => {
|
|||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
</Transition>
|
||||
</VTabItem>
|
||||
<VTabItem id="uninstalled" label="未安装" class="-mx-[16px]">
|
||||
<VEmpty v-if="!themes.length && !loading" title="当前没有未安装的主题">
|
||||
<VLoading v-if="loading" />
|
||||
<Transition v-else-if="!themes.length" appear name="fade">
|
||||
<VEmpty title="当前没有未安装的主题">
|
||||
<template #actions>
|
||||
<VSpace>
|
||||
<VButton :loading="loading" @click="handleFetchThemes">
|
||||
|
@ -378,9 +389,9 @@ const handleOpenPreview = (theme: Theme) => {
|
|||
</VSpace>
|
||||
</template>
|
||||
</VEmpty>
|
||||
|
||||
</Transition>
|
||||
<Transition v-else appear name="fade">
|
||||
<ul
|
||||
v-else
|
||||
class="box-border h-full w-full divide-y divide-gray-100"
|
||||
role="list"
|
||||
>
|
||||
|
@ -412,7 +423,9 @@ const handleOpenPreview = (theme: Theme) => {
|
|||
<div
|
||||
class="flex h-full items-center justify-center object-cover"
|
||||
>
|
||||
<span class="text-xs text-red-400">加载异常</span>
|
||||
<span class="text-xs text-red-400"
|
||||
>加载异常</span
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
</LazyImage>
|
||||
|
@ -465,6 +478,7 @@ const handleOpenPreview = (theme: Theme) => {
|
|||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
</Transition>
|
||||
</VTabItem>
|
||||
</VTabs>
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
VPageHeader,
|
||||
VSpace,
|
||||
VTabbar,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import ThemeListModal from "../components/ThemeListModal.vue";
|
||||
import ThemePreviewModal from "../components/preview/ThemePreviewModal.vue";
|
||||
|
@ -224,15 +225,13 @@ onMounted(() => {
|
|||
></VTabbar>
|
||||
</template>
|
||||
</VCard>
|
||||
<div>
|
||||
<div class="bg-white">
|
||||
<RouterView :key="activeTab" v-slot="{ Component }">
|
||||
<template v-if="Component">
|
||||
<Suspense>
|
||||
<component :is="Component"></component>
|
||||
<template #fallback>
|
||||
<div class="flex h-32 w-full justify-center bg-white">
|
||||
<span class="text-sm text-gray-600">加载中...</span>
|
||||
</div>
|
||||
<VLoading />
|
||||
</template>
|
||||
</Suspense>
|
||||
</template>
|
||||
|
|
|
@ -59,11 +59,15 @@ watchEffect(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Transition appear mode="out-in" name="fade">
|
||||
<div>
|
||||
<div class="flex items-center justify-between bg-white px-4 py-4 sm:px-6">
|
||||
<div>
|
||||
<h3 class="text-lg font-medium leading-6 text-gray-900">插件信息</h3>
|
||||
<p class="mt-1 flex max-w-2xl items-center gap-2">
|
||||
<span class="text-sm text-gray-500">{{ plugin?.spec.version }}</span>
|
||||
<span class="text-sm text-gray-500">{{
|
||||
plugin?.spec.version
|
||||
}}</span>
|
||||
<VTag>
|
||||
{{ isStarted ? "已启用" : "未启用" }}
|
||||
</VTag>
|
||||
|
@ -127,7 +131,10 @@ watchEffect(() => {
|
|||
class="list-inside"
|
||||
:class="{ 'list-disc': plugin?.spec.license.length > 1 }"
|
||||
>
|
||||
<li v-for="(license, index) in plugin.spec.license" :key="index">
|
||||
<li
|
||||
v-for="(license, index) in plugin.spec.license"
|
||||
:key="index"
|
||||
>
|
||||
<a v-if="license.url" :href="license.url" target="_blank">
|
||||
{{ license.name }}
|
||||
</a>
|
||||
|
@ -221,4 +228,6 @@ watchEffect(() => {
|
|||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
VPageHeader,
|
||||
VPagination,
|
||||
VSpace,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import PluginListItem from "./components/PluginListItem.vue";
|
||||
import PluginUploadModal from "./components/PluginUploadModal.vue";
|
||||
|
@ -310,8 +311,10 @@ function handleClearFilters() {
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<VLoading v-if="loading" />
|
||||
|
||||
<Transition v-else-if="!plugins.total" appear name="fade">
|
||||
<VEmpty
|
||||
v-if="!plugins.total && !loading"
|
||||
message="当前没有已安装的插件,你可以尝试刷新或者安装新插件"
|
||||
title="当前没有已安装的插件"
|
||||
>
|
||||
|
@ -331,9 +334,10 @@ function handleClearFilters() {
|
|||
</VSpace>
|
||||
</template>
|
||||
</VEmpty>
|
||||
</Transition>
|
||||
|
||||
<Transition v-else appear name="fade">
|
||||
<ul
|
||||
v-else
|
||||
class="box-border h-full w-full divide-y divide-gray-100"
|
||||
role="list"
|
||||
>
|
||||
|
@ -341,6 +345,7 @@ function handleClearFilters() {
|
|||
<PluginListItem :plugin="plugin" @reload="handleFetchPlugins" />
|
||||
</li>
|
||||
</ul>
|
||||
</Transition>
|
||||
|
||||
<template #footer>
|
||||
<div class="bg-white sm:flex sm:items-center sm:justify-end">
|
||||
|
|
|
@ -62,6 +62,7 @@ const handleFetchPlugin = async () => {
|
|||
await handleFetchPlugin();
|
||||
</script>
|
||||
<template>
|
||||
<Transition appear mode="out-in" name="fade">
|
||||
<div class="bg-white p-4">
|
||||
<div>
|
||||
<FormKit
|
||||
|
@ -74,7 +75,10 @@ await handleFetchPlugin();
|
|||
type="form"
|
||||
@submit="handleSaveConfigMap"
|
||||
>
|
||||
<FormKitSchema :schema="formSchema" :data="configMapFormData[group]" />
|
||||
<FormKitSchema
|
||||
:schema="formSchema"
|
||||
:data="configMapFormData[group]"
|
||||
/>
|
||||
</FormKit>
|
||||
</div>
|
||||
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
||||
|
@ -89,4 +93,5 @@ await handleFetchPlugin();
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
VPageHeader,
|
||||
VTabbar,
|
||||
VAvatar,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import BasicLayout from "@/layouts/BasicLayout.vue";
|
||||
|
||||
|
@ -172,15 +173,13 @@ watch([() => route.name, () => route.params], () => {
|
|||
></VTabbar>
|
||||
</template>
|
||||
</VCard>
|
||||
<div>
|
||||
<div class="bg-white">
|
||||
<RouterView :key="activeTab" v-slot="{ Component }">
|
||||
<template v-if="Component">
|
||||
<Suspense>
|
||||
<component :is="Component"></component>
|
||||
<template #fallback>
|
||||
<div class="flex h-32 w-full justify-center bg-white">
|
||||
<span class="text-sm text-gray-600">加载中...</span>
|
||||
</div>
|
||||
<VLoading />
|
||||
</template>
|
||||
</Suspense>
|
||||
</template>
|
||||
|
|
|
@ -12,12 +12,12 @@ import {
|
|||
VButton,
|
||||
VCard,
|
||||
VPageHeader,
|
||||
VPagination,
|
||||
VSpace,
|
||||
VTag,
|
||||
VStatusDot,
|
||||
VEntity,
|
||||
VEntityField,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import RoleEditingModal from "./components/RoleEditingModal.vue";
|
||||
|
||||
|
@ -39,7 +39,7 @@ const { currentUserHasPermission } = usePermission();
|
|||
const editingModal = ref<boolean>(false);
|
||||
const selectedRole = ref<Role>();
|
||||
|
||||
const { roles, handleFetchRoles } = useFetchRole();
|
||||
const { roles, handleFetchRoles, loading } = useFetchRole();
|
||||
|
||||
let fuse: Fuse<Role> | undefined = undefined;
|
||||
|
||||
|
@ -228,7 +228,12 @@ const handleDelete = async (role: Role) => {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<ul class="box-border h-full w-full divide-y divide-gray-100" role="list">
|
||||
<VLoading v-if="loading" />
|
||||
<Transition v-else appear name="fade">
|
||||
<ul
|
||||
class="box-border h-full w-full divide-y divide-gray-100"
|
||||
role="list"
|
||||
>
|
||||
<li v-for="(role, index) in searchResults" :key="index">
|
||||
<VEntity>
|
||||
<template #start>
|
||||
|
@ -301,12 +306,7 @@ const handleDelete = async (role: Role) => {
|
|||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<template #footer>
|
||||
<div class="bg-white sm:flex sm:items-center sm:justify-end">
|
||||
<VPagination :page="1" :size="10" :total="20" />
|
||||
</div>
|
||||
</template>
|
||||
</Transition>
|
||||
</VCard>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -33,6 +33,7 @@ await handleFetchSettings();
|
|||
await handleFetchConfigMap();
|
||||
</script>
|
||||
<template>
|
||||
<Transition appear mode="out-in" name="fade">
|
||||
<div class="bg-white p-4">
|
||||
<div>
|
||||
<FormKit
|
||||
|
@ -45,7 +46,10 @@ await handleFetchConfigMap();
|
|||
type="form"
|
||||
@submit="handleSaveConfigMap"
|
||||
>
|
||||
<FormKitSchema :schema="formSchema" :data="configMapFormData[group]" />
|
||||
<FormKitSchema
|
||||
:schema="formSchema"
|
||||
:data="configMapFormData[group]"
|
||||
/>
|
||||
</FormKit>
|
||||
</div>
|
||||
<div v-permission="['system:configmaps:manage']" class="pt-5">
|
||||
|
@ -60,4 +64,5 @@ await handleFetchConfigMap();
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
VPageHeader,
|
||||
VTabbar,
|
||||
IconSettings,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import type { SettingForm } from "@halo-dev/api-client";
|
||||
|
||||
|
@ -109,15 +110,13 @@ watch([() => route.name, () => route.params], async () => {
|
|||
></VTabbar>
|
||||
</template>
|
||||
</VCard>
|
||||
<div>
|
||||
<div class="bg-white">
|
||||
<RouterView :key="activeTab" v-slot="{ Component }">
|
||||
<template v-if="Component">
|
||||
<Suspense>
|
||||
<component :is="Component"></component>
|
||||
<template #fallback>
|
||||
<div class="flex h-32 w-full justify-center bg-white">
|
||||
<span class="text-sm text-gray-600">加载中...</span>
|
||||
</div>
|
||||
<VLoading />
|
||||
</template>
|
||||
</Suspense>
|
||||
</template>
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
VEntityField,
|
||||
Dialog,
|
||||
VStatusDot,
|
||||
VLoading,
|
||||
} from "@halo-dev/components";
|
||||
import UserEditingModal from "./components/UserEditingModal.vue";
|
||||
import UserPasswordChangeModal from "./components/UserPasswordChangeModal.vue";
|
||||
|
@ -47,6 +48,7 @@ const users = ref<UserList>({
|
|||
hasPrevious: false,
|
||||
totalPages: 0,
|
||||
});
|
||||
const loading = ref(false);
|
||||
const selectedUserNames = ref<string[]>([]);
|
||||
const selectedUser = ref<User>();
|
||||
|
||||
|
@ -56,6 +58,8 @@ let fuse: Fuse<User> | undefined = undefined;
|
|||
|
||||
const handleFetchUsers = async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
|
||||
const { data } = await apiClient.extension.user.listv1alpha1User({
|
||||
page: users.value.page,
|
||||
size: users.value.size,
|
||||
|
@ -70,6 +74,7 @@ const handleFetchUsers = async () => {
|
|||
console.error("Failed to fetch users", e);
|
||||
} finally {
|
||||
selectedUser.value = undefined;
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -385,7 +390,12 @@ onMounted(() => {
|
|||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<ul class="box-border h-full w-full divide-y divide-gray-100" role="list">
|
||||
<VLoading v-if="loading" />
|
||||
<Transition v-else appear name="fade">
|
||||
<ul
|
||||
class="box-border h-full w-full divide-y divide-gray-100"
|
||||
role="list"
|
||||
>
|
||||
<li v-for="(user, index) in searchResults" :key="index">
|
||||
<VEntity :is-selected="checkSelection(user)">
|
||||
<template
|
||||
|
@ -490,6 +500,7 @@ onMounted(() => {
|
|||
</VEntity>
|
||||
</li>
|
||||
</ul>
|
||||
</Transition>
|
||||
|
||||
<template #footer>
|
||||
<div class="bg-white sm:flex sm:items-center sm:justify-end">
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import "@halo-dev/richtext-editor/dist/style.css";
|
||||
import "@halo-dev/components/dist/style.css";
|
||||
import "@/styles/tailwind.css";
|
||||
import "@/styles/index.css";
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
.fade-enter-active {
|
||||
@apply duration-200 ease-out;
|
||||
}
|
||||
.fade-enter-from {
|
||||
@apply opacity-0;
|
||||
}
|
||||
.fade-enter-to {
|
||||
@apply opacity-100;
|
||||
}
|
||||
.fade-leave-active {
|
||||
@apply duration-200 ease-in;
|
||||
}
|
||||
.fade-leave-from {
|
||||
@apply opacity-100;
|
||||
}
|
||||
.fade-leave-to {
|
||||
@apply opacity-0;
|
||||
}
|
Loading…
Reference in New Issue