feat:增加实际使用量

This commit is contained in:
zheng020 2026-07-30 15:34:50 +08:00
parent 81ee22d2ee
commit 2f672920d9
4 changed files with 125 additions and 9 deletions

View File

@ -161,6 +161,9 @@ const info = await cacheManager.getCacheInfo()
// quotaTotalBytes, // 配额总量 = uni.getStorageInfoSync().limitSize*1024
// quotaAvailableBytes, // 配额可用 = quotaTotalBytes - appUsedBytes
// usagePercent, // 百分比0-1001 位小数)= appUsedBytes / quotaTotalBytes * 100
// deviceTotalBytes, // 设备总存储 = plus.io.getStorageInfo().totalSize * 1024HTML5+ APIMVP 已支持)
// deviceFreeBytes, // 设备可用存储 = plus.io.getStorageInfo().availableSize * 1024
// deviceUsagePercent, // 设备占比0-1002 位小数)= appUsedBytes / deviceTotalBytes * 100
// othersBytes, // "其他" section 大小 = 所有黑名单 keys + 不可清理文件大小
// categories: [{id, label, description, sizeBytes, keyCount, warning}, ...]
// }
@ -265,13 +268,18 @@ await invalidateAll() // 清空 memoryMap + inFlightMap
│ │
│ 49.7 MB │ ← 大字号主指标(主色)
│ │
│ 占据配额 16% 存储空间 │ ← 百分比副文字(次要色)
│ │
│ ▓▓▓▓░░░░░░░░░░░░░░░░░░░░░ 16% │ ← 进度条(当前用量占配额比例)
│ │
│ ┌────────────┬─────────────┬────────────┐ │
│ │ Topfans 已用空间 │ 内存缓存空间 │ 总内存空间 │ │ ← 一行 3 个 chip 标签(无数值)
│ └────────────┴─────────────┴────────────┘ │ 数值已分别显示在:大字 / 缓存分类 sum / 总配额
│ │
│ ┌──────────────────────────────────────┐ │
│ │ 设备总空间 │ 设备可用 │ │ ← 新增HTML5+ 设备级信息行
│ │ 128.0 GB │ 45.2 GB │ │ plus.io.getStorageInfo非 APP-PLUS 显示 "—"
│ └──────────────────────────────────────┘ │
│ │
│ 占设备 0.04% 存储空间 │ ← 百分比副文字(分母=设备总存储2 位小数)
├─────────────────────────────────────────────┤
│ 缓存分类 │ ← section 标题(次要色)
├─────────────────────────────────────────────┤
@ -314,8 +322,9 @@ await invalidateAll() // 清空 memoryMap + inFlightMap
- 该行**不可点击进入详情**(没有可清理内容)
**关于"磁盘" vs "配额"**
- MVP 用 `uni.getStorageInfoSync().limitSize`app 自己的 SQLite 配额),文案写"配额"而非"磁盘",不误导用户
- 设备级"磁盘总空间/可用空间"需要 native pluginiOS `NSFileManager` / Android `StatFs`**MVP 不引入**列为后续优化§2
- **进度条**仍以 `uni.getStorageInfoSync().limitSize`app 自己的 SQLite 配额)为 100%,表达"app 缓存压力"(用户的清理决策依据)
- **百分比分母** = `plus.io.getStorageInfo().totalSize`(设备总存储),即"占设备 X% 存储空间"——HTML5+ 标准 APIAndroid/iOS 均原生支持,**不需要 native plugin**
- **设备信息行** = `plus.io.getStorageInfo()` 直接拿 `{ totalSize, availableSize }`,无权限要求;非 APP-PLUS 平台或调用失败时显示 `—`
**草稿/引导的二级提示**:行右侧副文字"按账号分组清理",警示用户详情页会有分组。
@ -416,15 +425,19 @@ cacheManager.getCacheInfo()
│ └─ others.computeSize → 上述未匹配的 key 汇总
├─ [并行 2] 读取存储配额
│ ├─ uni.getStorageInfoSync() → { currentSize (KB), limitSize (KB) }
│ └─ ioPath.getSandboxTotalSize() → 沙盒 doc 目录下所有文件总字节数(新增 ioPath 只读 API与 scanSandboxTmpFiles 不同,前者含白名单 preload/share/image 所有文件,后者仅统计 tmp/
│ ├─ ioPath.getSandboxTotalSize() → 沙盒 doc 目录下所有文件总字节数(新增 ioPath 只读 API与 scanSandboxTmpFiles 不同,前者含白名单 preload/share/image 所有文件,后者仅统计 tmp/
│ └─ ioPath.getDeviceStorageInfo() → 设备级 { totalSize (KB), availableSize (KB) }HTML5+ API非 APP-PLUS 返回 {0,0}
聚合:
totalBytes = sum(categories.sizeBytes) // 可清理总量(=内存缓存空间 chip
appUsedBytes = currentSize * 1024 + sandboxBytes // 软件占用(大字 + Topfans已用空间 chip
// 注currentSize 是 uni storage 全量(含黑名单),这是 WeChat "已用空间" 口径
quotaTotalBytes = limitSize * 1024 // 配额总量(=总内存空间 chip
quotaTotalBytes = limitSize * 1024 // 配额总量(=总内存空间 chip,进度条 100% 基准
quotaAvailableBytes = quotaTotalBytes - appUsedBytes // 配额可用
usagePercent = (appUsedBytes / quotaTotalBytes) * 100 // 百分比
usagePercent = (appUsedBytes / quotaTotalBytes) * 100 // 配额百分比(进度条用)
deviceTotalBytes = deviceTotalKB * 1024 // 设备总存储(设备信息行"设备总空间"
deviceFreeBytes = deviceFreeKB * 1024 // 设备可用(设备信息行"设备可用"
deviceUsagePercent = (appUsedBytes / deviceTotalBytes) * 100 // 设备百分比(百分比副文字"占设备 X%"用)
othersBytes = sum(黑名单 keys size) + 不可清理文件大小 // "其他" section
渲染页面(大字 + 百分比 + 进度条 + 3 chip 标签 + 6 缓存分类行 + 1 其他 section

View File

@ -46,8 +46,21 @@
</view>
</view>
<!-- 设备级信息总空间 / 可用空间来自 plus.io.getStorageInfo -->
<view class="device-info">
<view class="device-info-item">
<text class="device-info-label">设备总空间</text>
<text class="device-info-value">{{ formatSize(info.deviceTotalBytes) }}</text>
</view>
<view class="device-info-divider"></view>
<view class="device-info-item">
<text class="device-info-label">设备可用</text>
<text class="device-info-value">{{ formatSize(info.deviceFreeBytes) }}</text>
</view>
</view>
<text class="hero-percent"
>占手机 {{ info.usagePercent.toFixed(1) }}% 存储空间</text
>设备 {{ info.deviceUsagePercent.toFixed(2) }}% 存储空间</text
>
</view>
@ -122,6 +135,9 @@ const info = ref({
quotaTotalBytes: 0,
quotaAvailableBytes: 0,
usagePercent: 0,
deviceTotalBytes: 0,
deviceFreeBytes: 0,
deviceUsagePercent: 0,
othersBytes: 0,
totalBytes: 0,
categories: [],
@ -231,6 +247,41 @@ onPullDownRefresh(async () => {
display: block;
margin-top: 24rpx;
}
/* ── 设备级信息行(总空间 / 可用空间)── */
.device-info {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 24rpx;
padding: 16rpx 20rpx;
background: #fafbfc;
border-radius: 12rpx;
box-sizing: border-box;
}
.device-info-item {
display: flex;
flex-direction: column;
gap: 4rpx;
flex: 1;
min-width: 0;
}
.device-info-divider {
width: 1rpx;
height: 32rpx;
background: #e8e8e8;
margin: 0 16rpx;
flex-shrink: 0;
}
.device-info-label {
font-size: 22rpx;
color: #8c8c8c;
}
.device-info-value {
font-size: 28rpx;
font-weight: 500;
color: #262626;
}
.hero-tip {
display: flex;
align-items: center;

View File

@ -9,7 +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'
import { getSandboxTotalSize, getDeviceStorageInfo } from './ioPath'
// ── 黑名单§3.4.1)──
// 全等匹配
@ -105,6 +105,16 @@ export async function getCacheInfo() {
} catch (e) {
console.warn('[cacheManager] getSandboxTotalSize failed:', e.message)
}
let deviceTotalKB = 0, deviceFreeKB = 0
try {
const dev = await getDeviceStorageInfo()
deviceTotalKB = dev.totalBytes || 0
deviceFreeKB = dev.freeBytes || 0
// 调试日志:把 getDeviceStorageInfo 的原始返回和 cacheManager 解析后的值都打出来
console.log('[cacheManager][debug] getDeviceStorageInfo raw =', JSON.stringify(dev), 'parsed totalKB =', deviceTotalKB, 'freeKB =', deviceFreeKB)
} catch (e) {
console.warn('[cacheManager] getDeviceStorageInfo failed:', e.message)
}
const totalBytes = categories.reduce((sum, c) => sum + (c.sizeBytes > 0 ? c.sizeBytes : 0), 0)
const appUsedBytes = currentSizeKB * 1024 + sandboxBytes
@ -113,9 +123,16 @@ export async function getCacheInfo() {
const quotaAvailableBytes = Math.max(0, raw)
const quotaExceeded = raw < 0
const usagePercent = quotaTotalBytes > 0 ? (appUsedBytes / quotaTotalBytes) * 100 : 0
// 设备级数据plus.io.getStorageInfo 返回 KB转字节
const deviceTotalBytes = deviceTotalKB * 1024
const deviceFreeBytes = deviceFreeKB * 1024
const deviceUsagePercent = deviceTotalBytes > 0
? (appUsedBytes / deviceTotalBytes) * 100
: 0
return {
totalBytes, appUsedBytes, quotaTotalBytes, quotaAvailableBytes, quotaExceeded, usagePercent,
deviceTotalBytes, deviceFreeBytes, deviceUsagePercent,
othersBytes: blacklistBytes + sandboxBytes,
categories,
}

View File

@ -522,3 +522,38 @@ async function _listDir(dirEntry) {
return []
// #endif
}
/**
* 获取设备级存储信息HTML5+ 标准 APIAndroid/iOS 均支持
* 不需要任何 native plugin APP-PLUS 平台或失败时返回安全零值
* 注意单位为 KBplus.io 原始返回调用方按需 * 1024 转字节
* @returns {Promise<{totalBytes:number, freeBytes:number}>}
*/
export function getDeviceStorageInfo() {
return new Promise((resolve) => {
// #ifdef APP-PLUS
try {
plus.io.getStorageInfo({
success: (res) => {
// 调试日志:打印 plus.io.getStorageInfo 的真实返回 shape
console.log('[ioPath][debug] plus.io.getStorageInfo res =', JSON.stringify(res), 'keys =', res ? Object.keys(res) : null)
resolve({
totalBytes: Number(res?.totalSize) || 0,
freeBytes: Number(res?.availableSize) || 0,
})
},
fail: (e) => {
console.warn('[ioPath] getStorageInfo failed:', e?.message)
resolve({ totalBytes: 0, freeBytes: 0 })
},
})
} catch (e) {
console.warn('[ioPath] getStorageInfo threw:', e?.message)
resolve({ totalBytes: 0, freeBytes: 0 })
}
// #endif
// #ifndef APP-PLUS
resolve({ totalBytes: 0, freeBytes: 0 })
// #endif
})
}