Skip to content

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是否显示booleanfalse
images图片列表string[][]
initialIndex初始显示的图片索引number0
zIndex层级number2000
toolbar是否显示工具栏booleantrue
navbar是否显示导航栏booleantrue
title是否显示标题booleantrue
keyboard是否支持键盘操作booleantrue
loop是否循环播放booleantrue

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>

注意事项

注意

  1. 图片 URL 需要支持跨域访问
  2. 大图片可能影响加载速度
  3. 建议使用缩略图提高性能
  4. 注意图片版权问题

提示

  • 支持键盘快捷键操作
  • 可以自定义工具栏按钮
  • 支持触摸手势(移动端)
  • 使用 loop 属性启用循环播放
  • 可以通过 zIndex 调整层级

相关链接

MIT License