整体优化
This commit is contained in:
+111
-28
@@ -13,7 +13,7 @@
|
||||
|
||||
<n-scrollbar ref="virtualListInst" class="relative z-[1] py-6" :style="boxStyle" @scroll="handleScroll">
|
||||
<section
|
||||
class="relative mx-auto w-[82%] rounded-[28px] border border-white/62 bg-[linear-gradient(145deg,rgba(255,251,246,0.68),rgba(237,246,244,0.56))] p-6 shadow-[0_20px_50px_rgba(110,124,112,0.11)] backdrop-blur-[20px] after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:bg-[linear-gradient(120deg,rgba(255,255,255,0.24),transparent_45%)] after:content-['']">
|
||||
class="relative mx-auto w-[82%] rounded-[28px] border border-white/62 bg-[linear-gradient(145deg,rgba(255,251,246,0.68),rgba(237,246,244,0.56))] p-6 shadow-[0_1px_3px_rgba(97,121,146,0.05),0_16px_38px_rgba(97,121,146,0.09),0_44px_84px_rgba(178,128,88,0.07),inset_0_1px_0_rgba(255,255,255,0.7)] backdrop-blur-[20px] after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:bg-[linear-gradient(120deg,rgba(255,255,255,0.24),transparent_45%)] after:content-['']">
|
||||
<div class="max-w-[48rem]">
|
||||
<h1 class="m-0 text-[clamp(1.8rem,2.5vw,2.8rem)] leading-[1.2] text-[#2c3a4a]">
|
||||
把喜欢的画面收进一片更柔和的光里
|
||||
@@ -24,11 +24,11 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mt-6 rounded-full border border-white/62 bg-[linear-gradient(135deg,rgba(255,249,241,0.76),rgba(236,245,243,0.72)),radial-gradient(circle_at_top_right,rgba(255,255,255,0.28),transparent_34%)] shadow-[inset_0_1px_0_rgba(255,255,255,0.55)]">
|
||||
<n-input class="gallery-search" round size="large" v-model:value="kw" @keyup.enter="onSearch"
|
||||
placeholder="请输入关键词,按回车开始搜索">
|
||||
class="mt-6 rounded-full border border-white/62 bg-[linear-gradient(135deg,rgba(255,249,241,0.76),rgba(236,245,243,0.72)),radial-gradient(circle_at_top_right,rgba(255,255,255,0.28),transparent_34%)] shadow-[inset_0_1px_0_rgba(255,255,255,0.55)] transition-[box-shadow,border-color] duration-300">
|
||||
<n-input class="gallery-search" round size="large" clearable v-model:value="kw" @keyup.enter="onSearch"
|
||||
placeholder="输入关键词,按回车开始搜索">
|
||||
<template #suffix>
|
||||
<n-icon class="cursor-pointer text-slate-500" size="large">
|
||||
<n-icon class="cursor-pointer text-slate-500 transition-colors duration-200 hover:text-primary" size="large" @click="onSearch">
|
||||
<icon-search />
|
||||
</n-icon>
|
||||
</template>
|
||||
@@ -37,43 +37,62 @@
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="mx-auto mt-6 w-[82%] rounded-[30px] border border-white/62 bg-white/42 p-6 shadow-[0_18px_42px_rgba(109,126,117,0.1)] backdrop-blur-[18px]">
|
||||
class="mx-auto mt-6 w-[82%] rounded-[30px] border border-white/62 bg-white/42 p-6 shadow-[0_1px_3px_rgba(97,121,146,0.05),0_14px_30px_rgba(97,121,146,0.09),0_36px_72px_rgba(178,128,88,0.07),inset_0_1px_0_rgba(255,255,255,0.7)] backdrop-blur-[18px]">
|
||||
<div class="mb-5 flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
|
||||
<div>
|
||||
<div class="text-[1.6rem] font-semibold text-[#2f4050]">图片列表</div>
|
||||
<div class="mt-1 flex items-center gap-2 text-[13px] text-slate-500">
|
||||
<span v-if="searchKeyword" class="inline-flex items-center rounded-full bg-[rgba(236,102,171,0.1)] px-2.5 py-0.5 font-semibold text-[rgb(212,88,154)]">
|
||||
关键词:{{ searchKeyword }}
|
||||
</span>
|
||||
<span>{{ galleryStats.status }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="inline-flex items-center whitespace-nowrap rounded-full border border-white/80 bg-white/68 px-4 py-[0.65rem] text-[#526376]">
|
||||
<div class="inline-flex items-center gap-2 self-start rounded-full bg-white/68 px-5 py-2 text-[#526376] shadow-[0_2px_8px_rgba(97,121,146,0.08),inset_0_1px_0_rgba(255,255,255,0.7)]">
|
||||
<span class="inline-block h-2 w-2 rounded-full" :class="loading ? 'animate-pulse bg-[#ec66ab]' : 'bg-[#7fb69a]'"></span>
|
||||
{{ galleryStats.loaded }} 张
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Waterfall ref="waterfall" :list="fileList" :gutter="gutter" :columns="column" class="w-full" img-selector="url"
|
||||
<Waterfall ref="waterfall" :list="fileList" :gutter="gutter" :columns="column" row-key="filepath" class="w-full" img-selector="url"
|
||||
animation-effect="fadeIn" :animation-duration="1000" :animation-delay="300" backgroundColor="transparent">
|
||||
<template #item="{ item }">
|
||||
<div
|
||||
class="group relative flex h-full items-center justify-center overflow-hidden rounded-[24px] border border-white/82 bg-[linear-gradient(180deg,rgba(255,252,248,0.92),rgba(239,244,241,0.78))] shadow-[0_16px_34px_rgba(116,132,124,0.14)] backdrop-blur-[14px] transition-[transform,box-shadow,border-color] duration-[320ms] [transition-timing-function:cubic-bezier(0.22,1,0.36,1)] hover:border-white/196 hover:shadow-[0_22px_42px_rgba(102,123,112,0.18)]">
|
||||
<LazyImg class="overflow-hidden rounded-[20px]" :Pwidth="item.width" :Pheight="item.height"
|
||||
:url="item.filepath" />
|
||||
class="gallery-card group relative flex h-full items-center justify-center overflow-hidden rounded-[12px] border border-white/82 bg-[linear-gradient(180deg,rgba(255,252,248,0.92),rgba(239,244,241,0.78))] backdrop-blur-[14px] transition-[transform,box-shadow,border-color] duration-[320ms] [transition-timing-function:cubic-bezier(0.22,1,0.36,1)] hover:-translate-y-1">
|
||||
<LazyImg class="gallery-img overflow-hidden rounded-[12px]" :Pwidth="item.width" :Pheight="item.height"
|
||||
:url="item.thumbnail" :preUrl="item.filepath" />
|
||||
<div
|
||||
class="absolute left-0 right-0 top-0 z-10 overflow-hidden text-ellipsis whitespace-nowrap rounded-[14px] border border-white/22 bg-[rgba(39,50,65,0.1)] px-3 py-1 items-center justify-center text-sm text-white hidden backdrop-blur-[12px] group-hover:flex">
|
||||
class="pointer-events-none absolute left-0 right-0 top-0 z-10 truncate rounded-[12px] border-b border-white/22 bg-gradient-to-b from-[rgba(39,50,65,0.55)] to-transparent px-3 pb-3 pt-1.5 text-sm text-white opacity-0 backdrop-blur-[6px] transition-opacity duration-300 group-hover:opacity-100">
|
||||
{{ item.filename }}
|
||||
</div>
|
||||
<div
|
||||
class="absolute bottom-0 left-0 right-0 z-10 flex items-center justify-between gap-3 rounded-[14px] border border-white/22 bg-[rgba(39,50,65,0.5)] px-3 py-1 text-white backdrop-blur-[12px]">
|
||||
class="absolute bottom-0 left-0 right-0 z-10 flex translate-y-[102%] items-center justify-between gap-3 rounded-[12px] border border-white/22 bg-[rgba(39,50,65,0.55)] px-3 py-1.5 text-white backdrop-blur-[12px] transition-transform duration-[320ms] [transition-timing-function:cubic-bezier(0.22,1,0.36,1)] group-hover:translate-y-0">
|
||||
<div class="min-w-0 overflow-hidden text-ellipsis whitespace-nowrap text-sm text-[#eef4f8]">
|
||||
由 <span class="font-semibold text-[#ffd8cb]">{{ item.nickname }}</span> 分享
|
||||
</div>
|
||||
<div
|
||||
class="inline-flex flex-shrink-0 cursor-pointer items-center rounded-full bg-white/18 px-4 py-1 text-[0.86rem] font-semibold text-[#ffe3d3] transition-colors duration-200 hover:bg-white/24 hover:text-white"
|
||||
@click="downloadFile(item.filepath)">
|
||||
<icon-download class="mr-1 h-5 w-5" />
|
||||
class="inline-flex flex-shrink-0 cursor-pointer items-center rounded-full bg-white/18 px-3.5 py-1 text-[0.86rem] font-semibold text-[#ffe3d3] transition-[background-color,transform] duration-200 hover:scale-105 hover:bg-white/28 hover:text-white active:scale-95"
|
||||
@click.stop="downloadFile(item.filepath)">
|
||||
<icon-download class="mr-1 h-4 w-4" />
|
||||
下载
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Waterfall>
|
||||
|
||||
<div v-if="fileList.length === 0 && !loading" class="flex flex-col items-center justify-center py-16 text-slate-400">
|
||||
<icon-search class="mb-3 h-10 w-10 opacity-50" />
|
||||
<p class="text-[15px]">{{ searchKeyword ? '未找到相关图片,换个关键词试试' : '暂无图片' }}</p>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="flex items-center justify-center gap-2 py-6 text-sm text-slate-400">
|
||||
<icon-loading class="zhuan h-5 w-5 text-primary" />
|
||||
<span>{{ fileList.length ? '加载中...' : '首次加载中...' }}</span>
|
||||
</div>
|
||||
<div v-else-if="isLoadAll && fileList.length" class="py-5 text-center text-[13px] text-slate-400">
|
||||
— 已加载全部 {{ fileList.length }} 张图片 —
|
||||
</div>
|
||||
</section>
|
||||
</n-scrollbar>
|
||||
</div>
|
||||
@@ -120,6 +139,7 @@ const cwidth = ref(240)
|
||||
const column = ref(5)
|
||||
const gutter = ref(18)
|
||||
const isLoadAll = ref(false)
|
||||
let requestSeq = 0
|
||||
|
||||
const searchKeyword = computed(() => kw.value.trim())
|
||||
|
||||
@@ -180,6 +200,7 @@ function resetGalleryState() {
|
||||
async function getFileList() {
|
||||
if (loading.value) return
|
||||
loading.value = true
|
||||
const seq = ++requestSeq
|
||||
|
||||
try {
|
||||
const res = await $http.file.getFileList({
|
||||
@@ -189,6 +210,9 @@ async function getFileList() {
|
||||
})
|
||||
const list = res.data as GalleryItem[]
|
||||
|
||||
// 过期请求直接丢弃,避免搜索时旧结果覆盖新结果
|
||||
if (seq !== requestSeq) return
|
||||
|
||||
if (list.length < ps.value) {
|
||||
isLoadAll.value = true
|
||||
}
|
||||
@@ -197,8 +221,10 @@ async function getFileList() {
|
||||
} catch (error) {
|
||||
console.error('获取图片列表失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
renderWaterfall()
|
||||
if (seq === requestSeq) {
|
||||
loading.value = false
|
||||
renderWaterfall()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,12 +242,17 @@ const handleScroll = throttle((e: Event) => {
|
||||
pn.value += 1
|
||||
getFileList()
|
||||
}
|
||||
}, 1000)
|
||||
}, 300)
|
||||
|
||||
function onSearch() {
|
||||
pn.value = 1
|
||||
kw.value = kw.value.trim()
|
||||
isLoadAll.value = false
|
||||
// 立即清空旧列表,避免搜索时看到上一批结果
|
||||
fileList.value = []
|
||||
// 若正在加载中,先解除守卫,让新搜索请求可以发起
|
||||
loading.value = false
|
||||
requestSeq += 1
|
||||
getFileList()
|
||||
}
|
||||
|
||||
@@ -240,16 +271,17 @@ onMounted(() => {
|
||||
calculateColumns()
|
||||
updateBoxStyle()
|
||||
getFileList()
|
||||
window.addEventListener('resize', calculateColumns)
|
||||
window.addEventListener('resize', updateBoxStyle)
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
window.addEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
function handleResize() {
|
||||
calculateColumns()
|
||||
updateBoxStyle()
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
resetGalleryState()
|
||||
window.removeEventListener('resize', calculateColumns)
|
||||
window.removeEventListener('resize', updateBoxStyle)
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
window.removeEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
// 防抖函数
|
||||
@@ -260,8 +292,14 @@ onUnmounted(() => {
|
||||
:deep(.gallery-search .n-input) {
|
||||
background: rgba(255, 255, 255, 0.74) !important;
|
||||
border: 1px solid rgba(255, 255, 255, 0.7) !important;
|
||||
box-shadow: 0 10px 28px rgba(109, 138, 132, 0.1) !important;
|
||||
box-shadow:
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.6),
|
||||
0 2px 6px rgba(97, 121, 146, 0.05),
|
||||
0 10px 24px rgba(97, 121, 146, 0.09) !important;
|
||||
backdrop-filter: blur(18px);
|
||||
transition:
|
||||
border-color 0.25s ease,
|
||||
box-shadow 0.25s ease;
|
||||
}
|
||||
|
||||
:deep(.gallery-search .n-input__input-el) {
|
||||
@@ -274,7 +312,52 @@ onUnmounted(() => {
|
||||
|
||||
:deep(.gallery-search .n-input.n-input--focus) {
|
||||
border-color: rgba(122, 160, 148, 0.7) !important;
|
||||
box-shadow: 0 16px 38px rgba(118, 149, 139, 0.16) !important;
|
||||
box-shadow:
|
||||
0 0 0 4px rgba(122, 160, 148, 0.12),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.6),
|
||||
0 2px 6px rgba(97, 121, 146, 0.06),
|
||||
0 16px 36px rgba(97, 121, 146, 0.14) !important;
|
||||
}
|
||||
|
||||
/* 图片卡片 hover 增强 */
|
||||
.gallery-card {
|
||||
box-shadow:
|
||||
0 1px 3px rgba(97, 121, 146, 0.05),
|
||||
0 12px 26px rgba(97, 121, 146, 0.09),
|
||||
0 32px 60px rgba(178, 128, 88, 0.06),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.72) !important;
|
||||
}
|
||||
|
||||
.gallery-card:hover {
|
||||
border-color: rgba(255, 255, 255, 0.88) !important;
|
||||
box-shadow:
|
||||
0 2px 6px rgba(97, 121, 146, 0.06),
|
||||
0 18px 38px rgba(97, 121, 146, 0.14),
|
||||
0 48px 90px rgba(178, 128, 88, 0.1),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.8) !important;
|
||||
}
|
||||
|
||||
.gallery-img {
|
||||
transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
.gallery-card:hover .gallery-img {
|
||||
transform: scale(1.045);
|
||||
}
|
||||
|
||||
/* 加载图标旋转 */
|
||||
.zhuan {
|
||||
animation: gallery-spin 1.5s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes gallery-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.n-image img) {
|
||||
|
||||
Reference in New Issue
Block a user