mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-05-30 01:31:06 +08:00
feat(update-center): 添加加载状态处理及UI优化
为更新中心添加加载状态管理,包括: - 在打开和刷新操作时显示加载状态 - 禁用刷新按钮防止重复操作 - 添加加载中的动画效果和提示文本 - 优化加载时的UI显示
This commit is contained in:
@@ -191,7 +191,14 @@ const loadAptssItemMetadata = async (
|
||||
}
|
||||
|
||||
const metadata = parsePrintUrisOutput(metadataResult.stdout);
|
||||
if (metadata) {
|
||||
console.log(`[DEBUG] APTSS parsed metadata:`, {
|
||||
...metadata,
|
||||
downloadUrl: `${metadata.downloadUrl}.metalink`,
|
||||
});
|
||||
} else {
|
||||
console.log(`[DEBUG] APTSS parsed metadata:`, metadata);
|
||||
}
|
||||
|
||||
if (!metadata) {
|
||||
return {
|
||||
|
||||
@@ -63,6 +63,7 @@ const createStore = (
|
||||
|
||||
return {
|
||||
isOpen: ref(true),
|
||||
loading: ref(false),
|
||||
showCloseConfirm: ref(true),
|
||||
showMigrationConfirm: ref(false),
|
||||
searchQuery: ref(""),
|
||||
@@ -220,4 +221,52 @@ describe("UpdateCenterModal", () => {
|
||||
|
||||
expect(store.requestClose).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("shows loading panel when loading with no items", () => {
|
||||
const store = createStore({
|
||||
items: [],
|
||||
tasks: [],
|
||||
warnings: [],
|
||||
hasRunningTasks: false,
|
||||
});
|
||||
store.loading.value = true;
|
||||
|
||||
render(UpdateCenterModal, {
|
||||
props: {
|
||||
show: true,
|
||||
store,
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByText("正在检查更新…")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("shows refresh hint while loading with existing items", () => {
|
||||
const store = createStore({ hasRunningTasks: false });
|
||||
store.loading.value = true;
|
||||
|
||||
render(UpdateCenterModal, {
|
||||
props: {
|
||||
show: true,
|
||||
store,
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByText("Spark Weather")).toBeTruthy();
|
||||
expect(screen.getByText("正在刷新更新列表…")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("disables refresh button while loading", () => {
|
||||
const store = createStore({ hasRunningTasks: false });
|
||||
store.loading.value = true;
|
||||
|
||||
render(UpdateCenterModal, {
|
||||
props: {
|
||||
show: true,
|
||||
store,
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByRole("button", { name: /刷新/ })).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,10 +61,16 @@ describe("updateCenter store", () => {
|
||||
open.mockResolvedValue(snapshot);
|
||||
const store = createUpdateCenterStore();
|
||||
|
||||
await store.open("apm");
|
||||
const openPromise = store.open("apm");
|
||||
|
||||
expect(store.isOpen.value).toBe(true);
|
||||
expect(store.loading.value).toBe(true);
|
||||
|
||||
await openPromise;
|
||||
|
||||
expect(open).toHaveBeenCalledWith("apm");
|
||||
expect(store.isOpen.value).toBe(true);
|
||||
expect(store.loading.value).toBe(false);
|
||||
expect(store.snapshot.value).toEqual(snapshot);
|
||||
expect(store.filteredItems.value).toEqual(snapshot.items);
|
||||
});
|
||||
@@ -76,7 +82,12 @@ describe("updateCenter store", () => {
|
||||
const store = createUpdateCenterStore();
|
||||
|
||||
await store.open("apm");
|
||||
await store.refresh();
|
||||
|
||||
const refreshPromise = store.refresh();
|
||||
expect(store.loading.value).toBe(true);
|
||||
|
||||
await refreshPromise;
|
||||
expect(store.loading.value).toBe(false);
|
||||
|
||||
expect(refresh).toHaveBeenCalledWith("apm");
|
||||
});
|
||||
@@ -209,11 +220,13 @@ describe("updateCenter store", () => {
|
||||
it("blocks close requests while the snapshot reports running tasks", () => {
|
||||
const store = createUpdateCenterStore();
|
||||
store.isOpen.value = true;
|
||||
store.loading.value = true;
|
||||
store.snapshot.value = createSnapshot({ hasRunningTasks: true });
|
||||
|
||||
store.requestClose();
|
||||
|
||||
expect(store.isOpen.value).toBe(false);
|
||||
expect(store.loading.value).toBe(false);
|
||||
expect(store.showCloseConfirm.value).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
:selected-count="selectedCount"
|
||||
:all-selected="store.allSelected.value"
|
||||
:some-selected="store.someSelected.value"
|
||||
:loading="store.loading.value"
|
||||
@refresh="store.refresh"
|
||||
@start-selected="emit('request-start-selected')"
|
||||
@request-close="store.requestClose"
|
||||
@@ -42,6 +43,22 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="store.loading.value && store.filteredItems.value.length === 0"
|
||||
class="flex min-h-0 flex-1 items-center justify-center p-6"
|
||||
>
|
||||
<div class="flex flex-col items-center gap-3 text-slate-500 dark:text-slate-400">
|
||||
<i class="fas fa-circle-notch fa-spin text-3xl"></i>
|
||||
<p class="text-sm">正在检查更新…</p>
|
||||
</div>
|
||||
</div>
|
||||
<template v-else>
|
||||
<div
|
||||
v-if="store.loading.value && store.filteredItems.value.length > 0"
|
||||
class="border-b border-slate-200/70 px-6 py-2 text-center text-xs text-slate-400 dark:border-slate-800/70 dark:text-slate-500"
|
||||
>
|
||||
正在刷新更新列表…
|
||||
</div>
|
||||
<div class="flex min-h-0 flex-1">
|
||||
<UpdateCenterList
|
||||
:items="store.filteredItems.value"
|
||||
@@ -52,6 +69,7 @@
|
||||
@unignore-item="store.unignoreItem"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<UpdateCenterMigrationConfirm
|
||||
:show="store.showMigrationConfirm.value"
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="inline-flex items-center gap-2 rounded-2xl border border-slate-200/70 px-4 py-2 text-sm font-semibold text-slate-600 transition hover:bg-slate-50 dark:border-slate-700 dark:text-slate-200 dark:hover:bg-slate-800"
|
||||
class="inline-flex items-center gap-2 rounded-2xl border border-slate-200/70 px-4 py-2 text-sm font-semibold text-slate-600 transition hover:bg-slate-50 disabled:opacity-40 dark:border-slate-700 dark:text-slate-200 dark:hover:bg-slate-800"
|
||||
:disabled="loading"
|
||||
@click="$emit('refresh')"
|
||||
>
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
刷新
|
||||
<i class="fas fa-sync-alt" :class="{ 'animate-spin': loading }"></i>
|
||||
{{ loading ? "刷新中" : "刷新" }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -88,6 +89,7 @@ const props = defineProps<{
|
||||
selectedCount: number;
|
||||
allSelected: boolean;
|
||||
someSelected: boolean;
|
||||
loading?: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -19,6 +19,7 @@ const EMPTY_SNAPSHOT: UpdateCenterSnapshot = {
|
||||
|
||||
export interface UpdateCenterStore {
|
||||
isOpen: Ref<boolean>;
|
||||
loading: Ref<boolean>;
|
||||
showCloseConfirm: Ref<boolean>;
|
||||
showMigrationConfirm: Ref<boolean>;
|
||||
searchQuery: Ref<string>;
|
||||
@@ -54,6 +55,7 @@ const matchesSearch = (item: UpdateCenterItem, query: string): boolean => {
|
||||
|
||||
export const createUpdateCenterStore = (): UpdateCenterStore => {
|
||||
const isOpen = ref(false);
|
||||
const loading = ref(false);
|
||||
const showCloseConfirm = ref(false);
|
||||
const showMigrationConfirm = ref(false);
|
||||
const searchQuery = ref("");
|
||||
@@ -134,17 +136,27 @@ export const createUpdateCenterStore = (): UpdateCenterStore => {
|
||||
const open = async (storeFilter: StoreFilter = "both"): Promise<void> => {
|
||||
lastStoreFilter = storeFilter;
|
||||
resetSessionState();
|
||||
isOpen.value = true;
|
||||
loading.value = true;
|
||||
try {
|
||||
const nextSnapshot = await window.updateCenter.open(storeFilter);
|
||||
applySnapshot(nextSnapshot);
|
||||
isOpen.value = true;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const refresh = async (
|
||||
storeFilter: StoreFilter = lastStoreFilter,
|
||||
): Promise<void> => {
|
||||
lastStoreFilter = storeFilter;
|
||||
loading.value = true;
|
||||
try {
|
||||
const nextSnapshot = await window.updateCenter.refresh(storeFilter);
|
||||
applySnapshot(nextSnapshot);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const ignoreItem = async (
|
||||
@@ -197,6 +209,7 @@ export const createUpdateCenterStore = (): UpdateCenterStore => {
|
||||
|
||||
const closeNow = (): void => {
|
||||
resetSessionState();
|
||||
loading.value = false;
|
||||
isOpen.value = false;
|
||||
};
|
||||
|
||||
@@ -270,6 +283,7 @@ export const createUpdateCenterStore = (): UpdateCenterStore => {
|
||||
|
||||
return {
|
||||
isOpen,
|
||||
loading,
|
||||
showCloseConfirm,
|
||||
showMigrationConfirm,
|
||||
searchQuery,
|
||||
|
||||
Reference in New Issue
Block a user