mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-04-30 03:10:16 +08:00
feat(update-center): 添加全选功能及状态管理
添加全选复选框组件及相关状态管理逻辑 实现全选/取消全选功能 添加部分选中状态显示 更新工具栏组件以支持新功能
This commit is contained in:
@@ -20,9 +20,12 @@
|
||||
<UpdateCenterToolbar
|
||||
:search-query="store.searchQuery.value"
|
||||
:selected-count="selectedCount"
|
||||
:all-selected="store.allSelected.value"
|
||||
:some-selected="store.someSelected.value"
|
||||
@refresh="store.refresh"
|
||||
@start-selected="emit('request-start-selected')"
|
||||
@request-close="store.requestClose"
|
||||
@toggle-select-all="store.toggleSelectAll"
|
||||
@update:search-query="emit('update:search-query', $event)"
|
||||
/>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="min-h-0 overflow-y-auto overscroll-contain border-r border-slate-200/70 p-6 dark:border-slate-800/70"
|
||||
class="min-h-0 overflow-y-auto overscroll-contain scrollbar-muted border-r border-slate-200/70 p-6 dark:border-slate-800/70"
|
||||
>
|
||||
<div
|
||||
v-if="items.length === 0"
|
||||
|
||||
@@ -40,6 +40,22 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<label class="inline-flex cursor-pointer items-center gap-2 select-none">
|
||||
<input
|
||||
ref="selectAllRef"
|
||||
type="checkbox"
|
||||
class="h-4 w-4 rounded border-slate-300 accent-brand focus:ring-brand"
|
||||
:checked="allSelected"
|
||||
@change="$emit('toggle-select-all')"
|
||||
/>
|
||||
<span class="text-sm font-medium text-slate-700 dark:text-slate-200">全选</span>
|
||||
</label>
|
||||
<span class="text-sm text-slate-400 dark:text-slate-500">
|
||||
已选 {{ selectedCount }} 项
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<label class="block">
|
||||
<span class="sr-only">搜索更新</span>
|
||||
<input
|
||||
@@ -54,18 +70,35 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
searchQuery: string;
|
||||
selectedCount: number;
|
||||
allSelected: boolean;
|
||||
someSelected: boolean;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: "refresh"): void;
|
||||
(e: "start-selected"): void;
|
||||
(e: "request-close"): void;
|
||||
(e: "toggle-select-all"): void;
|
||||
(e: "update:search-query", value: string): void;
|
||||
}>();
|
||||
|
||||
const selectAllRef = ref<HTMLInputElement | null>(null);
|
||||
|
||||
watch(
|
||||
[() => props.someSelected, () => props.allSelected],
|
||||
() => {
|
||||
if (selectAllRef.value) {
|
||||
selectAllRef.value.indeterminate = props.someSelected && !props.allSelected;
|
||||
}
|
||||
},
|
||||
{ flush: "post" },
|
||||
);
|
||||
|
||||
const handleInput = (event: Event): void => {
|
||||
const target = event.target as HTMLInputElement | null;
|
||||
emit("update:search-query", target?.value ?? props.searchQuery);
|
||||
|
||||
@@ -23,11 +23,14 @@ export interface UpdateCenterStore {
|
||||
selectedTaskKeys: Ref<Set<string>>;
|
||||
snapshot: Ref<UpdateCenterSnapshot>;
|
||||
filteredItems: ComputedRef<UpdateCenterItem[]>;
|
||||
allSelected: ComputedRef<boolean>;
|
||||
someSelected: ComputedRef<boolean>;
|
||||
bind: () => void;
|
||||
unbind: () => void;
|
||||
open: () => Promise<void>;
|
||||
refresh: () => Promise<void>;
|
||||
toggleSelection: (taskKey: string) => void;
|
||||
toggleSelectAll: () => void;
|
||||
getSelectedItems: () => UpdateCenterItem[];
|
||||
closeNow: () => void;
|
||||
startSelected: () => Promise<void>;
|
||||
@@ -74,11 +77,25 @@ export const createUpdateCenterStore = (): UpdateCenterStore => {
|
||||
snapshot.value = nextSnapshot;
|
||||
};
|
||||
|
||||
const selectableItems = computed(() =>
|
||||
snapshot.value.items.filter((item) => item.ignored !== true),
|
||||
);
|
||||
|
||||
const filteredItems = computed(() => {
|
||||
const query = searchQuery.value.trim();
|
||||
return snapshot.value.items.filter((item) => matchesSearch(item, query));
|
||||
});
|
||||
|
||||
const allSelected = computed(() => {
|
||||
const selectable = selectableItems.value;
|
||||
return selectable.length > 0 && selectable.every((item) => selectedTaskKeys.value.has(item.taskKey));
|
||||
});
|
||||
|
||||
const someSelected = computed(() => {
|
||||
const selectable = selectableItems.value;
|
||||
return selectable.length > 0 && selectable.some((item) => selectedTaskKeys.value.has(item.taskKey));
|
||||
});
|
||||
|
||||
const handleState = (nextSnapshot: UpdateCenterSnapshot): void => {
|
||||
applySnapshot(nextSnapshot);
|
||||
};
|
||||
@@ -133,6 +150,15 @@ export const createUpdateCenterStore = (): UpdateCenterStore => {
|
||||
selectedTaskKeys.value = nextSelection;
|
||||
};
|
||||
|
||||
const toggleSelectAll = (): void => {
|
||||
const selectable = selectableItems.value;
|
||||
if (allSelected.value) {
|
||||
selectedTaskKeys.value = new Set();
|
||||
} else {
|
||||
selectedTaskKeys.value = new Set(selectable.map((item) => item.taskKey));
|
||||
}
|
||||
};
|
||||
|
||||
const getSelectedItems = (): UpdateCenterItem[] => {
|
||||
return snapshot.value.items.filter(
|
||||
(item) =>
|
||||
@@ -217,11 +243,14 @@ export const createUpdateCenterStore = (): UpdateCenterStore => {
|
||||
selectedTaskKeys,
|
||||
snapshot,
|
||||
filteredItems,
|
||||
allSelected,
|
||||
someSelected,
|
||||
bind,
|
||||
unbind,
|
||||
open,
|
||||
refresh,
|
||||
toggleSelection,
|
||||
toggleSelectAll,
|
||||
getSelectedItems,
|
||||
closeNow,
|
||||
startSelected,
|
||||
|
||||
Reference in New Issue
Block a user