修复预设过多切换主题卡顿问题,更改预设图标样式
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { computed, ref, watch, onMounted, onUnmounted, nextTick } from 'vue';
|
||||
import type { ExtendedPreset, PresetType } from '../../types';
|
||||
import IconPresetType from '../icons/IconPresetType.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
presets: ExtendedPreset[];
|
||||
@@ -15,17 +16,73 @@ const emit = defineEmits<{
|
||||
(e: 'share', preset: ExtendedPreset): void;
|
||||
}>();
|
||||
|
||||
function getTypeIcon(type: PresetType) {
|
||||
const icons: Record<string, string> = {
|
||||
positive: '🪄',
|
||||
negative: '⛔',
|
||||
setting: '⚙️',
|
||||
style: '🖌️',
|
||||
character: '🧙',
|
||||
scene: '🏞️',
|
||||
custom: '🧩'
|
||||
};
|
||||
return icons[type] || '🧩';
|
||||
// Lazy Loading Logic
|
||||
const PAGE_SIZE = 20;
|
||||
const displayLimit = ref(PAGE_SIZE);
|
||||
const containerRef = ref<HTMLElement | null>(null);
|
||||
const sentinelRef = ref<HTMLElement | null>(null);
|
||||
let observer: IntersectionObserver | null = null;
|
||||
|
||||
const displayedPresets = computed(() => {
|
||||
return props.presets.slice(0, displayLimit.value);
|
||||
});
|
||||
|
||||
watch(() => props.presets, () => {
|
||||
displayLimit.value = PAGE_SIZE;
|
||||
if (containerRef.value) {
|
||||
containerRef.value.scrollTop = 0;
|
||||
}
|
||||
// Reset observer if needed, but the sentinel remains or is recreated
|
||||
nextTick(() => {
|
||||
checkIntersection();
|
||||
});
|
||||
});
|
||||
|
||||
function checkIntersection() {
|
||||
if (observer && sentinelRef.value) {
|
||||
// Re-observe just in case
|
||||
observer.disconnect();
|
||||
observer.observe(sentinelRef.value);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
observer = new IntersectionObserver((entries) => {
|
||||
if (entries[0] && entries[0].isIntersecting) {
|
||||
loadMore();
|
||||
}
|
||||
}, {
|
||||
root: containerRef.value,
|
||||
rootMargin: '200px',
|
||||
threshold: 0.1
|
||||
});
|
||||
|
||||
if (sentinelRef.value) {
|
||||
observer.observe(sentinelRef.value);
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (observer) {
|
||||
observer.disconnect();
|
||||
observer = null;
|
||||
}
|
||||
});
|
||||
|
||||
// Watch sentinel ref changes (e.g. when switching from empty to having presets)
|
||||
watch(sentinelRef, (newEl) => {
|
||||
if (observer) {
|
||||
observer.disconnect();
|
||||
if (newEl) {
|
||||
observer.observe(newEl);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function loadMore() {
|
||||
if (displayLimit.value < props.presets.length) {
|
||||
displayLimit.value += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeLabel(type: PresetType) {
|
||||
@@ -47,18 +104,19 @@ function formatDate(dateStr: string) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="preset-list-container">
|
||||
<div class="preset-list-container" ref="containerRef">
|
||||
<div v-if="presets.length === 0" class="empty-state">
|
||||
<div class="empty-icon">📭</div>
|
||||
<p class="empty-text">暂无预设</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="preset-grid">
|
||||
<div v-for="preset in presets" :key="preset.id" class="preset-card">
|
||||
<div class="card-header">
|
||||
<div class="preset-type" :title="getTypeLabel(preset.type)">
|
||||
{{ getTypeIcon(preset.type) }}
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="preset-grid">
|
||||
<div v-for="preset in displayedPresets" :key="preset.id" class="preset-card nav-btn">
|
||||
<div class="card-header">
|
||||
<div class="preset-type" :title="getTypeLabel(preset.type)">
|
||||
<IconPresetType :type="preset.type" width="24" height="24" />
|
||||
</div>
|
||||
<h4 class="preset-name" :title="preset.name">{{ preset.name }}</h4>
|
||||
<div class="preset-actions">
|
||||
<button @click="emit('apply', preset)" class="action-btn apply-btn" title="应用预设">
|
||||
@@ -126,6 +184,8 @@ function formatDate(dateStr: string) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ref="sentinelRef" class="sentinel" style="height: 20px; width: 100%;"></div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -162,11 +222,13 @@ function formatDate(dateStr: string) {
|
||||
background-color: var(--color-bg-primary);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1rem;
|
||||
padding: 0.75rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
transition: all 0.2s ease;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.preset-card:hover {
|
||||
@@ -183,8 +245,11 @@ function formatDate(dateStr: string) {
|
||||
|
||||
.preset-type {
|
||||
font-size: 1.25rem;
|
||||
margin-right: 0.75rem;
|
||||
margin-right: 0.5rem;
|
||||
flex-shrink: 0;
|
||||
line-height: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.preset-name {
|
||||
|
||||
Reference in New Issue
Block a user