uni-app 开发微信小程序,实现图片预览和保存

1.使用 uni.previewImage() 预览图片

1.1 图片列表

1.2 预览

1.2.1 样式无法调整

1.2.2 微信小程序不支持预览本地文件路径图片(图片上传到小程序的临时文件存储或云服务存储)

1.3 无法绑定 @longpress="saveImage(item)" 长按保存图片事件

1.4 前端代码

html 复制代码
<template>
	<view class="container">
		<view class="tabs">
			<uni-segmented-control
				:current="current"
				:values="tabList"
				@clickItem="onClickItem"
				styleType="button"
				activeColor="#27BA9B"
			/>
		</view>
		
		<view class="content">
			<view v-show="current === 0">
				<view class="video">
					<video
						style="width: 100%;"
						src="https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4"
						enable-play-gesture
						v-for="item in 3"
					/>
				</view>
			</view>
			
			<view v-show="current === 1">
				<scroll-view>
						<view class="image-box" v-for="(item,index) in images">
							<view class="navigator" >
                                <!-- @tap:点击事件, @longpress:长按事件 -->
								<image class="image" :src="item" @tap="previewImage(index)" @longpress="saveImage(item)" />
							</view>
						</view>
				</scroll-view>
			</view>
			
			<view v-show="current === 2">
				<scroll-view>
					<uni-card v-for="item in 10">
						<text>这是一个基础卡片示例,内容较少,此示例展示了一个没有任何属性不带阴影的卡片。</text>
					</uni-card>
				</scroll-view>
			</view>
		</view>
	</view>
</template>

<script setup>
	import { ref,computed } from 'vue'
	
	const tabList=['视频', '图片', '文案']
	
	// 选中tab的下标
	const current = ref(0)
	
	// 选中的图片index
	const activeIndex = ref(0)

	// 切换tab
	const onClickItem= (e)=>{
		current.value = e.currentIndex
	}

	// 图片列表
	// 微信小程序 uni.previewImage() 不支持使用本地文件路径预览图片,可上传到小程序的临时文件存储或云服务存储
	const images = ref([
		"https://ww2.sinaimg.cn/mw690/008a4fzDgy1hrc5rdztg7j30zk24ykfc.jpg", // 云服务存储
		"https://ww4.sinaimg.cn/mw690/005QiJkMgy1hrpsfxno4rj30zu25odq0.jpg", // 云服务存储
		"https://wx1.sinaimg.cn/mw690/005UJ76vgy1hrh4zt0k1ij30v91votw9.jpg", // 云服务存储
		"https://wx1.sinaimg.cn/mw690/60ed0cf7ly1hs8msnz6e6j20zu25o16d.jpg", // 云服务存储
		"/static/images/beauty1.jpg", // 本地文件存储
	])
	
	// uni.previewImage() 图片预览
	const previewImage= (index)=>{
		uni.previewImage({
			urls: images.value,
			current: images.value[index], // 当前显示图片的链接
		});
	}
	
	// 保存图片
	const saveImage= (url)=> {
		// 确认框
		uni.showModal({
			content: '下载该图片?',
			confirmColor: '#27BA9B',
			success: async(res) => {
				if (res.confirm) {
					try {
						let filePath;
						if (url.startsWith('/')) {
							filePath = url; // 本地图片路径,可直接保存
						} else {
							const result = await uni.downloadFile({ url }); // 云服务图片地址,要先下载
							filePath = result.tempFilePath;  // 本地临时存储路径
						}
						await uni.saveImageToPhotosAlbum({ filePath });
						uni.showToast({ title: '保存成功', icon: 'success', duration: 2000 });
					} catch (err) {
						console.error('保存失败:', err);
					}
				}
			},
		})
	}
</script>

<style lang="less" scoped>
.container{
	display: flex;
	flex-direction: column;
	height: 100vh;
	background-color: #fff;
	
	.tabs{
		padding: 10px;
	}
	
	.content{
		flex: 1;
		overflow-y: auto;
		
		.video{
			margin: 10px;
		}

		.image-box{
			display: inline-flex; // 使用 flex 布局,并且作为行内元素
			margin: 0 5px;
			width: 30%;
			
			.navigator{
				display: flex;
				width: 100%;
			}
		}
	}
}
</style>

2.使用自定义组件预览图片

2.1 图片列表

2.2 预览(可预览云服务存储和本地存储的图片)

2.3 长按保存

2.4 前端代码

2.4.1 自定义预览组件<Preview />

html 复制代码
<!-- 图片预览组件 -->
<template>
  <view class="container">
    <view class="fullscreen" >
      <swiper class="fullscreen-swiper" :current="activeIndexValue" @change="handleSwiperChange" circular>
        <swiper-item class="swiper-item" v-for="(item, index) in imageListValue" :key="index">
          <image :src="item" @longpress="saveImage(item)" mode="scaleToFill" style="width: 100vw; height: 100vh;"  />
        </swiper-item>
      </swiper>
			<view class="number">{{ activeIndexValue+1 }}/{{ imageListValue.length }}</view>
      <button class="btn" type="default" @tap="emit('close')">退出全屏</button>
    </view>
  </view>
</template>

<script setup>
	import { ref, onMounted } from 'vue';
	import { onLoad } from '@dcloudio/uni-app'
	
	// 子调父,退出预览模式
	const emit = defineEmits()

	// 获取父组件的参数,activeIndex:选中的索引,imageList:图片url列表
	let query = defineProps(["activeIndex","imageList"])
	let activeIndexValue = ref(query.activeIndex)
	let imageListValue = ref(query.imageList)
	
	// 要保存图片的url
	const url = ref('');

	// 获取屏幕边界到安全区域距离
	const { safeAreaInsets } = uni.getSystemInfoSync()

	// 处理 swiper change 事件
	const handleSwiperChange = (event) => {
		activeIndexValue.value = event.detail.current;
		url.value = imageListValue.value[activeIndexValue.value];
	};

	// 保存图片
	const saveImage= (url)=> {
		// 确认框
		uni.showModal({
			content: '下载该图片?',
			confirmColor: '#27BA9B',
			success: async(res) => {
				if (res.confirm) {
					try {
						let filePath;
						if (url.startsWith('/')) {
							filePath = url; // 本地图片路径,可直接保存
						} else {
							const result = await uni.downloadFile({ url }); // 云服务图片地址,要先下载
							filePath = result.tempFilePath;  // 本地临时存储路径
						}
						await uni.saveImageToPhotosAlbum({ filePath });
						uni.showToast({ title: '保存成功', icon: 'success', duration: 2000 });
					} catch (err) {
						console.error('保存失败:', err);
					}
				}
			},
		})
	}
</script>

<style lang="less" scoped>
.container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  background-color: #f4f4f4;
}

.swiper {
  height: 100%;
  width: 100%;
  border: 1px solid #ccc;
  overflow: hidden;
}

.swiper-item {
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

.fullscreen {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
  background-color: rgba(0, 0, 0, 0.8);
}

.fullscreen-swiper {
  width: 100%;
  height: 100%;
}

.number{
	position: absolute;
	top: 20px;
	left: 50%;
	transform: translateX(-50%);
	color: #fff;
	background-color: rgba(0, 0, 0, 0.2);
	padding: 5px 10px;
	border-radius: 20px;
}

.btn {
  position: absolute;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  color: #ccc;
  background-color: rgba(0, 0, 0, 0.2);
}
</style>

2.4.2 使用自定义预览组件<Preview />

html 复制代码
<template>
	<view class="container">
		<view class="tabs">
			<uni-segmented-control
				:current="current"
				:values="tabList"
				@clickItem="onClickItem"
				styleType="button"
				activeColor="#27BA9B"
			/>
		</view>
		
		<view class="content">
			<view v-show="current === 0">
				<view class="video">
					<video
						style="width: 100%;"
						src="https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4"
						enable-play-gesture
						v-for="item in 3"
					/>
				</view>
			</view>
			
			<view v-show="current === 1">
				<scroll-view>
						<view class="image-box" v-for="(item,index) in images">
							<!-- @tap:点击事件, @longpress:长按事件 -->
							<view class="navigator" @tap="preview(index)" >
								<image class="image" :src="item" @longpress="saveImage(item)" />
							</view>
						</view>
				</scroll-view>
			</view>
			
			<view v-show="current === 2">
				<scroll-view>
					<uni-card v-for="item in 10">
						<text>这是一个基础卡片示例,内容较少,此示例展示了一个没有任何属性不带阴影的卡片。</text>
					</uni-card>
				</scroll-view>
			</view>
		</view>
	</view>

	<!-- 自定义预览组件 -->
	<Preview class="container" v-if="previewFlag" :activeIndex="activeIndex" :imageList="images" @close="close" />
</template>

<script setup>
	import { ref,computed } from 'vue'
	
	const tabList=['视频', '图片', '文案']
	
	// 选中tab的下标
	const current = ref(0)
	
	// 是否是预览模式
	let previewFlag = ref(false);
	
	// 选中的图片index
	const activeIndex = ref(0)
	
	// 子组件调用父组件的关闭预览
	const close = ()=>{
		previewFlag.value = false
	}
	
	// 点击开启预览模式
	const preview= (index)=>{
		previewFlag.value = true
		activeIndex.value = index
	}
	
	// 切换tab
	const onClickItem= (e)=>{
		current.value = e.currentIndex
	}

	// 图片列表
	// 微信小程序 uni.previewImage() 不支持使用本地文件路径预览图片,可上传到小程序的临时文件存储或云服务存储
	const images = ref([
		"https://ww2.sinaimg.cn/mw690/008a4fzDgy1hrc5rdztg7j30zk24ykfc.jpg", // 云服务存储
		"https://ww4.sinaimg.cn/mw690/005QiJkMgy1hrpsfxno4rj30zu25odq0.jpg", // 云服务存储
		"https://wx1.sinaimg.cn/mw690/005UJ76vgy1hrh4zt0k1ij30v91votw9.jpg", // 云服务存储
		"https://wx1.sinaimg.cn/mw690/60ed0cf7ly1hs8msnz6e6j20zu25o16d.jpg", // 云服务存储
		"/static/images/beauty1.jpg", // 本地文件存储
	])
	
	// 保存图片
	const saveImage= (url)=> {
		// 确认框
		uni.showModal({
			content: '下载该图片?',
			confirmColor: '#27BA9B',
			success: async(res) => {
				if (res.confirm) {
					try {
						let filePath;
						if (url.startsWith('/')) {
							filePath = url; // 本地图片路径,可直接保存
						} else {
							const result = await uni.downloadFile({ url }); // 云服务图片地址,要先下载
							filePath = result.tempFilePath;  // 本地临时存储路径
						}
						await uni.saveImageToPhotosAlbum({ filePath });
						uni.showToast({ title: '保存成功', icon: 'success', duration: 2000 });
					} catch (err) {
						console.error('保存失败:', err);
					}
				}
			},
		})
	}
</script>

<style lang="less" scoped>
.container{
	display: flex;
	flex-direction: column;
	height: 100vh;
	background-color: #fff;
	
	.tabs{
		padding: 10px;
	}
	
	.content{
		flex: 1;
		overflow-y: auto;
		
		.video{
			margin: 10px;
		}

		.image-box{
			display: inline-flex; // 使用 flex 布局,并且作为行内元素
			margin: 0 5px;
			width: 30%;
			
			.navigator{
				display: flex;
				width: 100%;
			}
		}
	}
}
</style>
相关推荐
剪刀石头布啊1 小时前
微信小程序出现奇怪的渲染错误 framework inner error
微信小程序
x原力觉醒3 小时前
uniapp跨域问题,在开发环境中配置
javascript·vue.js·uni-app
小威编程4 小时前
uni-app应用级生命周期和页面级生命周期
前端·vue.js·uni-app
江-月*夜4 小时前
uniapp vuex 搭建
android·javascript·uni-app
圈圈的熊4 小时前
uniapp 使用 websocket
uni-app
小曲曲5 小时前
微信小程序时间弹窗——年月日时分
微信小程序·小程序
计算机学姐9 小时前
基于uniapp微信小程序的旅游系统
vue.js·spring boot·mysql·算法·微信小程序·uni-app·旅游
计算机学姐9 小时前
基于uniapp微信小程序的宠物救助宠物领养系统
vue.js·spring boot·mysql·微信小程序·小程序·uni-app·宠物
大牛哥哥9 小时前
uni-app @click.stop @click.stop.native均不生效
javascript·vue.js·uni-app
赵锦川10 小时前
微信小程序 uniapp 腾讯地图的调用
微信小程序·小程序·uni-app