czh-20260630-优化计划,增加分页、滚动加载等
This commit is contained in:
@@ -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 };
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user