From 35e0ee3ca0b7e46871599ead3fa345a277037559 Mon Sep 17 00:00:00 2001 From: zhangyibo95 <690096405@qq.com> Date: Mon, 24 Aug 2026 16:35:56 +0800 Subject: [PATCH] =?UTF-8?q?update=EF=BC=9A=E4=BC=98=E5=8C=96=E8=8A=82?= =?UTF-8?q?=E5=A5=8F=E8=AE=AD=E7=BB=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/audioManager.js | 21 + src/matchWebsocket.js | 21 +- src/pages/training/components/ScoreResult.vue | 22 +- .../training/components/ShootProgress.vue | 392 +++++++++++++++++- src/pages/training/difficulty.vue | 4 + src/pages/training/index.vue | 27 +- src/pages/training/practise-one.vue | 233 ++++++++++- .../result-bg-fail.png | Bin 0 -> 159954 bytes .../result-t-bg-fail.png | Bin 0 -> 66468 bytes 9 files changed, 679 insertions(+), 41 deletions(-) create mode 100644 src/static/training-difficulty-design/result-bg-fail.png create mode 100644 src/static/training-difficulty-design/result-t-bg-fail.png diff --git a/src/audioManager.js b/src/audioManager.js index 53d1d65..baeb623 100644 --- a/src/audioManager.js +++ b/src/audioManager.js @@ -1,10 +1,12 @@ export const AUDIO_INTERRUPTION_BEGIN_EVENT = "audio-interruption-begin"; export const AUDIO_INTERRUPTION_END_EVENT = "audio-interruption-end"; +export const RHYTHM_SHOOT_WINDOW_AUDIO_KEY = "请射箭"; const TRAINING_START_AUDIO_KEY_MAP = Object.freeze({ base: "请于计时前完成指定环数对应箭量", endurance: "请于计时前完成累计环数和箭量", precision: "请射箭命中高亮区域", + rhythm: "请在读条推进到标记区间内射箭", }); export const getTrainingStartAudioKey = (trainingType, fallback = "") => @@ -28,6 +30,17 @@ export const getPrecisionShotAudioKeys = (shootData, directionText = "") => { return [directionAudioKey, hitAudioKey].filter(Boolean); }; +export const getRhythmShotAudioKeys = (shootData) => { + if (!shootData || typeof shootData !== "object") return []; + const ring = Number(shootData.ring); + const ringAudioKey = + Number.isFinite(ring) && ring > 0 + ? `${shootData.ringX ? "X" : ring}环` + : "未上靶"; + // 节奏训练是否达标完全由服务端 ok 字段决定,前端不根据环数复算。 + return [ringAudioKey, shootData.ok === true ? "Perfect" : "miss"]; +}; + export const audioFils = { tententen: "https://static.shelingxingqiu.com/shootmini/static/audio/tententen.mp3", 点击按钮: "https://static.shelingxingqiu.com/shootmini/static/audio/%E7%82%B9%E5%87%BB%E6%8C%89%E9%92%AE.mp3", @@ -152,6 +165,14 @@ export const audioFils = { "https://static.shelingxingqiu.com/shootmini/static/audio0820/%E8%AF%B7%E4%BA%8E%E8%AE%A1%E6%97%B6%E5%89%8D%E5%AE%8C%E6%88%90%E7%B4%AF%E8%AE%A1%E7%8E%AF%E6%95%B0%E5%92%8C%E7%AE%AD%E9%87%8F.MP3", 请射箭命中高亮区域: "https://static.shelingxingqiu.com/shootmini/static/audio0820/%E8%AF%B7%E5%B0%84%E7%AE%AD%E5%91%BD%E4%B8%AD%E9%AB%98%E4%BA%AE%E5%8C%BA%E5%9F%9F.MP3", + 请在读条推进到标记区间内射箭: + "https://static.shelingxingqiu.com/shootmini/static/audio0820/%E8%AF%B7%E5%9C%A8%E8%AF%BB%E6%9D%A1%E6%8E%A8%E8%BF%9B%E5%88%B0%E6%A0%87%E8%AE%B0%E5%8C%BA%E9%97%B4%E5%86%85%E5%B0%84%E7%AE%AD.MP3", + [RHYTHM_SHOOT_WINDOW_AUDIO_KEY]: + "https://static.shelingxingqiu.com/shootmini/static/audio0820/%E8%AF%B7%E5%B0%84%E7%AE%AD.MP3", + Perfect: + "https://static.shelingxingqiu.com/shootmini/static/audio0820/Perfect.MP3", + miss: + "https://static.shelingxingqiu.com/shootmini/static/audio0820/miss.MP3", Bingo命中目标: "https://static.shelingxingqiu.com/shootmini/static/audio0820/Bingo%E5%91%BD%E4%B8%AD%E7%9B%AE%E6%A0%87.MP3", 未命中: diff --git a/src/matchWebsocket.js b/src/matchWebsocket.js index 13e0865..37d105b 100644 --- a/src/matchWebsocket.js +++ b/src/matchWebsocket.js @@ -16,6 +16,7 @@ import { } from "@/util"; import { getPrecisionShotAudioKeys, + getRhythmShotAudioKeys, getTrainingStartAudioKey, } from "@/audioManager"; import { @@ -405,10 +406,9 @@ function getAckAudioKeys(message, businessMessage) { ) { return []; } - if ( - (businessMessage?.trainingType || currentContext?.trainingType) === - "precision" - ) { + const trainingType = + businessMessage?.trainingType || currentContext?.trainingType; + if (trainingType === "precision") { const shootData = businessMessage?.shootData; const directionText = shootData?.angle !== null && shootData?.angle !== undefined @@ -416,6 +416,9 @@ function getAckAudioKeys(message, businessMessage) { : ""; return getPrecisionShotAudioKeys(shootData, directionText); } + if (trainingType === "rhythm") { + return getRhythmShotAudioKeys(businessMessage?.shootData); + } return getShootResultAudioKeys(businessMessage?.shootData); case ServerMessageType.SERVER_MSG_MATCH_END: return ["比赛结束"]; @@ -680,7 +683,9 @@ function sendHeartbeatAck() { } function sendPracticeInfoSync() { - if (!socket || !currentContext?.matchId || !currentContext?.userId) return; + if (!socket || !currentContext?.matchId || !currentContext?.userId) { + return false; + } const clientMessage = { type: ClientMessageType.CLIENT_MSG_SYNC_PRACTICE_INFO, @@ -695,6 +700,12 @@ function sendPracticeInfoSync() { "CLIENT_MSG_SYNC_PRACTICE_INFO", clientMessage ); + return true; +} + +// 训练开始后可在不重连 WebSocket 的情况下主动刷新完整练习快照。 +export function requestPracticeInfoSync() { + return sendPracticeInfoSync(); } function sendAck({ matchId, sequence }) { diff --git a/src/pages/training/components/ScoreResult.vue b/src/pages/training/components/ScoreResult.vue index 135a62f..dd7f2b6 100644 --- a/src/pages/training/components/ScoreResult.vue +++ b/src/pages/training/components/ScoreResult.vue @@ -54,6 +54,18 @@ const showComment = ref(false); const showBowData = ref(false); const showUpgrade = ref(false); +const isCompleted = computed(() => props.result.completed === true); +const heroBackground = computed(() => + isCompleted.value + ? "https://static.shelingxingqiu.com/shootmini/static/training-difficulty-design/result-bg.png" + : "https://static.shelingxingqiu.com/shootmini/static/training-difficulty-design/result-bg-fail.png" +); +const titleBackground = computed(() => + isCompleted.value + ? "https://static.shelingxingqiu.com/shootmini/static/training-difficulty-design/result-t-bg.png" + : "https://static.shelingxingqiu.com/shootmini/static/training-difficulty-design/result-t-bg-fail.png" +); + const closePanel = () => { showPanel.value = false; setTimeout(() => { @@ -321,13 +333,13 @@ const calories = computed(