557 lines
13 KiB
Vue
557 lines
13 KiB
Vue
<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>
|
||
|