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
Ryan Wang 2022-11-24 11:46:10 +08:00 committed by GitHub
parent c0877e2a9c
commit 3f3147ce34
27 changed files with 1941 additions and 1790 deletions

View File

@ -21,3 +21,4 @@ export * from "./components/empty";
export * from "./components/status";
export * from "./components/entity";
export * from "./components/toast";
export * from "./components/loading";

View File

@ -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>

View File

@ -0,0 +1 @@
export { default as VLoading } from "./Loading.vue";

View File

@ -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>

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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">

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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()">
新增

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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">

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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">

View File

@ -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";

18
src/styles/index.css Normal file
View File

@ -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;
}