跨平台应用开发进阶(十五) :uni-app 自定义 showToast并实现下载进度条

本文介绍了如何使用uni-app框架开发自定义的更新提示组件showToast。通过该组件,在检测到新版本时能够显示下载进度并进行版本更新。文章详细展示了状态管理和视图部分的实现代码,并探讨了在实现过程中可能遇到的问题及其解决方案。

一、前言

在《跨平台应用开发进阶(七) :uni-app 自定义 showToast》中讲解了利用uni-app跨平台开发框架开发多终端APP时,应用HBuilder自身提供的弹窗不满足业务需求,故开发自定义弹窗组件showToast。

二、代码实现

应用自定义 showToast在实现版本更新时,需要实现检测到APP发新版时,可以更新下载新版本,并显示下载进度条。状态管理模块实现如下:

// 开始下载任务
const downloadTask = uni.downloadFile({
		url: data.url,
		// 接口调用成功
		success: (downloadResult) => {
			uni.hideLoading();
			if (downloadResult.statusCode === 200) {
				plus.runtime.install(downloadResult.tempFilePath, {
						force: false
				}, function() {
					plus.runtime.restart();
				}, function(e) {
					console.error('install fail...');
					});
					}
				},
		// 接口调用失败
		fail: (err) => {
			uni.showToast({
				icon:'none',
				mask:true,
				title: '安装失败,请重新下载',
			});
		},
		// 接口调用结束
		complete: () => {
			downloadTask.offProgressUpdate(); //取消监听加载进度
		}
	});

	//监听下载进度
	downloadTask.onProgressUpdate(res => {
		state.percent = res.progress;
		// console.log('下载进度百分比:' + res.progress); // 下载进度百分比
		// console.log('已经下载的数据长度:' + res.totalBytesWritten); // 已经下载的数据长度,单位 Bytes
		// console.log('预期需要下载的数据总长度:' + res.totalBytesExpectedToWrite); // 预期需要下载的数据总长度,单位 Bytes
	});

视图部分实现如下:

<view v-if="downLineShow" style="width: 80%; height: 30rpx;">
	<text>新版本下载中,请稍等</text>
	<progress :percent="percent" font-size='24rpx' border-radius='44rpx' activeColor='#D52424' show-info stroke-width="10"></progress>
</view>

三、延伸阅读

在应用过程中,会发现downloadTask.onProgressUpdate不起作用,此时就需要去检查后台接口content-length这个参数是否加到response header中了?若没有加此参数,就不会触发下载进度监听方法。

四、拓展阅读

hello uni-app

progress | uni-app官网

<template> <view class="evidence-card-container"> <!-- 全局进度条 --> <view v-if="showProgress" class="progress-container"> <progress :percent="progress" show-info stroke-width="3" /> </view> <view class="card-row"> <view class="card-item" v-for="(item, index) in evidenceList.slice(0, 3)" :key="index" @click="handleClick(item)"> <image v-if="item.icon" class="item-icon" :src="item.icon" mode="aspectFit"></image> <text class="item-text">{{ item.text }}</text> </view> </view> <view class="card-row"> <view class="card-item" v-for="(item, index) in evidenceList.slice(3, 6)" :key="index + 3" @click="handleClick(item)"> <image v-if="item.icon" class="item-icon" :src="item.icon" mode="aspectFit"></image> <text class="item-text">{{ item.text }}</text> </view> </view> </view> </template> <script setup lang="ts"> import { ref } from 'vue'; // 定义单个卡片数据结构 interface EvidenceItem { text: string; icon?: string; // 图标地址,可选 action?: () => void; // 点击回调,可选 } // 合所有取证选项到一个列表 const evidenceList = ref<EvidenceItem[]>([ { text: '拍照取证', icon: '/static/home/ObtainEvidence/takepictures.svg' }, { text: '录像取证', icon: '/static/home/ObtainEvidence/picturerecording.svg' }, { text: '录音取证', icon: '/static/home/ObtainEvidence/soundrecording.svg' }, { text: '网页取证', icon: '/static/home/ObtainEvidence/Webpage.svg' }, { text: '录屏取证', icon: '/static/home/ObtainEvidence/screencap.svg' }, { text: '委托取证', icon: '/static/home/ObtainEvidence/commission.svg' } ]); const showProgress = ref(false); // 控制进度条是否显示 const progress = ref(0); // 上传进度(0-100) const isUploading = ref(false); // 标记是否正在上传(避免重复上传) // 录像取证 const uploadVideo = (filePath: string, duration: number, size: number) => { // 1. 处理文件名 const originalFileName = filePath.substring(filePath.lastIndexOf('/') + 1); const fileExtension = originalFileName.includes('.') ? originalFileName.split('.').pop() : 'mp4'; const timestamp = new Date().getTime(); const newFileName = `video_evidence_${timestamp}.${fileExtension}`; // 2. 显示进度 showProgress.value = true; progress.value = 0; isUploading.value = true; // 3. 显示加载提示 let loadingVisible = false; // 标记loading是否显示 const showLoading = (title: string) => { if (!loadingVisible) { uni.showLoading({ title, mask: true }); loadingVisible = true; } else { // uni.setLoadingText(title); // 已有loading时,只更新文本 } }; showLoading('准备上传...'); // 4. 创建上传任务 const uploadTask = uni.uploadFile({ url: 'http://192.168.1.80:1592/api/upload', filePath: filePath, name: 'file', formData: { originalFileName: originalFileName, customFileName: newFileName, duration: duration, size: size, type: 'video', uploadTime: new Date().toISOString() }, header: { // 'Authorization': 'Bearer your-token-here' // 如果需要身份验证 }, timeout: 300000, // 5分钟超时 success: (uploadRes) => { console.log('上传成功,原始响应:', uploadRes.data); try { const data = JSON.parse(uploadRes.data); if (data.timestampTime) { if (loadingVisible) { uni.hideLoading(); loadingVisible = false; } // 显示成功提示 uni.showToast({ title: '录像取证成功', icon: 'success', duration: 3000 }); console.log('传递给后端成功:', data); uni.setStorage({ key: `timestamp_${timestamp}`, data: data }); } else { throw new Error('传递给后端失败'); } } catch (e) { console.error('解析响应失败', e); if (loadingVisible) { uni.hideLoading(); loadingVisible = false; } // 显示错误提示 uni.showToast({ title: e.message || '处理响应失败', icon: 'none', duration: 3000 }); } }, fail: (err) => { console.error('上传失败:', err); if (loadingVisible) { uni.hideLoading(); loadingVisible = false; } // 显示失败提示 uni.showToast({ title: `上传失败: ${err.errMsg || '网络错误'}`, icon: 'none', duration: 3000 }); }, complete: () => { // 确保loading被关闭 if (loadingVisible) { uni.hideLoading(); loadingVisible = false; } showProgress.value = false; isUploading.value = false; } }); // 5. 监听上传进度 uploadTask.onProgressUpdate((res) => { console.log('上传进度:', res.progress); progress.value = res.progress; }); }; // 点击事件处理 const handleClick = (item: EvidenceItem) => { console.log(`点击了:${item.text}`); if (item.action) { item.action(); // 如果有自定义回调,优先执行 return; } // 默认处理逻辑 switch (item.text) { case '拍照取证': uni.chooseImage({ count: 1, sizeType: ['original', 'compressed'], sourceType: ['camera'], success: async (res) => { console.log('拍照结果:', res.tempFilePaths); const tempFilePath = res.tempFilePaths[0]; const originalFileName = tempFilePath.substring(tempFilePath.lastIndexOf('/') + 1); const fileExtension = originalFileName.includes('.') ? originalFileName.split('.').pop() : 'jpg'; const timestamp = new Date().getTime(); const newFileName = `evidence_${timestamp}.${fileExtension}`; uni.uploadFile({ url: 'http://192.168.1.80:1592/api/upload', filePath: tempFilePath, name: 'file', type: 'image', formData: { originalFileName: originalFileName, customFileName: newFileName, }, success: (uploadFileRes) => { console.log("上传至后端成功",uploadFileRes.data) }, fail: (res) => { console.log("上传至后端失败"+res.errMsg) } }); console.log("图片上传至本地成功") }, fail: (err) => { console.error('拍照失败:', err); uni.showToast({ title: '拍照失败', icon: 'none' }); } }); break; case '录像取证': uni.chooseVideo({ sourceType: ['camera'], compressed: true, maxDuration: 180, camera: 'back', success: (res) => { console.log('录像结果:', res.tempFilePath); if (!res.tempFilePath) { uni.showToast({ title: '未获取到视频文件', icon: 'none' }); return; } // 检查视频大小 (限制为100MB) if (res.size > 100 * 1024 * 1024) { uni.showToast({ title: '视频大小不能超过100MB', icon: 'none' }); return; } // 准备上传 uploadVideo(res.tempFilePath, res.duration, res.size); }, fail: (err) => { console.error('录像失败:', err); uni.showToast({ title: '录像失败', icon: 'none' }); } }); break; case '录音取证': uni.showToast({ title: '录音取证功能开发中', icon: 'none' }); break; case '网页取证': uni.navigateTo({ url: '/pages/home/components/Webforensics' }); break; case '录屏取证': // uni.showToast({ // title: '录屏取证功能开发中', // icon: 'none' // }); uni.navigateTo({ url: '/pages/home/components/Screenrecord' }); break; case '委托取证': uni.showToast({ title: '请联系客服', icon: 'none' }); break; default: console.warn('未知的取证类型:', item.text); } }; </script> <style scoped> .evidence-card-container { } .card-row { display: flex; flex-direction: row; justify-content: space-around; margin-bottom: 30rpx; } .card-row:last-child { margin-bottom: 0; } .card-item { width: 30%; background-color: #ffffff; display: flex; flex-direction: column; align-items: center; border-radius: 16rpx; text-align: center; margin-right: 20rpx; padding: 20rpx 10rpx; box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1); transition: all 0.3s ease; } .card-item:last-child { margin-right: 0; } /* .card-item:hover { transform: translateY(-3rpx); box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.15); } */ .item-icon { width: 80rpx; height: 80rpx; margin-bottom: 10rpx; } .item-text { font-size: 24rpx; color: #333; line-height: 1.4; } </style> 根据这个代码修改 拍照取证照片传给后端成功 怎样接收后端返回的pdf文件预览
08-15
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

No Silver Bullet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值