VbenImagePreview 图片预览
基于 Viewer.js 的图片预览组件,支持缩放、旋转、切换等功能。
基础用法
vue
<script setup lang="ts">
import { ref } from 'vue'
import { VbenImagePreview } from '@vben/common-ui'
const visible = ref(false)
const images = [
'https://example.com/image1.jpg',
'https://example.com/image2.jpg',
'https://example.com/image3.jpg'
]
const showPreview = () => {
visible.value = true
}
</script>
<template>
<div>
<a-button @click="showPreview">预览图片</a-button>
<VbenImagePreview
v-model:visible="visible"
:images="images"
/>
</div>
</template>指定初始图片
vue
<script setup lang="ts">
import { ref } from 'vue'
import { VbenImagePreview } from '@vben/common-ui'
const visible = ref(false)
const initialIndex = ref(1)
const images = [
'https://example.com/image1.jpg',
'https://example.com/image2.jpg',
'https://example.com/image3.jpg'
]
</script>
<template>
<VbenImagePreview
v-model:visible="visible"
:images="images"
:initial-index="initialIndex"
/>
</template>缩略图列表
vue
<script setup lang="ts">
import { ref } from 'vue'
import { VbenImagePreview } from '@vben/common-ui'
const visible = ref(false)
const currentIndex = ref(0)
const images = [
'https://example.com/image1.jpg',
'https://example.com/image2.jpg',
'https://example.com/image3.jpg',
'https://example.com/image4.jpg'
]
const handlePreview = (index: number) => {
currentIndex.value = index
visible.value = true
}
</script>
<template>
<div>
<div class="thumbnail-list">
<div
v-for="(image, index) in images"
:key="index"
class="thumbnail-item"
@click="handlePreview(index)"
>
<img :src="image" :alt="`图片${index + 1}`">
</div>
</div>
<VbenImagePreview
v-model:visible="visible"
:images="images"
:initial-index="currentIndex"
/>
</div>
</template>
<style scoped>
.thumbnail-list {
display: flex;
gap: 16px;
flex-wrap: wrap;
}
.thumbnail-item {
width: 120px;
height: 120px;
cursor: pointer;
border: 2px solid transparent;
border-radius: 4px;
overflow: hidden;
transition: all 0.3s;
}
.thumbnail-item:hover {
border-color: #1890ff;
transform: scale(1.05);
}
.thumbnail-item img {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>API
Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| visible | 是否显示 | boolean | false |
| images | 图片列表 | string[] | [] |
| initialIndex | 初始显示的图片索引 | number | 0 |
| zIndex | 层级 | number | 2000 |
| toolbar | 是否显示工具栏 | boolean | true |
| navbar | 是否显示导航栏 | boolean | true |
| title | 是否显示标题 | boolean | true |
| keyboard | 是否支持键盘操作 | boolean | true |
| loop | 是否循环播放 | boolean | true |
Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| update:visible | 显示状态变化时触发 | (visible: boolean) => void |
| change | 切换图片时触发 | (index: number) => void |
| close | 关闭时触发 | () => void |
Methods
| 方法名 | 说明 | 参数 |
|---|---|---|
| show | 显示预览 | (index?: number) => void |
| hide | 隐藏预览 | - |
| prev | 上一张 | - |
| next | 下一张 | - |
| zoomIn | 放大 | - |
| zoomOut | 缩小 | - |
| rotate | 旋转 | (degree: number) => void |
| reset | 重置 | - |
完整示例
vue
<script setup lang="ts">
import { ref } from 'vue'
import { VbenImagePreview } from '@vben/common-ui'
const previewRef = ref()
const visible = ref(false)
const currentIndex = ref(0)
const images = [
{
url: 'https://example.com/image1.jpg',
title: '图片 1'
},
{
url: 'https://example.com/image2.jpg',
title: '图片 2'
},
{
url: 'https://example.com/image3.jpg',
title: '图片 3'
},
{
url: 'https://example.com/image4.jpg',
title: '图片 4'
}
]
const imageUrls = images.map(img => img.url)
const handlePreview = (index: number) => {
currentIndex.value = index
visible.value = true
}
const handleChange = (index: number) => {
console.log('当前图片索引:', index)
currentIndex.value = index
}
const handlePrev = () => {
previewRef.value?.prev()
}
const handleNext = () => {
previewRef.value?.next()
}
const handleZoomIn = () => {
previewRef.value?.zoomIn()
}
const handleZoomOut = () => {
previewRef.value?.zoomOut()
}
const handleRotate = () => {
previewRef.value?.rotate(90)
}
const handleReset = () => {
previewRef.value?.reset()
}
</script>
<template>
<div class="image-gallery">
<!-- 缩略图网格 -->
<div class="thumbnail-grid">
<div
v-for="(image, index) in images"
:key="index"
class="thumbnail-card"
@click="handlePreview(index)"
>
<img :src="image.url" :alt="image.title">
<div class="thumbnail-overlay">
<span class="thumbnail-title">{{ image.title }}</span>
</div>
</div>
</div>
<!-- 自定义工具栏 -->
<div class="custom-toolbar">
<a-space>
<a-button @click="handlePrev">上一张</a-button>
<a-button @click="handleNext">下一张</a-button>
<a-button @click="handleZoomIn">放大</a-button>
<a-button @click="handleZoomOut">缩小</a-button>
<a-button @click="handleRotate">旋转</a-button>
<a-button @click="handleReset">重置</a-button>
</a-space>
</div>
<!-- 图片预览 -->
<VbenImagePreview
ref="previewRef"
v-model:visible="visible"
:images="imageUrls"
:initial-index="currentIndex"
@change="handleChange"
/>
</div>
</template>
<style scoped>
.image-gallery {
padding: 20px;
}
.thumbnail-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 16px;
margin-bottom: 20px;
}
.thumbnail-card {
position: relative;
aspect-ratio: 1;
cursor: pointer;
border-radius: 8px;
overflow: hidden;
transition: all 0.3s;
}
.thumbnail-card:hover {
transform: translateY(-4px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.thumbnail-card img {
width: 100%;
height: 100%;
object-fit: cover;
}
.thumbnail-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 12px;
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
opacity: 0;
transition: opacity 0.3s;
}
.thumbnail-card:hover .thumbnail-overlay {
opacity: 1;
}
.thumbnail-title {
color: #fff;
font-size: 14px;
}
.custom-toolbar {
padding: 16px;
background: #f5f5f5;
border-radius: 4px;
}
</style>功能特性
缩放
vue
<script setup lang="ts">
const handleZoom = (type: 'in' | 'out') => {
if (type === 'in') {
previewRef.value?.zoomIn()
} else {
previewRef.value?.zoomOut()
}
}
</script>旋转
vue
<script setup lang="ts">
const handleRotate = (degree: number) => {
previewRef.value?.rotate(degree)
}
</script>
<template>
<a-space>
<a-button @click="handleRotate(90)">顺时针旋转</a-button>
<a-button @click="handleRotate(-90)">逆时针旋转</a-button>
</a-space>
</template>切换
vue
<script setup lang="ts">
const handleSwitch = (direction: 'prev' | 'next') => {
if (direction === 'prev') {
previewRef.value?.prev()
} else {
previewRef.value?.next()
}
}
</script>键盘快捷键
| 快捷键 | 功能 |
|---|---|
| ← | 上一张 |
| → | 下一张 |
| ↑ | 放大 |
| ↓ | 缩小 |
| Ctrl + ← | 逆时针旋转 |
| Ctrl + → | 顺时针旋转 |
| Ctrl + 0 | 重置 |
| Esc | 关闭 |
自定义配置
vue
<script setup lang="ts">
import { ref } from 'vue'
import { VbenImagePreview } from '@vben/common-ui'
const visible = ref(false)
const images = ['...']
const options = {
toolbar: {
zoomIn: true,
zoomOut: true,
oneToOne: true,
reset: true,
prev: true,
play: true,
next: true,
rotateLeft: true,
rotateRight: true,
flipHorizontal: true,
flipVertical: true
},
navbar: true,
title: true,
tooltip: true,
movable: true,
zoomable: true,
rotatable: true,
scalable: true,
transition: true,
fullscreen: true,
keyboard: true,
url: 'data-source'
}
</script>
<template>
<VbenImagePreview
v-model:visible="visible"
:images="images"
:options="options"
/>
</template>与表格集成
vue
<script setup lang="ts">
import { ref } from 'vue'
import { VbenImagePreview } from '@vben/common-ui'
const visible = ref(false)
const currentImages = ref<string[]>([])
const currentIndex = ref(0)
const dataSource = [
{
id: 1,
name: '商品 1',
images: ['url1.jpg', 'url2.jpg']
},
{
id: 2,
name: '商品 2',
images: ['url3.jpg', 'url4.jpg']
}
]
const handlePreview = (images: string[], index: number = 0) => {
currentImages.value = images
currentIndex.value = index
visible.value = true
}
</script>
<template>
<div>
<a-table :data-source="dataSource">
<a-table-column title="商品名称" data-index="name" />
<a-table-column title="图片">
<template #default="{ record }">
<a-space>
<img
v-for="(img, index) in record.images"
:key="index"
:src="img"
style="width: 50px; height: 50px; cursor: pointer"
@click="handlePreview(record.images, index)"
>
</a-space>
</template>
</a-table-column>
</a-table>
<VbenImagePreview
v-model:visible="visible"
:images="currentImages"
:initial-index="currentIndex"
/>
</div>
</template>注意事项
注意
- 图片 URL 需要支持跨域访问
- 大图片可能影响加载速度
- 建议使用缩略图提高性能
- 注意图片版权问题
提示
- 支持键盘快捷键操作
- 可以自定义工具栏按钮
- 支持触摸手势(移动端)
- 使用
loop属性启用循环播放 - 可以通过
zIndex调整层级