Files
cunkebao_v3/Store_vue/components/UpdateDialog.vue
2025-11-03 14:07:15 +08:00

557 lines
13 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>