feat:修改样式
This commit is contained in:
parent
b0357d39a4
commit
81ee22d2ee
@ -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>
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user