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>
<view class="cache-cleanup">
<!-- 顶部 WeChat 风格 -->
<view class="hero">
<!-- 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-value">{{ formatSize(info.appUsedBytes) }}</text>
<text class="hero-percent"
>占据配额 {{ info.usagePercent.toFixed(1) }}% 存储空间</text
>
<!-- 嵌套进度条Topfans 已用空间 内存缓存空间叠在蓝内 总内存空间剩余绿 -->
<view class="progress-bar">
<!-- 绿段剩余配额贴在右侧 -->
<view
class="progress-fill"
:style="{ width: Math.min(100, info.usagePercent) + '%' }"
class="seg-available"
:style="{ width: availablePct + '%' }"
></view>
<!-- 蓝段完整 Topfans 已用从左 -->
<view
class="seg-app"
:style="{ width: appPct + '%' }"
></view>
<!-- 紫段缓存从左叠在蓝段内不超过蓝段右边界 -->
<view
class="seg-cache"
:style="{ width: cachePct + '%' }"
></view>
</view>
<view class="chips">
<view class="chip">Topfans 已用空间</view>
<view class="chip">内存缓存空间</view>
<view class="chip">总内存空间</view>
<view class="chip">
<view class="chip-dot dot-app"></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>
<text class="hero-percent"
>占手机 {{ info.usagePercent.toFixed(1) }}% 存储空间</text
>
</view>
<!-- 缓存分类 -->
<view class="section">
<!-- 缓存分类卡片 -->
<view class="section-card">
<text class="section-title">缓存分类</text>
<!-- 全局空状态失败时不显示避免冲突显示 -->
<view
@ -32,12 +63,14 @@
"
class="empty"
>
<text> 当前无缓存可清理</text>
<view class="empty-icon"></view>
<text class="empty-text">当前无缓存可清理</text>
</view>
<view
v-for="cat in info.categories"
v-for="(cat, index) in info.categories"
:key="cat.id"
class="row"
:class="{ 'row-last': index === info.categories.length - 1 }"
@click="goDetail(cat.id)"
>
<view class="row-main">
@ -54,10 +87,10 @@
</view>
</view>
<!-- "其他" section -->
<view class="section others-section">
<!-- "其他" 卡片 -->
<view class="section-card others-section">
<text class="section-title">其他</text>
<view class="row others-row">
<view class="row others-row row-last">
<view class="row-main">
<text class="row-label">其他</text>
<text class="row-desc"
@ -80,7 +113,7 @@
</template>
<script setup>
import { ref } from "vue";
import { ref, computed } from "vue";
import { onPullDownRefresh, onShow } from "@dcloudio/uni-app";
import { getCacheInfo, formatSize } from "@/utils/cacheManager";
@ -95,6 +128,38 @@ const info = ref({
});
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() {
loadFailed.value = false;
try {
@ -130,69 +195,154 @@ onPullDownRefresh(async () => {
</script>
<style scoped>
/* ── 容器 ── */
.cache-cleanup {
padding-bottom: 40rpx;
padding: 24rpx 24rpx 40rpx;
background: #f5f7fa;
min-height: 100vh;
box-sizing: border-box;
}
.hero {
/* ── Hero 卡片 ── */
.hero-card {
background: #fff;
border-radius: 16rpx;
padding: 40rpx 32rpx 32rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
}
.hero-label {
font-size: 26rpx;
color: #666;
font-size: 28rpx;
color: #8c8c8c;
display: block;
}
.hero-value {
font-size: 72rpx;
font-weight: 600;
font-size: 88rpx;
font-weight: 700;
color: #1890ff;
letter-spacing: -1rpx;
display: block;
margin: 12rpx 0;
margin: 8rpx 0 24rpx;
line-height: 1.1;
}
.hero-percent {
font-size: 24rpx;
color: #999;
color: #8c8c8c;
display: block;
margin-top: 24rpx;
}
.progress-bar {
height: 8rpx;
background: #f0f0f0;
border-radius: 4rpx;
margin: 16rpx 0 24rpx;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: #1890ff;
}
.chips {
.hero-tip {
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;
border-radius: 8rpx;
font-size: 22rpx;
color: #666;
text-align: center;
overflow: hidden;
}
.section {
background: #fff;
/* absolute 绿
紫叠在蓝之上 视觉上"缓存占用 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;
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 {
font-size: 24rpx;
color: #999;
padding: 20rpx 0;
color: #8c8c8c;
font-weight: 500;
padding: 24rpx 0 16rpx;
display: block;
}
/* ── Row ── */
.row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 28rpx 0;
border-bottom: 1rpx solid #f0f0f0;
padding: 32rpx 0;
border-bottom: 1rpx solid #f5f5f5;
transition: background-color 0.15s ease;
}
.row:active {
background: #fafafa;
}
.row-last {
border-bottom: none;
}
.row-main {
display: flex;
@ -204,7 +354,8 @@ onPullDownRefresh(async () => {
}
.row-label {
font-size: 30rpx;
color: #333;
font-weight: 500;
color: #262626;
}
.row-warn {
font-size: 22rpx;
@ -218,16 +369,18 @@ onPullDownRefresh(async () => {
}
.row-sub .size {
font-size: 26rpx;
color: #999;
color: #8c8c8c;
}
.row-sub .chevron {
font-size: 32rpx;
color: #ccc;
font-size: 28rpx;
color: #bfbfbf;
line-height: 1;
}
.row-desc {
font-size: 22rpx;
color: #999;
color: #8c8c8c;
margin-top: 6rpx;
line-height: 1.5;
}
.others-row {
align-items: flex-start;
@ -236,22 +389,49 @@ onPullDownRefresh(async () => {
padding-top: 4rpx;
flex-shrink: 0;
white-space: nowrap;
color: #8c8c8c;
font-size: 26rpx;
}
.error {
color: #ff4d4f;
font-size: 26rpx;
}
/* ── 空状态 ── */
.empty {
text-align: center;
padding: 60rpx 0;
color: #999;
display: flex;
flex-direction: column;
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;
}
/* ── 加载失败按钮 ── */
.load-failed {
margin: 0 24rpx;
text-align: center;
padding: 40rpx 0;
padding: 24rpx 0;
background: #e6f7ff;
color: #1890ff;
font-size: 28rpx;
font-weight: 500;
border-radius: 16rpx;
cursor: pointer;
}
</style>

View File

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

View File

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