czh-20260630-优化计划,增加分页、滚动加载等

This commit is contained in:
zhihao
2026-06-30 16:15:06 +08:00
parent e20fa25000
commit fd3bfea809
8 changed files with 599 additions and 123 deletions
+41
View File
@@ -0,0 +1,41 @@
import { ref } from 'vue';
import { message } from 'ant-design-vue';
interface OptimisticOptions {
/** 调用前乐观修改本地数据 */
onOptimistic: () => void;
/** 失败时回滚本地数据 */
onRollback: () => void;
/** 失败提示消息 */
errorMsg: string;
/** 成功回调 */
onSuccess?: () => void;
}
export function useOptimisticMutation() {
const loading = ref(false);
async function execute<TResult>(
apiCall: () => Promise<TResult>,
options: OptimisticOptions
): Promise<TResult | null> {
const { onOptimistic, onRollback, errorMsg, onSuccess } = options;
onOptimistic();
try {
const result = await apiCall();
onSuccess?.();
return result;
} catch (e: any) {
console.error('[OptimisticMutation] 操作失败', e);
message.error(errorMsg);
onRollback();
throw e;
} finally {
loading.value = false;
}
}
return { execute, loading };
}
+67
View File
@@ -0,0 +1,67 @@
import { ref, computed, type Ref } from 'vue';
export interface VirtualScrollOptions {
items: Ref<any[]>;
itemHeight: number;
buffer: number;
containerRef: Ref<HTMLElement | null>;
}
export function useVirtualScroll(options: VirtualScrollOptions) {
const { items, itemHeight, buffer, containerRef } = options;
const scrollTop = ref(0);
const containerHeight = ref(0);
const totalHeight = computed(() => items.value.length * itemHeight);
const startIndex = computed(() => {
const idx = Math.floor(scrollTop.value / itemHeight) - buffer;
return Math.max(0, idx);
});
const endIndex = computed(() => {
const visible = Math.ceil(containerHeight.value / itemHeight);
const idx = Math.floor(scrollTop.value / itemHeight) + visible + buffer;
return Math.min(items.value.length, idx);
});
const visibleItems = computed(() => {
return items.value.slice(startIndex.value, endIndex.value).map((item, idx) => ({
item,
index: startIndex.value + idx,
style: {
position: 'absolute' as const,
top: `${(startIndex.value + idx) * itemHeight}px`,
width: '100%',
height: `${itemHeight}px`,
},
}));
});
const containerStyle = computed(() => ({
height: `${totalHeight.value}px`,
position: 'relative' as const,
overflow: 'hidden',
}));
function onScroll(event: Event) {
const target = event.target as HTMLElement;
scrollTop.value = target.scrollTop;
}
function updateContainer() {
if (containerRef.value) {
containerHeight.value = containerRef.value.clientHeight;
}
}
return {
visibleItems,
containerStyle,
onScroll,
updateContainer,
totalHeight,
scrollTop,
};
}