Files
cunkebao_v3/Store_vue/components/UpdateDialog.vue

557 lines
13 KiB
Vue
Raw Normal View History

2025-11-03 14:07:15 +08:00
<template>
<view class="update-dialog-mask" v-if="show" @tap.stop="handleMaskClick">
<view class="update-dialog-container" @tap.stop>
<!-- 火箭图标 -->
<view class="rocket-container">
<view class="rocket-wrapper">
<!-- 火箭 SVG 图片 -->
<image class="rocket-svg" :src="rocketBase64" mode="aspectFit"></image>
<!-- 火焰效果 -->
<view class="flame-container">
<view class="flame flame-1"></view>
<view class="flame flame-2"></view>
<view class="flame flame-3"></view>
</view>
<!-- 星星装饰 -->
<view class="star star-1"></view>
<view class="star star-2"></view>
<view class="star star-3"></view>
<view class="star star-4"></view>
</view>
</view>
<!-- 内容区域 -->
<view class="dialog-content">
<!-- 强制更新提示 -->
<view class="force-notice" v-if="forceUpdate">
<text class="force-notice-icon"></text>
<text class="force-notice-text">本次为重要更新需要立即升级</text>
</view>
<!-- 更新内容 -->
<view class="update-content">
<text class="update-item" v-for="(item, index) in updateList" :key="index">{{ index + 1 }}.{{ item }}</text>
</view>
<!-- 下载进度 -->
<view class="progress-container" v-if="downloading">
<view class="progress-bar">
<view class="progress-fill" :style="{width: downloadProgress + '%'}"></view>
</view>
<text class="progress-text">{{ downloadProgress }}%</text>
</view>
<!-- 按钮 -->
<view class="button-container" v-if="!downloading">
<button class="update-button" @tap="handleUpdate">即刻升级</button>
</view>
<!-- 下载中按钮 -->
<view class="button-container" v-else>
<button class="update-button downloading">下载中...</button>
</view>
</view>
<!-- 关闭按钮 -->
<view class="close-button" @tap="handleClose" v-if="!forceUpdate && !downloading">
<text class="close-icon">×</text>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'UpdateDialog',
props: {
show: {
type: Boolean,
default: false
},
version: {
type: String,
default: ''
},
updateContent: {
type: String,
default: ''
},
downloadUrl: {
type: String,
default: ''
},
forceUpdate: {
type: Boolean,
default: false
}
},
data() {
return {
downloading: false,
downloadProgress: 0,
downloadTask: null,
// 火箭 SVG 的 base64 图片
rocketBase64: 'data:image/svg+xml;base64,PHN2ZyB0PSIxNzYxODA0ODYyMzIwIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjIwNDAiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNMzk1LjI2NCAzMDQuMTI4Yy03MC42NTYgOTIuMTYtMTQ1LjQwOCAxOTQuNTYtMTg5LjQ0IDMwNC4xMjgtNS4xMiAxMi4yODggNi4xNDQgMjMuNTUyIDE4LjQzMiAyMC40OEwzNTguNCA1OTAuODQ4TTYyOC43MzYgMzA0LjEyOGM3MC42NTYgOTIuMTYgMTQ1LjQwOCAxOTQuNTYgMTg5LjQ0IDMwNC4xMjggNS4xMiAxMi4yODgtNi4xNDQgMjMuNTUyLTE4LjQzMiAyMC40OEw2NjUuNiA1OTAuODQ4IiBmaWxsPSIjRjc5ODM5IiBwLWlkPSIyMDQxIj48L3BhdGg+PHBhdGggZD0iTTY3Ni44NjQgNzExLjY4SDM0NS4wODhDMzE4LjQ2NCA2MjQuNjQgMzEyLjMyIDUzMi40OCAzMzEuNzc2IDQ0My4zOTJjMjIuNTI4LTEwMS4zNzYgNzAuNjU2LTE5Ny42MzIgMTQwLjI4OC0yNzcuNTA0bDcuMTY4LTguMTkyYzE2LjM4NC0xOS40NTYgNDYuMDgtMTkuNDU2IDYyLjQ2NCAwbDcuMTY4IDguMTkyYzcwLjY1NiA3OS44NzIgMTE3Ljc2IDE3Ni4xMjggMTQwLjI4OCAyNzcuNTA0IDIwLjQ4IDg5LjA4OCAxNC4zMzYgMTgxLjI0OC0xMi4yODggMjY4LjI4OHoiIGZpbGw9IiMwMDRGRkYiIHAtaWQ9IjIwNDIiPjwvcGF0aD48cGF0aCBkPSJNNDY3Ljk2OCA2NzUuODRjLTUxLjIgMC05NS4yMzItMzcuODg4LTEwMi40LTg4LjA2NC04LjE5Mi02MC40MTYtNi4xNDQtMTIwLjgzMiA2LjE0NC0xODAuMjI0IDIxLjUwNC05NS4yMzIgNjQuNTEyLTE4NS4zNDQgMTI2Ljk3Ni0yNjIuMTQ0LTguMTkyIDIuMDQ4LTE1LjM2IDYuMTQ0LTIwLjQ4IDEyLjI4OGwtNy4xNjggOC4xOTJDNDAyLjQzMiAyNDUuNzYgMzU0LjMwNCAzNDAuOTkyIDMzMS43NzYgNDQzLjM5MiAzMTIuMzIgNTMyLjQ4IDMxOC40NjQgNjI0LjY0IDM0NS4wODggNzExLjY4aDMzMS43NzZjNC4wOTYtMTIuMjg4IDcuMTY4LTIzLjU1MiAxMC4yNC0zNS44NEg0NjcuOTY4eiIgZmlsbD0iIzFENkZGRiIgcC1pZD0iMjA0MyI+PC9wYXRoPjxwYXRoIGQ9Ik0zODEuOTUyIDcyMS45MmgyMzYuNTQ0Vjc3OC4yNEgzODEuOTUyeiIgZmlsbD0iIzAwNEZGRiIgcC1pZD0iMjA0NCI+PC9wYXRoPjxwYXRoIGQ9Ik01MTQuNjk5Mjc2MjUgNDc0LjA2MzEyNjMxSDUwOC42MTAyMTEyM2wzLjA0NDUzMjUxIDIuODg3NTk3ODV6IiBmaWxsPSIjZmZmZmZmIiBwLWlkPSIyMDQ1Ij48L3BhdGg+PHBhdGggZD0iTTQzMC4wOCA0MjcuMDA4YTgwLjg5NiA3OS44NzIgMCAxIDAgMTYxLjc5MiAwIDgwLjg5NiA3OS44NzIgMCAxIDAgLTE2MS43OTIgMFoiIGZpbGw9IiNFOUYzRkIiIHAtaWQ9IjIwNDYiPjwvcGF0aD48L3N2Zz4='
}
},
computed: {
updateList() {
if (!this.updateContent) {
return ['修复已知问题', '优化用户体验', '提升系统稳定性'];
}
// 将更新内容按换行或分号分割
return this.updateContent.split(/[\n;]/).filter(item => item.trim());
}
},
methods: {
handleMaskClick() {
if (!this.forceUpdate && !this.downloading) {
this.handleClose();
}
},
handleClose() {
if (this.forceUpdate || this.downloading) {
return;
}
this.$emit('close');
},
handleUpdate() {
// #ifdef APP-PLUS
if (this.downloading) {
return;
}
if (!this.downloadUrl) {
uni.showToast({
title: '下载地址无效',
icon: 'none'
});
return;
}
this.downloading = true;
this.downloadProgress = 0;
// 创建下载任务
const downloadTask = uni.downloadFile({
url: this.downloadUrl.trim(),
success: (res) => {
if (res.statusCode === 200) {
console.log('下载成功,文件路径:', res.tempFilePath);
// 下载完成,安装应用
this.installApp(res.tempFilePath);
} else {
console.error('下载失败,状态码:', res.statusCode);
uni.showToast({
title: '下载失败',
icon: 'none'
});
this.downloading = false;
}
},
fail: (err) => {
console.error('下载失败:', err);
uni.showToast({
title: '下载失败,请稍后重试',
icon: 'none'
});
this.downloading = false;
}
});
// 监听下载进度
downloadTask.onProgressUpdate((res) => {
this.downloadProgress = res.progress;
console.log('下载进度:', res.progress + '%');
});
this.downloadTask = downloadTask;
// #endif
},
installApp(filePath) {
// #ifdef APP-PLUS
console.log('开始安装应用:', filePath);
plus.runtime.install(
filePath,
{
force: false
},
() => {
console.log('安装成功');
uni.showToast({
title: '安装成功,请重启应用',
icon: 'success',
duration: 2000
});
// 安装成功后关闭弹窗
setTimeout(() => {
this.downloading = false;
this.downloadProgress = 0;
this.$emit('close');
// 如果是强制更新,重启应用
if (this.forceUpdate) {
plus.runtime.restart();
}
}, 2000);
},
(error) => {
console.error('安装失败:', error);
uni.showToast({
title: '安装失败',
icon: 'none'
});
this.downloading = false;
}
);
// #endif
}
},
beforeDestroy() {
// 组件销毁时,取消下载任务
if (this.downloadTask) {
this.downloadTask.abort();
}
}
}
</script>
<style lang="scss" scoped>
.update-dialog-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 99999;
}
.update-dialog-container {
width: 580rpx;
background: linear-gradient(180deg, #4A9FF5 0%, #2E7FD9 100%);
border-radius: 32rpx;
position: relative;
overflow: visible;
box-shadow: 0 20rpx 60rpx rgba(0, 0, 0, 0.3);
}
.rocket-container {
position: absolute;
top: -120rpx;
left: 50%;
transform: translateX(-50%);
width: 200rpx;
height: 200rpx;
display: flex;
align-items: center;
justify-content: center;
}
.rocket-wrapper {
position: relative;
width: 100%;
height: 100%;
animation: rocketFloat 2s ease-in-out infinite;
}
/* 火箭 SVG 图片 */
.rocket-svg {
position: absolute;
top: 10rpx;
left: 50%;
transform: translateX(-50%);
width: 160rpx;
height: 160rpx;
z-index: 10;
}
/* 火焰容器 */
.flame-container {
position: absolute;
bottom: 10rpx;
left: 50%;
transform: translateX(-50%);
width: 60rpx;
height: 80rpx;
display: flex;
flex-direction: column;
align-items: center;
z-index: 5;
}
/* 火焰效果 */
.flame {
position: absolute;
border-radius: 50%;
animation: flameFlicker 0.3s ease-in-out infinite alternate;
}
.flame-1 {
width: 40rpx;
height: 30rpx;
background: radial-gradient(ellipse at center, #FCD34D 0%, #FBBF24 50%, transparent 80%);
top: 0;
animation-delay: 0s;
}
.flame-2 {
width: 30rpx;
height: 40rpx;
background: radial-gradient(ellipse at center, #FBBF24 0%, #F59E0B 50%, transparent 80%);
top: 15rpx;
animation-delay: 0.1s;
}
.flame-3 {
width: 20rpx;
height: 35rpx;
background: radial-gradient(ellipse at center, #F59E0B 0%, #EF4444 50%, transparent 80%);
top: 30rpx;
animation-delay: 0.2s;
}
/* 星星装饰 */
.star {
position: absolute;
width: 8rpx;
height: 8rpx;
background: #FCD34D;
border-radius: 50%;
box-shadow: 0 0 10rpx #FCD34D;
}
.star-1 {
top: 30rpx;
left: 20rpx;
animation: starTwinkle 2s ease-in-out infinite;
animation-delay: 0s;
}
.star-2 {
top: 50rpx;
right: 15rpx;
animation: starTwinkle 2s ease-in-out infinite;
animation-delay: 0.5s;
}
.star-3 {
top: 80rpx;
left: 10rpx;
animation: starTwinkle 1.5s ease-in-out infinite;
animation-delay: 1s;
}
.star-4 {
top: 100rpx;
right: 20rpx;
animation: starTwinkle 1.5s ease-in-out infinite;
animation-delay: 1.5s;
}
/* 动画定义 */
@keyframes rocketFloat {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-15rpx);
}
}
@keyframes flameFlicker {
0% {
opacity: 0.8;
transform: scaleY(1);
}
100% {
opacity: 1;
transform: scaleY(1.2);
}
}
@keyframes starTwinkle {
0%, 100% {
opacity: 0.3;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.5);
}
}
.dialog-content {
padding: 50rpx 40rpx 40rpx;
background: #FFFFFF;
border-radius: 32rpx;
margin-top: 80rpx;
}
.dialog-title {
font-size: 36rpx;
font-weight: bold;
color: #333333;
text-align: center;
margin-bottom: 40rpx;
display: flex;
flex-direction: column;
align-items: center;
}
.force-tag {
display: inline-block;
margin-top: 10rpx;
padding: 4rpx 16rpx;
background: linear-gradient(135deg, #FF6B6B 0%, #FF4757 100%);
color: #FFFFFF;
font-size: 20rpx;
border-radius: 20rpx;
font-weight: normal;
animation: tagPulse 1.5s ease-in-out infinite;
}
@keyframes tagPulse {
0%, 100% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(255, 71, 87, 0.7);
}
50% {
transform: scale(1.05);
box-shadow: 0 0 0 8rpx rgba(255, 71, 87, 0);
}
}
.force-notice {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx;
margin-bottom: 24rpx;
background: linear-gradient(135deg, #FFF5F5 0%, #FFE5E5 100%);
border-radius: 16rpx;
border: 2rpx solid #FFB8B8;
}
.force-notice-icon {
font-size: 32rpx;
margin-right: 12rpx;
}
.force-notice-text {
font-size: 26rpx;
color: #FF4757;
font-weight: 500;
}
.update-content {
background: #F8F9FA;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 40rpx;
max-height: 300rpx;
overflow-y: auto;
}
.update-item {
display: block;
font-size: 28rpx;
color: #666666;
line-height: 44rpx;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
}
.progress-container {
margin-bottom: 40rpx;
}
.progress-bar {
width: 100%;
height: 12rpx;
background: #E5E7EB;
border-radius: 6rpx;
overflow: hidden;
margin-bottom: 16rpx;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #4A9FF5 0%, #2E7FD9 100%);
border-radius: 6rpx;
transition: width 0.3s ease;
}
.progress-text {
display: block;
text-align: center;
font-size: 24rpx;
color: #4A9FF5;
font-weight: bold;
}
.button-container {
width: 100%;
}
.update-button {
width: 100%;
height: 88rpx;
background: linear-gradient(135deg, #4A9FF5 0%, #2E7FD9 100%);
border-radius: 44rpx;
border: none;
color: #FFFFFF;
font-size: 32rpx;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 24rpx rgba(46, 127, 217, 0.4);
&.downloading {
background: #CCCCCC;
box-shadow: none;
}
&::after {
border: none;
}
}
.close-button {
position: absolute;
bottom: -120rpx;
left: 50%;
transform: translateX(-50%);
width: 80rpx;
height: 80rpx;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 2rpx solid rgba(255, 255, 255, 0.6);
}
.close-icon {
font-size: 60rpx;
color: #FFFFFF;
line-height: 1;
font-weight: 300;
}
</style>