492 lines
16 KiB
Vue
492 lines
16 KiB
Vue
<template>
|
|
<view class="data-backup">
|
|
<!-- 顶部导航栏 -->
|
|
<view class="nav-bar">
|
|
<view class="nav-back" @click="navigateBack">
|
|
<u-icon name="arrow-left" size="24" color="#606266"></u-icon>
|
|
</view>
|
|
<view class="nav-title">数据备份</view>
|
|
<view class="nav-right">
|
|
<u-button type="primary" size="small" @click="createBackup" :disabled="isBackingUp">
|
|
{{ isBackingUp ? '备份中...' : '立即备份' }}
|
|
</u-button>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 备份设置 -->
|
|
<view class="backup-settings">
|
|
<u-cell-group>
|
|
<u-cell
|
|
title="自动备份频率"
|
|
:right-text="`${autoBackupFrequency}小时`"
|
|
is-link
|
|
@click="showFrequencyModal = true"
|
|
></u-cell>
|
|
<u-cell
|
|
title="保留备份数量"
|
|
:right-text="`${maxBackups}个`"
|
|
is-link
|
|
@click="showMaxBackupsModal = true"
|
|
></u-cell>
|
|
<u-cell
|
|
title="备份存储位置"
|
|
:right-text="backupStorageLocation"
|
|
is-link
|
|
@click="selectStorageLocation"
|
|
></u-cell>
|
|
</u-cell-group>
|
|
</view>
|
|
|
|
<!-- 备份列表 -->
|
|
<view class="backup-list">
|
|
<view class="section-header">
|
|
<text class="section-title">历史备份</text>
|
|
<u-button type="text" @click="cleanOldBackups">清理旧备份</u-button>
|
|
</view>
|
|
|
|
<u-list v-if="backups.length > 0" :load-more="loadMore" @loadmore="onLoadMore">
|
|
<u-list-item v-for="backup in paginatedBackups" :key="backup.id" :border="true">
|
|
<view class="backup-item">
|
|
<view class="backup-header">
|
|
<view class="backup-name">{{ backup.name }}</view>
|
|
<view class="backup-time">{{ formatTime(backup.timestamp) }}</view>
|
|
</view>
|
|
<view class="backup-info">
|
|
<text class="backup-size">大小: {{ backup.size }}</text>
|
|
<text class="backup-type" :class="backup.type">类型: {{ backup.type === 'auto' ? '自动' : '手动' }}</text>
|
|
</view>
|
|
<view class="backup-actions">
|
|
<u-button size="mini" type="primary" @click="downloadBackup(backup.id)">下载</u-button>
|
|
<u-button size="mini" type="success" @click="restoreBackup(backup.id)">恢复</u-button>
|
|
<u-button size="mini" type="error" @click="deleteBackup(backup.id)">删除</u-button>
|
|
</view>
|
|
</view>
|
|
</u-list-item>
|
|
</u-list>
|
|
<u-empty v-else text="暂无备份记录"></u-empty>
|
|
</view>
|
|
|
|
<!-- 自动备份频率选择弹窗 -->
|
|
<u-popup v-model="showFrequencyModal" mode="bottom" :round="true" :closeable="true">
|
|
<view class="modal-content">
|
|
<view class="modal-title">选择自动备份频率</view>
|
|
<view class="picker-options">
|
|
<u-button
|
|
v-for="option in frequencyOptions"
|
|
:key="option.value"
|
|
:type="autoBackupFrequency === option.value ? 'primary' : 'default'"
|
|
style="margin-bottom: 20rpx;"
|
|
@click="selectFrequency(option.value)"
|
|
>
|
|
{{ option.label }}
|
|
</u-button>
|
|
</view>
|
|
<view class="modal-buttons">
|
|
<u-button @click="showFrequencyModal = false">取消</u-button>
|
|
</view>
|
|
</view>
|
|
</u-popup>
|
|
|
|
<!-- 保留备份数量选择弹窗 -->
|
|
<u-popup v-model="showMaxBackupsModal" mode="bottom" :round="true" :closeable="true">
|
|
<view class="modal-content">
|
|
<view class="modal-title">选择保留备份数量</view>
|
|
<view class="picker-options">
|
|
<u-button
|
|
v-for="option in maxBackupsOptions"
|
|
:key="option.value"
|
|
:type="maxBackups === option.value ? 'primary' : 'default'"
|
|
style="margin-bottom: 20rpx;"
|
|
@click="selectMaxBackups(option.value)"
|
|
>
|
|
{{ option.label }}
|
|
</u-button>
|
|
</view>
|
|
<view class="modal-buttons">
|
|
<u-button @click="showMaxBackupsModal = false">取消</u-button>
|
|
</view>
|
|
</view>
|
|
</u-popup>
|
|
|
|
<!-- 备份进度弹窗 -->
|
|
<u-popup v-model="showProgressModal" mode="center" :round="true" :closeable="false" :mask-close-able="false">
|
|
<view class="progress-modal-content">
|
|
<view class="progress-title">正在备份数据</view>
|
|
<u-progress :percentage="backupProgress" :show-text="true" bar-height="10" active></u-progress>
|
|
<view class="progress-text">{{ backupProgressText }}</view>
|
|
</view>
|
|
</u-popup>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
data() {
|
|
return {
|
|
isBackingUp: false,
|
|
backupProgress: 0,
|
|
backupProgressText: '准备备份...',
|
|
showProgressModal: false,
|
|
showFrequencyModal: false,
|
|
showMaxBackupsModal: false,
|
|
autoBackupFrequency: 24, // 默认24小时
|
|
maxBackups: 10, // 默认保留10个备份
|
|
backupStorageLocation: '本地存储',
|
|
currentPage: 1,
|
|
pageSize: 10,
|
|
loadMore: {
|
|
loading: false,
|
|
finished: false,
|
|
error: false
|
|
},
|
|
frequencyOptions: [
|
|
{ label: '6小时', value: 6 },
|
|
{ label: '12小时', value: 12 },
|
|
{ label: '24小时', value: 24 },
|
|
{ label: '48小时', value: 48 },
|
|
{ label: '72小时', value: 72 },
|
|
{ label: '关闭自动备份', value: 0 }
|
|
],
|
|
maxBackupsOptions: [
|
|
{ label: '5个', value: 5 },
|
|
{ label: '10个', value: 10 },
|
|
{ label: '20个', value: 20 },
|
|
{ label: '50个', value: 50 }
|
|
],
|
|
backups: [
|
|
{ id: 1, name: 'backup_20230801_120000', timestamp: 1690843200000, size: '12.5 MB', type: 'auto' },
|
|
{ id: 2, name: 'backup_20230802_120000', timestamp: 1690929600000, size: '13.2 MB', type: 'auto' },
|
|
{ id: 3, name: 'backup_20230803_103000', timestamp: 1691005800000, size: '14.1 MB', type: 'manual' },
|
|
{ id: 4, name: 'backup_20230804_120000', timestamp: 1691092200000, size: '14.3 MB', type: 'auto' },
|
|
{ id: 5, name: 'backup_20230805_120000', timestamp: 1691178600000, size: '14.8 MB', type: 'auto' },
|
|
{ id: 6, name: 'backup_20230806_120000', timestamp: 1691265000000, size: '15.2 MB', type: 'auto' },
|
|
{ id: 7, name: 'backup_20230807_091500', timestamp: 1691336100000, size: '15.5 MB', type: 'manual' },
|
|
{ id: 8, name: 'backup_20230808_120000', timestamp: 1691434200000, size: '16.1 MB', type: 'auto' },
|
|
{ id: 9, name: 'backup_20230809_120000', timestamp: 1691520600000, size: '16.5 MB', type: 'auto' },
|
|
{ id: 10, name: 'backup_20230810_120000', timestamp: 1691607000000, size: '17.0 MB', type: 'auto' },
|
|
{ id: 11, name: 'backup_20230811_120000', timestamp: 1691693400000, size: '17.3 MB', type: 'auto' },
|
|
{ id: 12, name: 'backup_20230812_153000', timestamp: 1691779800000, size: '17.8 MB', type: 'manual' }
|
|
]
|
|
}
|
|
},
|
|
computed: {
|
|
paginatedBackups() {
|
|
const start = (this.currentPage - 1) * this.pageSize
|
|
const end = start + this.pageSize
|
|
return this.backups.sort((a, b) => b.timestamp - a.timestamp).slice(start, end)
|
|
}
|
|
},
|
|
methods: {
|
|
navigateBack() {
|
|
uni.navigateBack()
|
|
},
|
|
formatTime(timestamp) {
|
|
const date = new Date(timestamp)
|
|
const year = date.getFullYear()
|
|
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
|
const day = date.getDate().toString().padStart(2, '0')
|
|
const hour = date.getHours().toString().padStart(2, '0')
|
|
const minute = date.getMinutes().toString().padStart(2, '0')
|
|
return `${year}-${month}-${day} ${hour}:${minute}`
|
|
},
|
|
createBackup() {
|
|
this.isBackingUp = true
|
|
this.backupProgress = 0
|
|
this.backupProgressText = '准备备份...'
|
|
this.showProgressModal = true
|
|
|
|
// 模拟备份进度
|
|
let progress = 0
|
|
const interval = setInterval(() => {
|
|
progress += 5
|
|
this.backupProgress = progress
|
|
|
|
if (progress <= 30) {
|
|
this.backupProgressText = '正在收集数据...'
|
|
} else if (progress <= 60) {
|
|
this.backupProgressText = '正在压缩数据...'
|
|
} else if (progress <= 90) {
|
|
this.backupProgressText = '正在保存备份...'
|
|
} else {
|
|
this.backupProgressText = '备份完成!'
|
|
}
|
|
|
|
if (progress >= 100) {
|
|
clearInterval(interval)
|
|
setTimeout(() => {
|
|
this.isBackingUp = false
|
|
this.showProgressModal = false
|
|
|
|
// 添加新备份到列表
|
|
const now = new Date()
|
|
const newBackup = {
|
|
id: this.backups.length + 1,
|
|
name: `backup_${now.getFullYear()}${(now.getMonth() + 1).toString().padStart(2, '0')}${now.getDate().toString().padStart(2, '0')}_${now.getHours().toString().padStart(2, '0')}${now.getMinutes().toString().padStart(2, '0')}${now.getSeconds().toString().padStart(2, '0')}`,
|
|
timestamp: now.getTime(),
|
|
size: `${(Math.random() * 5 + 15).toFixed(1)} MB`,
|
|
type: 'manual'
|
|
}
|
|
this.backups.unshift(newBackup)
|
|
this.currentPage = 1
|
|
this.updateLoadMoreStatus()
|
|
|
|
uni.showToast({ title: '备份成功', icon: 'success' })
|
|
}, 1000)
|
|
}
|
|
}, 200)
|
|
},
|
|
downloadBackup(id) {
|
|
uni.showLoading({ title: '正在下载...' })
|
|
|
|
// 模拟下载
|
|
setTimeout(() => {
|
|
uni.hideLoading()
|
|
const backup = this.backups.find(b => b.id === id)
|
|
uni.showToast({
|
|
title: `备份文件 ${backup.name} 已下载`,
|
|
icon: 'success'
|
|
})
|
|
}, 1500)
|
|
},
|
|
restoreBackup(id) {
|
|
uni.showModal({
|
|
title: '确认恢复',
|
|
content: '恢复数据将覆盖当前系统数据,是否继续?',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
uni.showLoading({ title: '正在恢复...' })
|
|
|
|
// 模拟恢复
|
|
setTimeout(() => {
|
|
uni.hideLoading()
|
|
uni.showToast({
|
|
title: '数据恢复成功',
|
|
icon: 'success'
|
|
})
|
|
}, 2000)
|
|
}
|
|
}
|
|
})
|
|
},
|
|
deleteBackup(id) {
|
|
uni.showModal({
|
|
title: '确认删除',
|
|
content: '确定要删除此备份文件吗?',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
this.backups = this.backups.filter(backup => backup.id !== id)
|
|
uni.showToast({
|
|
title: '删除成功',
|
|
icon: 'success'
|
|
})
|
|
this.updateLoadMoreStatus()
|
|
}
|
|
}
|
|
})
|
|
},
|
|
cleanOldBackups() {
|
|
uni.showModal({
|
|
title: '清理旧备份',
|
|
content: `系统将只保留最近 ${this.maxBackups} 个备份文件,是否继续?`,
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
// 模拟清理
|
|
setTimeout(() => {
|
|
const sortedBackups = this.backups.sort((a, b) => b.timestamp - a.timestamp)
|
|
this.backups = sortedBackups.slice(0, this.maxBackups)
|
|
uni.showToast({
|
|
title: '清理完成',
|
|
icon: 'success'
|
|
})
|
|
this.currentPage = 1
|
|
this.updateLoadMoreStatus()
|
|
}, 1000)
|
|
}
|
|
}
|
|
})
|
|
},
|
|
selectFrequency(value) {
|
|
this.autoBackupFrequency = value
|
|
this.showFrequencyModal = false
|
|
uni.showToast({
|
|
title: `自动备份频率已设置为${value === 0 ? '关闭' : value + '小时'}`,
|
|
icon: 'success'
|
|
})
|
|
},
|
|
selectMaxBackups(value) {
|
|
this.maxBackups = value
|
|
this.showMaxBackupsModal = false
|
|
uni.showToast({
|
|
title: `保留备份数量已设置为${value}个`,
|
|
icon: 'success'
|
|
})
|
|
},
|
|
selectStorageLocation() {
|
|
// 模拟选择存储位置
|
|
uni.showActionSheet({
|
|
itemList: ['本地存储', '云存储'],
|
|
success: (res) => {
|
|
this.backupStorageLocation = res.tapIndex === 0 ? '本地存储' : '云存储'
|
|
uni.showToast({
|
|
title: `存储位置已设置为${this.backupStorageLocation}`,
|
|
icon: 'success'
|
|
})
|
|
}
|
|
})
|
|
},
|
|
onLoadMore() {
|
|
if (this.loadMore.loading) return
|
|
|
|
this.loadMore.loading = true
|
|
|
|
// 模拟加载更多数据
|
|
setTimeout(() => {
|
|
this.currentPage++
|
|
this.loadMore.loading = false
|
|
this.updateLoadMoreStatus()
|
|
}, 1000)
|
|
},
|
|
updateLoadMoreStatus() {
|
|
const hasMore = this.paginatedBackups.length < this.backups.length
|
|
this.loadMore.finished = !hasMore
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.data-backup {
|
|
padding-bottom: 20rpx;
|
|
background-color: #f5f5f5;
|
|
|
|
.nav-bar {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
height: 88rpx;
|
|
padding: 0 30rpx;
|
|
background-color: #fff;
|
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
|
|
|
.nav-title {
|
|
font-size: 36rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
}
|
|
|
|
.backup-settings {
|
|
margin-top: 20rpx;
|
|
}
|
|
|
|
.backup-list {
|
|
margin-top: 20rpx;
|
|
background-color: #fff;
|
|
|
|
.section-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 20rpx;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
|
.section-title {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
}
|
|
|
|
.backup-item {
|
|
padding: 20rpx;
|
|
|
|
.backup-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 10rpx;
|
|
|
|
.backup-name {
|
|
font-size: 32rpx;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.backup-time {
|
|
font-size: 28rpx;
|
|
color: #999;
|
|
}
|
|
}
|
|
|
|
.backup-info {
|
|
display: flex;
|
|
gap: 30rpx;
|
|
margin-bottom: 20rpx;
|
|
|
|
.backup-size {
|
|
font-size: 28rpx;
|
|
color: #666;
|
|
}
|
|
|
|
.backup-type {
|
|
font-size: 28rpx;
|
|
}
|
|
|
|
.backup-type.auto {
|
|
color: #409eff;
|
|
}
|
|
|
|
.backup-type.manual {
|
|
color: #67c23a;
|
|
}
|
|
}
|
|
|
|
.backup-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 10rpx;
|
|
}
|
|
}
|
|
}
|
|
|
|
.modal-content {
|
|
padding: 30rpx;
|
|
|
|
.modal-title {
|
|
font-size: 36rpx;
|
|
font-weight: bold;
|
|
text-align: center;
|
|
margin-bottom: 30rpx;
|
|
color: #333;
|
|
}
|
|
|
|
.picker-options {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 15rpx;
|
|
margin-bottom: 30rpx;
|
|
}
|
|
|
|
.modal-buttons {
|
|
margin-top: 20rpx;
|
|
}
|
|
}
|
|
|
|
.progress-modal-content {
|
|
padding: 40rpx;
|
|
text-align: center;
|
|
|
|
.progress-title {
|
|
font-size: 36rpx;
|
|
font-weight: bold;
|
|
margin-bottom: 30rpx;
|
|
color: #333;
|
|
}
|
|
|
|
.progress-text {
|
|
margin-top: 20rpx;
|
|
font-size: 30rpx;
|
|
color: #666;
|
|
}
|
|
}
|
|
}
|
|
</style> |