重构图片组件,使用naive-ui的NImage替换el-image,移除冗余依赖和样式
This commit is contained in:
@ -1,3 +1,2 @@
|
||||
@import "./base.less";
|
||||
@import "qweather-icons/font/qweather-icons.css";
|
||||
@import "vue3-perfect-scrollbar/style.css";
|
||||
|
||||
@ -1,47 +1,20 @@
|
||||
<template>
|
||||
<div class="lazy__box">
|
||||
<div class="lazy__resource">
|
||||
<div class="image-container">
|
||||
<el-image
|
||||
ref="lazyRef"
|
||||
class="lazy__img"
|
||||
:src="url"
|
||||
:preview-src-list="previewSrcList.length > 0 ? previewSrcList : []"
|
||||
fit="contain"
|
||||
:preview-teleported="true"
|
||||
:initial-index="0"
|
||||
:hide-on-click-modal="hideOnClickModal"
|
||||
@load="handleLoad"
|
||||
@error="handleError"
|
||||
>
|
||||
<template #placeholder>
|
||||
<img :src="loading" alt="loading" />
|
||||
</template>
|
||||
<template #error>
|
||||
<img :src="errorImg" alt="error" />
|
||||
</template>
|
||||
</el-image>
|
||||
<div
|
||||
class="overlay"
|
||||
v-if="previewSrcList.length > 0 && previewIcon"
|
||||
@click="hanldeShowPreview"
|
||||
>
|
||||
<div class="preview-icon">
|
||||
<img :src="previewIcon" alt="preview" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-container">
|
||||
<n-image ref="lazyRef" class="lazy__img" :src="url" @load="handleLoad" @error="handleError">
|
||||
<template #placeholder>
|
||||
<img :src="loading" alt="loading" />
|
||||
</template>
|
||||
<template #error>
|
||||
<img :src="errorImg" alt="error" />
|
||||
</template>
|
||||
</n-image>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElImage } from "element-plus";
|
||||
import type { PropType } from "vue";
|
||||
import { inject, onMounted, ref, toRefs } from "vue";
|
||||
import loadError from "../assets/loadError.png";
|
||||
import loadingImg from "../assets/loading.gif";
|
||||
import type { Nullable } from "../types/util";
|
||||
const props = defineProps({
|
||||
previewIcon: {
|
||||
type: String,
|
||||
@ -58,21 +31,12 @@ const props = defineProps({
|
||||
errorImg: {
|
||||
type: String,
|
||||
default: loadError,
|
||||
},
|
||||
previewSrcList: {
|
||||
type: Array as PropType<string[]>,
|
||||
default: () => [],
|
||||
},
|
||||
hideOnClickModal: {
|
||||
// 是否可以通过点击遮罩层关闭预览
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
}
|
||||
});
|
||||
const { url, loading, errorImg, previewSrcList } = toRefs(props);
|
||||
const { url, loading, errorImg } = toRefs(props);
|
||||
|
||||
const imgLoaded = inject("imgLoaded") as () => void;
|
||||
const lazyRef = ref<Nullable<InstanceType<typeof ElImage>>>(null);
|
||||
const lazyRef = ref<any>(null);
|
||||
|
||||
const handleLoad = () => {
|
||||
imgLoaded();
|
||||
@ -81,12 +45,7 @@ const handleLoad = () => {
|
||||
const handleError = () => {
|
||||
// 可以在这里添加错误处理逻辑
|
||||
};
|
||||
// 显示预览
|
||||
const hanldeShowPreview = () => {
|
||||
if (lazyRef.value) {
|
||||
lazyRef.value.showPreview();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
if (lazyRef.value) {
|
||||
imgLoaded();
|
||||
@ -95,89 +54,18 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.lazy__box {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.lazy__resource {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.lazy__img {
|
||||
:deep(.n-image img) {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
:deep(.el-image) {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
:deep(.el-image__inner) {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
/* object-fit: cover; */
|
||||
}
|
||||
|
||||
.lazy__img img[alt="loading"],
|
||||
.lazy__img img[alt="error"] {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
height: 80px;
|
||||
padding: 1em;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.image-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.image-container:hover .overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.preview-icon {
|
||||
color: white;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
:deep(.el-image__placeholder),
|
||||
:deep(.el-image__error) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.el-image__placeholder) img,
|
||||
:deep(.el-image__error) img {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
object-fit: contain;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,34 +1,21 @@
|
||||
<template>
|
||||
<div
|
||||
ref="waterfallWrapper"
|
||||
class="waterfall-list"
|
||||
:style="{ height: `${wrapperHeight}px` }"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in list"
|
||||
:key="getKey(item, index)"
|
||||
class="waterfall-item"
|
||||
>
|
||||
<div ref="waterfallWrapper" class="waterfall-list" :style="{ height: `${wrapperHeight}px` }">
|
||||
<div v-for="(item, index) in list" :key="getKey(item, index)" class="waterfall-item">
|
||||
<div class="waterfall-card">
|
||||
<slot
|
||||
name="item"
|
||||
:item="item"
|
||||
:index="index"
|
||||
:url="getRenderURL(item)"
|
||||
/>
|
||||
<slot name="item" :item="item" :index="index" :url="getRenderURL(item)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useDebounceFn } from "@vueuse/core";
|
||||
import type { PropType } from "vue";
|
||||
import { provide, ref, watch } from "vue";
|
||||
import { useDebounceFn } from "@vueuse/core";
|
||||
import type { ViewCard } from "../types/waterfall";
|
||||
import { useCalculateCols, useLayout } from "../use";
|
||||
import Lazy from "../utils/Lazy";
|
||||
import { getValue } from "../utils/util";
|
||||
import type { ViewCard } from "../types/waterfall";
|
||||
|
||||
const props = defineProps({
|
||||
list: {
|
||||
@ -85,7 +72,7 @@ const props = defineProps({
|
||||
},
|
||||
loadProps: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
default: () => { },
|
||||
},
|
||||
crossOrigin: {
|
||||
type: Boolean,
|
||||
@ -174,6 +161,7 @@ defineExpose({
|
||||
overflow: hidden;
|
||||
background-color: v-bind(backgroundColor);
|
||||
}
|
||||
|
||||
.waterfall-item {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
@ -189,18 +177,22 @@ defineExpose({
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.fadeIn {
|
||||
-webkit-animation-name: fadeIn;
|
||||
animation-name: fadeIn;
|
||||
|
||||
@ -9,7 +9,6 @@ import router from "./router";
|
||||
// 自定义主题配置 - 设置主色和二级色\
|
||||
import "vfonts/FiraCode.css";
|
||||
import Tag from 'vue-devui/tag';
|
||||
import { PerfectScrollbarPlugin } from "vue3-perfect-scrollbar";
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(Tag)
|
||||
@ -21,5 +20,4 @@ for (const key in icon) {
|
||||
// console.log(key, icon[key]);
|
||||
app.component("icon-" + key, icon[key] as any);
|
||||
}
|
||||
app.use(PerfectScrollbarPlugin);
|
||||
app.mount("#app");
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
<template>
|
||||
<!-- <PerfectScrollbar ref="scrollbar" @ps-scroll-y="handleScroll"> -->
|
||||
<div ref="myCon" class="gallery-page py-5 px-[10%]">
|
||||
<!-- <d-search class="mt-0 mb-8 w-2/3 mx-auto rounded-full" v-model="kw" is-keyup-search :delay="1000"
|
||||
@search="onSearch"></d-search> -->
|
||||
<n-input class="mb-8 !w-[90%] ml-[5%]" size="large" v-model:value="kw" @keyup.enter="onSearch" placeholder="请输入">
|
||||
<div ref="myCon" class="py-5 px-[10%]">
|
||||
<n-input class="my-4 !w-[90%] ml-[5%]" round size="large" v-model:value="kw" @keyup.enter="onSearch" placeholder="请输入关键字">
|
||||
<template #suffix>
|
||||
<n-icon size="large">
|
||||
<icon-search />
|
||||
@ -12,7 +10,7 @@
|
||||
</n-input>
|
||||
|
||||
<!-- <div v-infinite-scroll="loadMore"> -->
|
||||
<Waterfall ref="waterfall" :list="fileList" :width="cwidth" :gutter="gutter" :columns="column" img-selector="url"
|
||||
<Waterfall ref="waterfall" :list="fileList" :gutter="gutter" :columns="column" img-selector="url"
|
||||
animation-effect="fadeIn" :animation-duration="1000" :animation-delay="300" backgroundColor="transparent"> >
|
||||
<template #item="{ item }">
|
||||
<div
|
||||
@ -24,11 +22,11 @@
|
||||
class="hidden truncate group-hover:block absolute rounded-md z-10 truncate top-0 text-center w-full bg-[#00000070] text-white">
|
||||
{{ item.filename }}
|
||||
</div>
|
||||
<div
|
||||
class="absolute rounded-md flex z-10 truncate bottom-0 w-full bg-[#00000070] text-white justify-between items-center">
|
||||
<!-- <div
|
||||
class="absolute hidden rounded-md flex z-10 truncate bottom-0 w-full bg-[#00000070] text-white justify-between items-center">
|
||||
<div>由 <span class="text-[#f1d9db] font-600">{{ item.nickname }}</span> 分享</div>
|
||||
<icon-download class="cursor-pointer w-4 h-4" @click="downloadFile(item.filepath)" />
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
</Waterfall>
|
||||
@ -60,14 +58,14 @@ const pn = ref(1);
|
||||
const ps = ref(40);
|
||||
const loading = ref(false);
|
||||
const kw = ref<string>('');
|
||||
const cwidth = ref<number>(220);
|
||||
const cwidth = ref<number>(240);
|
||||
const column = ref<number>(5);
|
||||
const gutter = ref<number>(20);
|
||||
|
||||
// 计算列数
|
||||
function calculateColumns() {
|
||||
const totalWidth = window.innerWidth * 0.8; // 画廊宽度为视口宽度的80%
|
||||
const col = Math.floor(totalWidth / (cwidth.value + gutter.value));
|
||||
const col = Math.floor((totalWidth + gutter.value) / (cwidth.value + gutter.value));
|
||||
column.value = col > 0 ? col : 1;
|
||||
waterfall.value?.renderer()
|
||||
}
|
||||
@ -162,37 +160,5 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
/* 画廊页样式 */
|
||||
:deep(.devui-tabs__nav) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100% !important;
|
||||
// padding: 0 10%;
|
||||
|
||||
li {
|
||||
width: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
li a span {
|
||||
font-size: 18px !important;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.ps {
|
||||
height: calc(100vh - 65px);
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.devui-upload) {
|
||||
width: 100%;
|
||||
|
||||
&>div {
|
||||
width: 100%;
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user