diff --git a/src/audioManager.js b/src/audioManager.js
index f8608a5..0b01a5f 100644
--- a/src/audioManager.js
+++ b/src/audioManager.js
@@ -128,9 +128,42 @@ class AudioManager {
this.failedLoadKeys = new Set();
// 加载代数,用于 reloadAll 时作废旧的加载循环
this.loadGeneration = 0;
+ // 本地路径缓存 Map: { url: localPath }
+ this.localFileCache = uni.getStorageSync("audio_local_files") || {};
+ // 启动时自动清理过期的缓存文件(URL 已不在 audioFils 中的文件)
+ this.cleanObsoleteCache();
this.initAudios();
}
+ // 清理不再使用的缓存文件
+ cleanObsoleteCache() {
+ const activeUrls = new Set(Object.values(audioFils));
+ const cachedUrls = Object.keys(this.localFileCache);
+ let hasChanges = false;
+
+ for (const url of cachedUrls) {
+ if (!activeUrls.has(url)) {
+ debugLog(`发现废弃音频缓存,正在清理: ${url}`);
+ const path = this.localFileCache[url];
+ // 移除物理文件
+ uni.removeSavedFile({
+ filePath: path,
+ complete: () => {
+ // 忽略移除结果,直接移除记录
+ },
+ });
+ // 移除记录
+ delete this.localFileCache[url];
+ hasChanges = true;
+ }
+ }
+
+ if (hasChanges) {
+ uni.setStorageSync("audio_local_files", this.localFileCache);
+ debugLog("废弃缓存清理完成");
+ }
+ }
+
// 初始化音频(两阶段:首轮串行加载全部,次轮仅串行加载失败项一次)
initAudios() {
if (this.isLoading) {
@@ -244,7 +277,7 @@ class AudioManager {
});
}
- // 创建单个音频实例(统一在失败时记录,而非立即重试)
+ // 创建单个音频实例(支持本地缓存)
createAudio(key, callback) {
this.currentLoadingIndex++;
const src = audioFils[key];
@@ -287,7 +320,7 @@ class AudioManager {
clearTimeout(loadTimeout);
this.readyMap.set(key, true);
this.failedLoadKeys.delete(key);
- debugLog(`音频 ${key} 已加载完成`, this.getLoadProgress());
+ // debugLog(`音频 ${key} 已加载完成`, this.getLoadProgress());
uni.$emit("audioLoaded", key);
const loadedAudioKeys = uni.getStorageSync("loadedAudioKeys") || {};
loadedAudioKeys[key] = true;
@@ -298,11 +331,20 @@ class AudioManager {
audio.onError((res) => {
clearTimeout(loadTimeout);
debugLog(`音频 ${key} 加载失败:`, res.errMsg);
+ // 如果是本地文件加载失败,可能是文件损坏,清除缓存以便下次重新下载
+ if (realSrc !== src && this.localFileCache[src] === realSrc) {
+ debugLog(`本地缓存失效,移除记录: ${key}`);
+ delete this.localFileCache[src];
+ uni.setStorageSync("audio_local_files", this.localFileCache);
+ // 移除文件
+ uni.removeSavedFile({ filePath: realSrc });
+ }
this.recordLoadFailure(key);
this.audioMap.delete(key);
audio.destroy();
if (this.readyMap.get(key)) {
- this.readyMap.set(key, false);
+ // 这里不要去除,不然检查进度的时候由于没有重新加载而进度卡住,等播放失败的时候会重新加载
+ // this.readyMap.set(key, false);
} else {
if (callback) callback();
}
@@ -326,23 +368,72 @@ class AudioManager {
this.audioMap.set(key, audio);
};
- // 统一先下载到本地再加载
- uni.downloadFile({
- url: src,
- timeout: 20000,
- success: (res) => {
- // 成功必须有临时文件路径
- if (res.tempFilePath) {
- setupAudio(res.tempFilePath);
- } else {
- this.recordLoadFailure(key);
- if (callback) callback();
- }
- },
- fail: () => {
- this.recordLoadFailure(key);
- if (callback) callback();
- },
+ // 检查是否有可用的本地缓存
+ this.checkLocalFile(src).then((localPath) => {
+ if (localPath) {
+ debugLog(`命中本地缓存: ${key}`);
+ setupAudio(localPath);
+ } else {
+ console.log("download");
+ // 下载并尝试保存
+ uni.downloadFile({
+ url: src,
+ timeout: 20000,
+ success: (res) => {
+ if (res.tempFilePath) {
+ // 尝试保存文件到本地存储(持久化)
+ uni.saveFile({
+ tempFilePath: res.tempFilePath,
+ success: (saveRes) => {
+ const savedPath = saveRes.savedFilePath;
+ this.localFileCache[src] = savedPath;
+ uni.setStorageSync("audio_local_files", this.localFileCache);
+ debugLog(`音频已缓存到本地: ${key}`);
+ setupAudio(savedPath);
+ },
+ fail: (err) => {
+ debugLog(
+ `保存音频失败(可能空间不足),使用临时文件: ${key}`,
+ err
+ );
+ setupAudio(res.tempFilePath);
+ },
+ });
+ } else {
+ this.recordLoadFailure(key);
+ if (callback) callback();
+ }
+ },
+ fail: () => {
+ this.recordLoadFailure(key);
+ if (callback) callback();
+ },
+ });
+ }
+ });
+ }
+
+ // 检查本地文件是否有效
+ checkLocalFile(url) {
+ return new Promise((resolve) => {
+ const path = this.localFileCache[url];
+ if (!path) {
+ resolve(null);
+ return;
+ }
+ // 检查文件是否存在
+ uni.getFileSystemManager().getFileInfo({
+ filePath: path,
+ success: () => {
+ resolve(path);
+ },
+ fail: () => {
+ // 文件不存在,清理记录
+ delete this.localFileCache[url];
+ uni.setStorageSync("audio_local_files", this.localFileCache);
+ resolve(null);
+ },
+ });
});
}
@@ -550,9 +641,28 @@ class AudioManager {
return Number((loaded / total).toFixed(2));
}
-
+ // 清理本地音频缓存文件
+ clearCache() {
+ debugLog("开始清理本地音频缓存...");
+ const cache = uni.getStorageSync("audio_local_files") || {};
+ const paths = Object.values(cache);
+ for (const path of paths) {
+ uni.removeSavedFile({
+ filePath: path,
+ complete: (res) => {
+ // 无论成功失败都继续
+ },
+ });
+ }
+ uni.removeStorageSync("audio_local_files");
+ this.localFileCache = {};
+ debugLog("本地音频缓存清理完成");
+ }
+
// 手动重置并重新加载所有音频(用于卡住时恢复)
reloadAll() {
+ debugLog("执行 reloadAll: 重置所有状态并重新加载");
+
// 1. 停止所有播放
this.stopAll();
@@ -573,6 +683,10 @@ class AudioManager {
this.sequenceIndex = 0;
this.isSequenceRunning = false;
+ // 清理一下可能损坏的缓存(可选,如果用户因为缓存坏了卡住,这一步很有用)
+ // 这里选择不自动全清,而是依赖 onError 里的单点清除。如果需要彻底重置,可取消注释:
+ // this.clearCache();
+
// 4. 强制重置加载锁
this.isLoading = false;
this.loadingPromise = null;
diff --git a/src/components/BowTarget.vue b/src/components/BowTarget.vue
index 431fc90..8d92d1e 100644
--- a/src/components/BowTarget.vue
+++ b/src/components/BowTarget.vue
@@ -1,6 +1,8 @@
-
+
-
- {{ name }}
+
+ {{ player.name }}
diff --git a/src/components/PointSwitcher.vue b/src/components/PointSwitcher.vue
new file mode 100644
index 0000000..3891612
--- /dev/null
+++ b/src/components/PointSwitcher.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+ 放大
+
+ 真实
+
+
+
+
+
diff --git a/src/pages/index.vue b/src/pages/index.vue
index efa871e..cd6e827 100644
--- a/src/pages/index.vue
+++ b/src/pages/index.vue
@@ -9,6 +9,7 @@ import SModal from "@/components/SModal.vue";
import Signin from "@/components/Signin.vue";
import BubbleTip from "@/components/BubbleTip.vue";
import ScreenHint2 from "@/components/ScreenHint2.vue";
+
import {
getAppConfig,
getRankListAPI,
diff --git a/src/pages/melee-match.vue b/src/pages/melee-match.vue
index 910722e..2744dfe 100644
--- a/src/pages/melee-match.vue
+++ b/src/pages/melee-match.vue
@@ -48,6 +48,7 @@ watch(
function recoverData(battleInfo) {
uni.removeStorageSync("last-awake-time");
+ // 注释用于测试
battleId.value = battleInfo.id;
players.value = [...battleInfo.blueTeam, ...battleInfo.redTeam];
players.value.forEach((p) => {
@@ -221,8 +222,7 @@ onHide(() => {
v-if="start"
v-for="(player, index) in playersSorted"
:key="index"
- :name="player.name"
- :avatar="player.avatar"
+ :player="player"
:scores="playersScores[player.id] || []"
/>