feat:修改样式

This commit is contained in:
zheng020 2026-07-30 15:07:56 +08:00
parent b0357d39a4
commit 81ee22d2ee
3 changed files with 250 additions and 69 deletions

View File

@ -1,27 +1,58 @@
<template> <template>
<view class="cache-cleanup"> <view class="cache-cleanup">
<!-- 顶部 WeChat 风格 --> <!-- Hero 卡片核心占用展示 -->
<view class="hero"> <view class="hero-card">
<!-- iOS 受沙盒限制无法读 .app bundle 大小提示用户到系统设置查看真实值 -->
<!-- #ifdef APP-PLUS -->
<view v-if="isIos" class="hero-tip"
>准确值请查看 iPhone设置-通用-iPhone 储存空间</view
>
<!-- #endif -->
<text class="hero-label">Topfans 已用空间</text> <text class="hero-label">Topfans 已用空间</text>
<text class="hero-value">{{ formatSize(info.appUsedBytes) }}</text> <text class="hero-value">{{ formatSize(info.appUsedBytes) }}</text>
<text class="hero-percent"
>占据配额 {{ info.usagePercent.toFixed(1) }}% 存储空间</text <!-- 嵌套进度条Topfans 已用空间 内存缓存空间叠在蓝内 总内存空间剩余绿 -->
>
<view class="progress-bar"> <view class="progress-bar">
<!-- 绿段剩余配额贴在右侧 -->
<view <view
class="progress-fill" class="seg-available"
:style="{ width: Math.min(100, info.usagePercent) + '%' }" :style="{ width: availablePct + '%' }"
></view>
<!-- 蓝段完整 Topfans 已用从左 -->
<view
class="seg-app"
:style="{ width: appPct + '%' }"
></view>
<!-- 紫段缓存从左叠在蓝段内不超过蓝段右边界 -->
<view
class="seg-cache"
:style="{ width: cachePct + '%' }"
></view> ></view>
</view> </view>
<view class="chips"> <view class="chips">
<view class="chip">Topfans 已用空间</view> <view class="chip">
<view class="chip">内存缓存空间</view> <view class="chip-dot dot-app"></view>
<view class="chip">总内存空间</view> <text>Topfans 已用空间</text>
</view>
<view class="chip">
<view class="chip-dot dot-cache"></view>
<text>内存缓存空间</text>
</view>
<view class="chip">
<view class="chip-dot dot-available"></view>
<text>总内存空间</text>
</view>
</view> </view>
<text class="hero-percent"
>占手机 {{ info.usagePercent.toFixed(1) }}% 存储空间</text
>
</view> </view>
<!-- 缓存分类 --> <!-- 缓存分类卡片 -->
<view class="section"> <view class="section-card">
<text class="section-title">缓存分类</text> <text class="section-title">缓存分类</text>
<!-- 全局空状态失败时不显示避免冲突显示 --> <!-- 全局空状态失败时不显示避免冲突显示 -->
<view <view
@ -32,12 +63,14 @@
" "
class="empty" class="empty"
> >
<text> 当前无缓存可清理</text> <view class="empty-icon"></view>
<text class="empty-text">当前无缓存可清理</text>
</view> </view>
<view <view
v-for="cat in info.categories" v-for="(cat, index) in info.categories"
:key="cat.id" :key="cat.id"
class="row" class="row"
:class="{ 'row-last': index === info.categories.length - 1 }"
@click="goDetail(cat.id)" @click="goDetail(cat.id)"
> >
<view class="row-main"> <view class="row-main">
@ -54,10 +87,10 @@
</view> </view>
</view> </view>
<!-- "其他" section --> <!-- "其他" 卡片 -->
<view class="section others-section"> <view class="section-card others-section">
<text class="section-title">其他</text> <text class="section-title">其他</text>
<view class="row others-row"> <view class="row others-row row-last">
<view class="row-main"> <view class="row-main">
<text class="row-label">其他</text> <text class="row-label">其他</text>
<text class="row-desc" <text class="row-desc"
@ -80,7 +113,7 @@
</template> </template>
<script setup> <script setup>
import { ref } from "vue"; import { ref, computed } from "vue";
import { onPullDownRefresh, onShow } from "@dcloudio/uni-app"; import { onPullDownRefresh, onShow } from "@dcloudio/uni-app";
import { getCacheInfo, formatSize } from "@/utils/cacheManager"; import { getCacheInfo, formatSize } from "@/utils/cacheManager";
@ -95,6 +128,38 @@ const info = ref({
}); });
const loadFailed = ref(false); const loadFailed = ref(false);
// quotaTotalBytes = 100%
// 100% 0
// = Topfans = 绿 =
const appPct = computed(() => {
const total = info.value.quotaTotalBytes;
if (!total) return 0;
return Math.min(100, Math.max(0, (info.value.appUsedBytes / total) * 100));
});
const cachePct = computed(() => {
const total = info.value.quotaTotalBytes;
if (!total) return 0;
// Topfans cache used
const cache = Math.min(info.value.totalBytes, info.value.appUsedBytes);
return Math.min(appPct.value, Math.max(0, (cache / total) * 100));
});
const availablePct = computed(() => {
const total = info.value.quotaTotalBytes;
if (!total) return 0;
return Math.max(0, 100 - appPct.value);
});
// APP-PLUS
// #ifdef APP-PLUS
const isIos = (() => {
try {
return uni.getSystemInfoSync().platform === "ios";
} catch (e) {
return false;
}
})();
// #endif
async function load() { async function load() {
loadFailed.value = false; loadFailed.value = false;
try { try {
@ -130,69 +195,154 @@ onPullDownRefresh(async () => {
</script> </script>
<style scoped> <style scoped>
/* ── 容器 ── */
.cache-cleanup { .cache-cleanup {
padding-bottom: 40rpx; padding: 24rpx 24rpx 40rpx;
background: #f5f7fa;
min-height: 100vh;
box-sizing: border-box;
} }
.hero {
/* ── Hero 卡片 ── */
.hero-card {
background: #fff; background: #fff;
border-radius: 16rpx;
padding: 40rpx 32rpx 32rpx; padding: 40rpx 32rpx 32rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
} }
.hero-label { .hero-label {
font-size: 26rpx; font-size: 28rpx;
color: #666; color: #8c8c8c;
display: block;
} }
.hero-value { .hero-value {
font-size: 72rpx; font-size: 88rpx;
font-weight: 600; font-weight: 700;
color: #1890ff; color: #1890ff;
letter-spacing: -1rpx;
display: block; display: block;
margin: 12rpx 0; margin: 8rpx 0 24rpx;
line-height: 1.1;
} }
.hero-percent { .hero-percent {
font-size: 24rpx; font-size: 24rpx;
color: #999; color: #8c8c8c;
display: block;
margin-top: 24rpx;
} }
.progress-bar { .hero-tip {
height: 8rpx;
background: #f0f0f0;
border-radius: 4rpx;
margin: 16rpx 0 24rpx;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: #1890ff;
}
.chips {
display: flex; display: flex;
gap: 16rpx; align-items: center;
font-size: 24rpx;
color: #faad14;
background: #fffbe6;
border-left: 4rpx solid #faad14;
border-radius: 8rpx;
padding: 16rpx 20rpx;
margin-bottom: 24rpx;
line-height: 1.5;
} }
.chip {
flex: 1; /* ── 进度条 ── */
padding: 12rpx; .progress-bar {
position: relative;
height: 16rpx;
background: #f5f7fa; background: #f5f7fa;
border-radius: 8rpx; border-radius: 8rpx;
font-size: 22rpx; overflow: hidden;
color: #666;
text-align: center;
} }
.section { /* absolute 绿
background: #fff; 紫叠在蓝之上 视觉上"缓存占用 Topfans 已用的一部分" */
.seg-app,
.seg-cache,
.seg-available {
position: absolute;
top: 0;
height: 100%;
min-width: 0;
box-shadow: inset 0 0 0 1rpx rgba(255, 255, 255, 0.2);
}
.seg-app {
left: 0;
background: #1890ff;
}
.seg-cache {
left: 0;
background: #722ed1;
}
.seg-available {
right: 0;
background: #52c41a;
}
/* ── 图例 chips ── */
.chips {
display: flex;
gap: 12rpx;
margin-top: 20rpx; margin-top: 20rpx;
padding: 0 32rpx; }
.chip {
/* 不再 flex: 1按内容自适应宽度white-space 防中文被挤压换行 */
padding: 12rpx 16rpx;
background: #fafbfc;
border: 1rpx solid #f0f0f0;
border-radius: 12rpx;
font-size: 22rpx;
color: #595959;
display: inline-flex;
align-items: center;
gap: 8rpx;
box-sizing: border-box;
white-space: nowrap;
flex-shrink: 0;
}
.chip-dot {
width: 8rpx;
height: 8rpx;
border-radius: 50%;
flex-shrink: 0;
}
.dot-app {
background: #1890ff;
}
.dot-cache {
background: #722ed1;
}
.dot-available {
background: #52c41a;
}
/* ── 分组卡片(缓存分类、其他) ── */
.section-card {
background: #fff;
border-radius: 16rpx;
padding: 0 24rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.03);
} }
.section-title { .section-title {
font-size: 24rpx; font-size: 24rpx;
color: #999; color: #8c8c8c;
padding: 20rpx 0; font-weight: 500;
padding: 24rpx 0 16rpx;
display: block; display: block;
} }
/* ── Row ── */
.row { .row {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding: 28rpx 0; padding: 32rpx 0;
border-bottom: 1rpx solid #f0f0f0; border-bottom: 1rpx solid #f5f5f5;
transition: background-color 0.15s ease;
}
.row:active {
background: #fafafa;
}
.row-last {
border-bottom: none;
} }
.row-main { .row-main {
display: flex; display: flex;
@ -204,7 +354,8 @@ onPullDownRefresh(async () => {
} }
.row-label { .row-label {
font-size: 30rpx; font-size: 30rpx;
color: #333; font-weight: 500;
color: #262626;
} }
.row-warn { .row-warn {
font-size: 22rpx; font-size: 22rpx;
@ -218,16 +369,18 @@ onPullDownRefresh(async () => {
} }
.row-sub .size { .row-sub .size {
font-size: 26rpx; font-size: 26rpx;
color: #999; color: #8c8c8c;
} }
.row-sub .chevron { .row-sub .chevron {
font-size: 32rpx; font-size: 28rpx;
color: #ccc; color: #bfbfbf;
line-height: 1;
} }
.row-desc { .row-desc {
font-size: 22rpx; font-size: 22rpx;
color: #999; color: #8c8c8c;
margin-top: 6rpx; margin-top: 6rpx;
line-height: 1.5;
} }
.others-row { .others-row {
align-items: flex-start; align-items: flex-start;
@ -236,22 +389,49 @@ onPullDownRefresh(async () => {
padding-top: 4rpx; padding-top: 4rpx;
flex-shrink: 0; flex-shrink: 0;
white-space: nowrap; white-space: nowrap;
color: #8c8c8c;
font-size: 26rpx;
} }
.error { .error {
color: #ff4d4f; color: #ff4d4f;
font-size: 26rpx; font-size: 26rpx;
} }
/* ── 空状态 ── */
.empty { .empty {
text-align: center; display: flex;
padding: 60rpx 0; flex-direction: column;
color: #999; align-items: center;
padding: 80rpx 0;
gap: 16rpx;
}
.empty-icon {
width: 96rpx;
height: 96rpx;
border-radius: 50%;
background: #f6ffed;
color: #52c41a;
font-size: 48rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
}
.empty-text {
color: #8c8c8c;
font-size: 28rpx; font-size: 28rpx;
} }
/* ── 加载失败按钮 ── */
.load-failed { .load-failed {
margin: 0 24rpx;
text-align: center; text-align: center;
padding: 40rpx 0; padding: 24rpx 0;
background: #e6f7ff;
color: #1890ff; color: #1890ff;
font-size: 28rpx; font-size: 28rpx;
font-weight: 500;
border-radius: 16rpx;
cursor: pointer; cursor: pointer;
} }
</style> </style>

View File

@ -9,6 +9,7 @@ import othersHandler from './handlers/othersHandler'
import preloadHandler from './handlers/preloadHandler' import preloadHandler from './handlers/preloadHandler'
import guideHandler from './handlers/guideHandler' import guideHandler from './handlers/guideHandler'
import draftHandler from './handlers/draftHandler' import draftHandler from './handlers/draftHandler'
import { getSandboxTotalSize } from './ioPath'
// ── 黑名单§3.4.1)── // ── 黑名单§3.4.1)──
// 全等匹配 // 全等匹配
@ -100,7 +101,6 @@ export async function getCacheInfo() {
console.warn('[cacheManager] getStorageInfoSync failed:', e.message) console.warn('[cacheManager] getStorageInfoSync failed:', e.message)
} }
try { try {
const { getSandboxTotalSize } = await import('./ioPath')
sandboxBytes = await getSandboxTotalSize() sandboxBytes = await getSandboxTotalSize()
} catch (e) { } catch (e) {
console.warn('[cacheManager] getSandboxTotalSize failed:', e.message) console.warn('[cacheManager] getSandboxTotalSize failed:', e.message)

View File

@ -343,13 +343,14 @@ async function getAndroidApkSize() {
if (typeof plus === 'undefined' || !plus.android) return 0 if (typeof plus === 'undefined' || !plus.android) return 0
const main = plus.android.runtimeMainActivity() const main = plus.android.runtimeMainActivity()
if (!main) return 0 if (!main) return 0
const Context = plus.android.importClass('android.content.Context') // 走 Context.getApplicationInfo()App.vue#setPermissions 验证过可工作)
const FLAG_PKG_DATA = Context.PM_FLAGS || 0 // 不一定存在 // 不走 PackageManager.getApplicationInfo(pkg, 0):
const pm = main.getPackageManager() // plus.android proxy 对 PackageManager 的 getApplicationInfo 重载方法暴露不稳定,
const pkg = main.getPackageName() // 会报 "pm.getApplicationInfo is not a function"。
// getPackageSizeInfo 是异步回调,这里改用 sourceDir 同步读 // Context.getApplicationInfo() 返回当前应用的 ApplicationInfo,正是我们想要的。
const appInfo = pm.getApplicationInfo(pkg, 0) const appInfo = main.getApplicationInfo()
const sourceDir = appInfo.sourceDir if (!appInfo) return 0
const sourceDir = plus.android.invoke(appInfo, 'getSourceDir') || appInfo.plusGetAttribute?.('sourceDir')
if (!sourceDir) return 0 if (!sourceDir) return 0
const File = plus.android.importClass('java.io.File') const File = plus.android.importClass('java.io.File')
const f = new File(sourceDir) const f = new File(sourceDir)