topfans/frontend/pages/profile/cache-cleanup.vue
2026-07-29 16:03:33 +08:00

258 lines
5.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

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="cache-cleanup">
<!-- 顶部 WeChat 风格 -->
<view class="hero">
<text class="hero-label">Topfans 已用空间</text>
<text class="hero-value">{{ formatSize(info.appUsedBytes) }}</text>
<text class="hero-percent"
>占据配额 {{ info.usagePercent.toFixed(1) }}% 存储空间</text
>
<view class="progress-bar">
<view
class="progress-fill"
:style="{ width: Math.min(100, info.usagePercent) + '%' }"
></view>
</view>
<view class="chips">
<view class="chip">Topfans 已用空间</view>
<view class="chip">内存缓存空间</view>
<view class="chip">总内存空间</view>
</view>
</view>
<!-- 缓存分类 -->
<view class="section">
<text class="section-title">缓存分类</text>
<!-- 全局空状态失败时不显示避免冲突显示 -->
<view
v-if="
!loadFailed &&
info.totalBytes === 0 &&
info.categories.every((c) => c.sizeBytes <= 0)
"
class="empty"
>
<text>✓ 当前无缓存可清理</text>
</view>
<view
v-for="cat in info.categories"
:key="cat.id"
class="row"
@click="goDetail(cat.id)"
>
<view class="row-main">
<text class="row-label">{{ cat.label }}</text>
<text v-if="cat.warning" class="row-warn">⚠ 按账号分组清理</text>
</view>
<view class="row-sub">
<text v-if="cat.sizeBytes === -1" class="error">? · 加载失败</text>
<text v-else class="size"
>{{ formatSize(cat.sizeBytes) }} · {{ cat.keyCount }} 项</text
>
<text class="chevron"></text>
</view>
</view>
</view>
<!-- "其他" section -->
<view class="section others-section">
<text class="section-title">其他</text>
<view class="row others-row">
<view class="row-main">
<text class="row-label">其他</text>
<text class="row-desc"
>包含运行 Topfans 的必要数据、账号会话、其他账号的数据等</text
>
</view>
<text class="size">{{ formatSize(info.othersBytes) }}</text>
</view>
</view>
<!-- 整体加载失败状态reviewer P0 修正加点击重试 -->
<view
v-if="loadFailed && info.totalBytes === 0"
class="load-failed"
@click="load"
>
<text>加载失败点此重试</text>
</view>
</view>
</template>
<script setup>
import { ref } from "vue";
import { onPullDownRefresh, onShow } from "@dcloudio/uni-app";
import { getCacheInfo, formatSize } from "@/utils/cacheManager";
const info = ref({
appUsedBytes: 0,
quotaTotalBytes: 0,
quotaAvailableBytes: 0,
usagePercent: 0,
othersBytes: 0,
totalBytes: 0,
categories: [],
});
const loadFailed = ref(false);
async function load() {
loadFailed.value = false;
try {
const result = await Promise.race([
getCacheInfo(),
new Promise((_, rej) =>
setTimeout(() => rej(new Error("timeout")), 5000),
),
]);
info.value = result;
} catch (e) {
console.warn("[cache-cleanup] load failed:", e.message);
loadFailed.value = true;
}
}
function goDetail(id) {
const cat = info.value.categories.find((c) => c.id === id);
if (!cat || cat.sizeBytes <= 0) {
uni.showToast({ title: "该分类暂无缓存", icon: "none" });
return;
}
uni.navigateTo({ url: `/pages/profile/cache-cleanup-detail?id=${id}` });
}
// reviewer P0使用 onShow 统一触发 load进页面 + 从详情页返回时都触发)
// 不再使用 onMounted 避免重复触发
onShow(load);
onPullDownRefresh(async () => {
await load();
uni.stopPullDownRefresh();
});
</script>
<style scoped>
.cache-cleanup {
padding-bottom: 40rpx;
}
.hero {
background: #fff;
padding: 40rpx 32rpx 32rpx;
}
.hero-label {
font-size: 26rpx;
color: #666;
}
.hero-value {
font-size: 72rpx;
font-weight: 600;
color: #1890ff;
display: block;
margin: 12rpx 0;
}
.hero-percent {
font-size: 24rpx;
color: #999;
}
.progress-bar {
height: 8rpx;
background: #f0f0f0;
border-radius: 4rpx;
margin: 16rpx 0 24rpx;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: #1890ff;
}
.chips {
display: flex;
gap: 16rpx;
}
.chip {
flex: 1;
padding: 12rpx;
background: #f5f7fa;
border-radius: 8rpx;
font-size: 22rpx;
color: #666;
text-align: center;
}
.section {
background: #fff;
margin-top: 20rpx;
padding: 0 32rpx;
}
.section-title {
font-size: 24rpx;
color: #999;
padding: 20rpx 0;
display: block;
}
.row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 28rpx 0;
border-bottom: 1rpx solid #f0f0f0;
}
.row-main {
display: flex;
flex-direction: column;
gap: 6rpx;
flex: 1;
min-width: 0;
padding-right: 24rpx;
}
.row-label {
font-size: 30rpx;
color: #333;
}
.row-warn {
font-size: 22rpx;
color: #faad14;
}
.row-sub {
display: flex;
align-items: center;
gap: 12rpx;
flex-shrink: 0;
}
.row-sub .size {
font-size: 26rpx;
color: #999;
}
.row-sub .chevron {
font-size: 32rpx;
color: #ccc;
}
.row-desc {
font-size: 22rpx;
color: #999;
margin-top: 6rpx;
}
.others-row {
align-items: flex-start;
}
.others-row .size {
padding-top: 4rpx;
flex-shrink: 0;
white-space: nowrap;
}
.error {
color: #ff4d4f;
font-size: 26rpx;
}
.empty {
text-align: center;
padding: 60rpx 0;
color: #999;
font-size: 28rpx;
}
.load-failed {
text-align: center;
padding: 40rpx 0;
color: #1890ff;
font-size: 28rpx;
cursor: pointer;
}
</style>