update:个人训练优化
This commit is contained in:
@@ -9,17 +9,23 @@ const playAudio = (key) => {
|
||||
audioManager.play(key);
|
||||
};
|
||||
|
||||
const onAudioLoaded = (key) => {
|
||||
loaded.value = {
|
||||
...loaded.value,
|
||||
[key]: true,
|
||||
};
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const loadedAudioKeys = uni.getStorageSync("loadedAudioKeys") || {};
|
||||
loaded.value = loadedAudioKeys;
|
||||
|
||||
uni.$on("audioLoaded", (key) => {
|
||||
loaded.value[key] = true;
|
||||
});
|
||||
uni.$on("audioLoaded", onAudioLoaded);
|
||||
void audioManager.initAudios();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
uni.$off("audioLoaded");
|
||||
uni.$off("audioLoaded", onAudioLoaded);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -40,8 +46,10 @@ onBeforeUnmount(() => {
|
||||
</view>
|
||||
<view v-for="key in Object.keys(audioFils)" :key="key">
|
||||
<text>{{ key }}</text>
|
||||
<text v-if="!loaded[key]">未加载</text>
|
||||
<button v-else hover-class="none" @click="playAudio(key)">播放</button>
|
||||
<text>{{ loaded[key] ? "已加载" : "未加载" }}</text>
|
||||
<button hover-class="none" @click="playAudio(key)">
|
||||
{{ loaded[key] ? "播放" : "加载并播放" }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</Container>
|
||||
|
||||
+82
-16
@@ -1,25 +1,70 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import Container from "@/components/Container.vue";
|
||||
import Avatar from "@/components/Avatar.vue";
|
||||
import BowTarget from "@/components/BowTarget.vue";
|
||||
import ScorePanel from "@/components/ScorePanel.vue";
|
||||
import { getPractiseAPI } from "@/apis";
|
||||
import ScorePanel2 from "@/components/TrainingScorePanel.vue";
|
||||
import { getPractiseDetailAPI } from "@/apis";
|
||||
import useStore from "@/store";
|
||||
import { storeToRefs } from "pinia";
|
||||
const store = useStore();
|
||||
const { user } = storeToRefs(store);
|
||||
const arrows = ref([]);
|
||||
const isSvip = ref(false);
|
||||
const total = ref(0);
|
||||
const practiseDetail = ref({});
|
||||
const trainingTypeNameMap = Object.freeze({
|
||||
base: "基础训练",
|
||||
endurance: "耐力训练",
|
||||
precision: "精准训练",
|
||||
rhythm: "节奏训练",
|
||||
});
|
||||
|
||||
const trainingType = computed(() =>
|
||||
String(practiseDetail.value.trainingType || "").trim().toLowerCase()
|
||||
);
|
||||
const trainingTypeName = computed(
|
||||
() => trainingTypeNameMap[trainingType.value] || "自由训练"
|
||||
);
|
||||
const targetTypeText = computed(() => {
|
||||
const targetType = Number(practiseDetail.value.targetType);
|
||||
return targetType > 0 ? `${targetType}CM靶` : "";
|
||||
});
|
||||
const difficultyText = computed(() => {
|
||||
const difficultyLevel = Number(practiseDetail.value.difficultyLevel);
|
||||
return Number.isInteger(difficultyLevel) && difficultyLevel > 0
|
||||
? `LV${difficultyLevel}`
|
||||
: "";
|
||||
});
|
||||
const totalRings = computed(() => {
|
||||
const interpretationTotal = Number(
|
||||
practiseDetail.value.interpretation?.totalRings
|
||||
);
|
||||
if (Number.isFinite(interpretationTotal)) return interpretationTotal;
|
||||
return arrows.value.reduce(
|
||||
(total, arrow) => total + (Number(arrow.ring) || 0),
|
||||
0
|
||||
);
|
||||
});
|
||||
|
||||
const normalizeArrow = (arrow = {}) => ({
|
||||
...arrow,
|
||||
ring: Number(arrow.ring) || 0,
|
||||
ringX:
|
||||
arrow.ringX === true ||
|
||||
Number(arrow.ringX) === 1 ||
|
||||
Number(arrow.ifX) === 1,
|
||||
ok: arrow.ok === true || Number(arrow.ok) === 1,
|
||||
});
|
||||
|
||||
onLoad(async (options) => {
|
||||
if (!options.id) return;
|
||||
const result = await getPractiseAPI(options.id || 176);
|
||||
arrows.value = result.details;
|
||||
isSvip.value = result.sVip === true;
|
||||
total.value = result.details.length;
|
||||
const result = (await getPractiseDetailAPI(options.id)) || {};
|
||||
practiseDetail.value = result;
|
||||
arrows.value = Array.isArray(result.details)
|
||||
? result.details.map(normalizeArrow)
|
||||
: [];
|
||||
isSvip.value = result.sVip === true || user.value?.sVip === true;
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -35,22 +80,29 @@ onLoad(async (options) => {
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
<view v-if="practiseDetail.id" class="practice-meta">
|
||||
<text v-if="targetTypeText">{{ targetTypeText }}</text>
|
||||
<text>{{ trainingTypeName }}</text>
|
||||
<text v-if="difficultyText">{{ difficultyText }}</text>
|
||||
</view>
|
||||
<view :style="{ marginBottom: '20px' }">
|
||||
<BowTarget :scores="arrows" :isSvip="isSvip" />
|
||||
<BowTarget
|
||||
:scores="arrows"
|
||||
:isSvip="isSvip"
|
||||
:enableShotEffect="false"
|
||||
/>
|
||||
</view>
|
||||
<view class="desc">
|
||||
<text>{{ arrows.length }}</text>
|
||||
<text>支箭,共</text>
|
||||
<text>{{ arrows.reduce((a, b) => a + b.ring, 0) }}</text>
|
||||
<text>{{ totalRings }}</text>
|
||||
<text>环</text>
|
||||
</view>
|
||||
<ScorePanel
|
||||
:completeEffect="false"
|
||||
:rowCount="total === 12 ? 6 : 9"
|
||||
:total="total"
|
||||
<ScorePanel2
|
||||
:arrows="arrows"
|
||||
:margin="arrows.length === 12 ? 4 : 1"
|
||||
:fontSize="arrows.length === 12 ? 25 : 22"
|
||||
:total="arrows.length"
|
||||
:trainingType="trainingType"
|
||||
recordMode
|
||||
/>
|
||||
</view>
|
||||
</Container>
|
||||
@@ -63,6 +115,20 @@ onLoad(async (options) => {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.practice-meta {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
line-height: 40rpx;
|
||||
padding: 16rpx 0;
|
||||
}
|
||||
.practice-meta > text + text {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
+22
-3
@@ -15,6 +15,25 @@ const selectedIndex = ref(0);
|
||||
const matchList = ref([]);
|
||||
const battleList = ref([]);
|
||||
const practiseList = ref([]);
|
||||
const trainingTypeNameMap = Object.freeze({
|
||||
base: "基础训练",
|
||||
endurance: "耐力训练",
|
||||
precision: "精准训练",
|
||||
rhythm: "节奏训练",
|
||||
});
|
||||
|
||||
const getTrainingTypeName = (trainingType) => {
|
||||
const normalizedType = String(trainingType || "").trim().toLowerCase();
|
||||
return trainingTypeNameMap[normalizedType] || "自由训练";
|
||||
};
|
||||
|
||||
const formatPractiseTime = (value) => {
|
||||
const normalizedTime = String(value || "").trim();
|
||||
const matchedTime = normalizedTime.match(
|
||||
/^(\d{4}-\d{2}-\d{2})[T\s](\d{2}:\d{2}:\d{2})/
|
||||
);
|
||||
return matchedTime ? `${matchedTime[1]} ${matchedTime[2]}` : normalizedTime;
|
||||
};
|
||||
|
||||
const toMatchDetail = (id) => {
|
||||
uni.navigateTo({
|
||||
@@ -81,7 +100,7 @@ onLoad((options) => {
|
||||
<Container title="我的成长脚印" :scroll="false">
|
||||
<view class="tabs">
|
||||
<view
|
||||
v-for="(rankType, index) in ['排位赛', '好友约战', '个人练习']"
|
||||
v-for="(rankType, index) in ['排位赛', '好友约战', '个人训练']"
|
||||
:key="index"
|
||||
:style="{
|
||||
color: index === selectedIndex ? '#000' : '#fff',
|
||||
@@ -153,8 +172,8 @@ onLoad((options) => {
|
||||
@click="() => getPractiseDetail(item.id)"
|
||||
>
|
||||
<text
|
||||
>{{ item.completed_arrows === 36 ? "耐力挑战" : "单组练习" }}
|
||||
{{ item.createTime }}</text
|
||||
>{{ getTrainingTypeName(item.trainingType) }}
|
||||
{{ formatPractiseTime(item.createTime) }}</text
|
||||
>
|
||||
<image src="../static/back.png" mode="widthFix" />
|
||||
</view>
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
MATCH_WS_STATE_EVENT,
|
||||
} from "@/matchWebsocket";
|
||||
import { MESSAGETYPESV2 } from "@/constants";
|
||||
import { getDirectionText } from "@/util";
|
||||
import { getDirectionText, getInvalidShotAudioKey } from "@/util";
|
||||
import { takeMatchReturnSnapshot } from "@/utils/matchReturn";
|
||||
import audioManager, {
|
||||
AUDIO_INTERRUPTION_BEGIN_EVENT,
|
||||
@@ -1136,7 +1136,9 @@ async function runInvalidShotTask(task, runId) {
|
||||
title: "距离不足,无效",
|
||||
icon: "none",
|
||||
});
|
||||
await playAudioKeys("射击无效", { interrupt: false });
|
||||
await playAudioKeys(getInvalidShotAudioKey(task.message?.shootData), {
|
||||
interrupt: false,
|
||||
});
|
||||
notifyMatchAudioAck(task);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,10 @@ const props = defineProps({
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
highlightRefreshToken: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: "solo", // solo 单排,team 双排
|
||||
@@ -530,6 +534,7 @@ onBeforeUnmount(() => {
|
||||
:sectorCount="sectorCount"
|
||||
:activeSector="activeSector"
|
||||
:activeRing="activeRing"
|
||||
:highlightRefreshToken="highlightRefreshToken"
|
||||
:showSectorLabels="showSectorLabels"
|
||||
/>
|
||||
<view v-if="angle !== null" class="arrow-dir" :style="arrowStyle">
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
<script setup>
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
arrows: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const getDisplayText = (arrow = {}) => {
|
||||
if (!arrow) return "";
|
||||
if (!arrow.ring) return "0";
|
||||
return arrow.ringX ? "X" : String(arrow.ring);
|
||||
};
|
||||
|
||||
const isLowScore = (arrow = {}) => {
|
||||
if (!arrow || arrow.ringX) return false;
|
||||
return Number(arrow.ring) < 6;
|
||||
};
|
||||
|
||||
const displayArrows = computed(() => {
|
||||
const list = [...props.arrows];
|
||||
// total 是达标箭数,不是实际射箭上限;训练中始终预留下一箭空框。
|
||||
list.push(null);
|
||||
return list;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view v-if="displayArrows.length" class="score-panel">
|
||||
<view class="score-grid">
|
||||
<view
|
||||
v-for="(arrow, index) in displayArrows"
|
||||
:key="index"
|
||||
class="score-card"
|
||||
>
|
||||
<image class="score-card-bg" :src="isLowScore(arrow)?'https://static.shelingxingqiu.com/shootmini/static/training-difficulty-design/block-gray.png':'https://static.shelingxingqiu.com/shootmini/static/training-difficulty-design/block-gold.png'"></image>
|
||||
<text
|
||||
class="score-value"
|
||||
:class="{ 'score-value--low': isLowScore(arrow) }"
|
||||
>
|
||||
{{ getDisplayText(arrow) }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.score-panel {
|
||||
width: 100%;
|
||||
padding: 30rpx 40rpx 0 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.score-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.score-card {
|
||||
position: relative;
|
||||
width: 100rpx;
|
||||
height: 56rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
margin-right: 14rpx;
|
||||
margin-bottom: 14rpx;
|
||||
}
|
||||
|
||||
.score-card:nth-child(6n) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.score-card-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100rpx;
|
||||
height: 56rpx;
|
||||
}
|
||||
|
||||
.score-value {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
min-width: 28rpx;
|
||||
text-align: center;
|
||||
font-size: 34rpx;
|
||||
line-height: 1;
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
color: #f6e3b2;
|
||||
text-shadow: 0 2rpx 0 rgba(36, 36, 48, 0.5);
|
||||
margin-left: -10rpx;
|
||||
}
|
||||
|
||||
.score-value--low {
|
||||
color: #cfcfcf;
|
||||
text-shadow: 0 2rpx 0 rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
</style>
|
||||
@@ -2,7 +2,7 @@
|
||||
import { ref, watch, onMounted, onBeforeUnmount, computed } from "vue";
|
||||
import audioManager from "@/audioManager";
|
||||
import { MESSAGETYPESV2 } from "@/constants";
|
||||
import { getDirectionText } from "@/util";
|
||||
import { getDirectionText, getInvalidShotAudioKey } from "@/util";
|
||||
import Avatar from "@/components/Avatar.vue";
|
||||
|
||||
import useStore from "@/store";
|
||||
@@ -241,7 +241,7 @@ async function onReceiveMessage(msg) {
|
||||
title: "距离不足,无效",
|
||||
icon: "none",
|
||||
});
|
||||
audioManager.play("射击无效");
|
||||
audioManager.play(getInvalidShotAudioKey(msg.shootData));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,8 +55,13 @@ onBeforeUnmount(() => {
|
||||
async function onReceiveMessage(msg) {
|
||||
if (Array.isArray(msg)) return;
|
||||
if (msg.type === MESSAGETYPESV2.TestDistance) {
|
||||
distance.value = Number((msg.shootData.distance / 100).toFixed(2));
|
||||
if (distance.value >= 5) audioManager.play("距离合格");
|
||||
const rawDistance = Number(msg.shootData?.distance);
|
||||
distance.value = Number.isFinite(rawDistance)
|
||||
? Number((rawDistance / 100).toFixed(2))
|
||||
: 0;
|
||||
if (rawDistance === 0) {
|
||||
audioManager.play("未发现靶纸,请瞄准靶纸射箭");
|
||||
} else if (distance.value >= 5) audioManager.play("距离合格");
|
||||
else audioManager.play("距离不足");
|
||||
}
|
||||
}
|
||||
@@ -94,7 +99,7 @@ onBeforeUnmount(() => {
|
||||
模拟射箭
|
||||
</button>
|
||||
<view class="warnning-text">
|
||||
<view class="target-tip">当前靶子为<text class="text-yellow">{{ targetType }}cm</text>全环靶,请更换靶子</view>
|
||||
<view class="target-tip">当前靶纸为<text class="text-yellow">{{ targetType }}cm</text>全环靶</view>
|
||||
<block v-if="distance > 0">
|
||||
<text>当前距离<text class="text-yellow">{{ distance }}</text>米</text>
|
||||
<text v-if="distance >= 5">已达到距离要求</text>
|
||||
|
||||
@@ -107,30 +107,27 @@ const createDifficultySummary = (item = {}) => {
|
||||
const timeLimit = toNumber(item.time_limit);
|
||||
const hitReq = toNumber(item.hit_req);
|
||||
const totalReq = toNumber(item.total_req);
|
||||
const blocks = toNumber(item.blocks);
|
||||
const promoteCnt = toNumber(item.promote_cnt);
|
||||
const timeText = timeLimit > 0 ? `${timeLimit}秒内完成` : "不限时完成";
|
||||
const shootingTimeText =
|
||||
timeLimit > 0 ? `在${timeLimit}秒内进行射箭` : "不限时进行射箭";
|
||||
const enduranceTimeText =
|
||||
timeLimit > 0
|
||||
? `在${timeLimit}秒内完成${arrows}箭`
|
||||
: `不限时完成${arrows}箭`;
|
||||
const promoteText = promoteCnt > 0 ? `完成${promoteCnt}次晋级` : "";
|
||||
|
||||
const summaryMap = {
|
||||
base: [
|
||||
desc || (hitReq > 0 ? `每箭命中${hitReq}环以上` : "上靶即可"),
|
||||
[`${arrows}箭`, promoteText].filter(Boolean).join(" · "),
|
||||
shootingTimeText,
|
||||
`需要有${arrows}箭命中${hitReq}环内`,
|
||||
],
|
||||
endurance: [
|
||||
desc || `${timeText}${arrows}箭`,
|
||||
[`累计${totalReq}环`, promoteText].filter(Boolean).join(" · "),
|
||||
enduranceTimeText,
|
||||
`且累计环数大于${totalReq}环`,
|
||||
],
|
||||
precision: [
|
||||
desc || `命中${blocks}个指定区域`,
|
||||
[
|
||||
`${arrows}箭`,
|
||||
timeText,
|
||||
getDifficultyModeText(item.mode),
|
||||
promoteText,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" · "),
|
||||
shootingTimeText,
|
||||
`需要有${arrows}箭命中高亮区域`,
|
||||
],
|
||||
rhythm: [
|
||||
desc || `间隔${timeLimit}秒射击`,
|
||||
|
||||
@@ -64,8 +64,8 @@ const createDefaultTrainingData = () => ({
|
||||
stats: {
|
||||
total_training_days: 0,
|
||||
total_arrows: 0,
|
||||
hit_rate: 0,
|
||||
endurance_shoot_speed: 0,
|
||||
target_rate: 0,
|
||||
ten_ring_count: 0,
|
||||
total_calories: 0,
|
||||
},
|
||||
beat_percent: 0,
|
||||
@@ -286,8 +286,8 @@ const loadPersonalTrainingData = async () => {
|
||||
stats: {
|
||||
total_training_days: result?.stats?.total_training_days ?? 0,
|
||||
total_arrows: result?.stats?.total_arrows ?? 0,
|
||||
hit_rate: result?.stats?.hit_rate ?? 0,
|
||||
endurance_shoot_speed: result?.stats?.endurance_shoot_speed ?? 0,
|
||||
target_rate: result?.stats?.target_rate ?? 0,
|
||||
ten_ring_count: result?.stats?.ten_ring_count ?? 0,
|
||||
total_calories: result?.stats?.total_calories ?? 0,
|
||||
},
|
||||
beat_percent: result?.beat_percent ?? 0,
|
||||
@@ -431,26 +431,26 @@ onShow(async () => {
|
||||
<view class="stats-value-row">
|
||||
<view class="stats-value-group">
|
||||
<text class="stats-value">
|
||||
{{ formatValue(trainingData.stats.hit_rate) }}
|
||||
{{ formatValue(trainingData.stats.target_rate) }}
|
||||
</text>
|
||||
<text class="stats-unit">%</text>
|
||||
<view class="stats-value-decoration"></view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="stats-label">命中率</text>
|
||||
<text class="stats-label">上靶率</text>
|
||||
</view>
|
||||
|
||||
<view class="stats-item">
|
||||
<view class="stats-value-row">
|
||||
<view class="stats-value-group">
|
||||
<text class="stats-value">
|
||||
{{ formatValue(trainingData.stats.endurance_shoot_speed, 0) }}
|
||||
{{ formatValue(trainingData.stats.ten_ring_count, 0) }}
|
||||
</text>
|
||||
<text class="stats-unit">支/分钟</text>
|
||||
<text class="stats-unit">支</text>
|
||||
<view class="stats-value-decoration"></view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="stats-label">耐力射击</text>
|
||||
<text class="stats-label">10环数</text>
|
||||
</view>
|
||||
|
||||
<view class="stats-item">
|
||||
|
||||
@@ -4,7 +4,7 @@ import { onHide, onLoad, onShow, onUnload } from "@dcloudio/uni-app";
|
||||
import Container from "@/components/Container.vue";
|
||||
import ShootProgress from "./components/ShootProgress.vue";
|
||||
import BowTarget from "./components/BowTarget.vue";
|
||||
import ScorePanel2 from "./components/ScorePanel2.vue";
|
||||
import ScorePanel2 from "@/components/TrainingScorePanel.vue";
|
||||
import ScoreResult from "./components/ScoreResult.vue";
|
||||
import Avatar from "@/components/Avatar.vue";
|
||||
import BowPower from "@/components/BowPower.vue";
|
||||
@@ -48,6 +48,8 @@ const pageStage = ref(pageStages.LOADING);
|
||||
const scores = ref([]);
|
||||
// 只在实时 ShootResult 新增一箭时递增,避免同步快照重播飞箭特效。
|
||||
const shotEffectToken = ref(0);
|
||||
// 可见区域每次正式提交都递增,同一区域连续刷新也能触发动效。
|
||||
const precisionTargetRefreshToken = ref(0);
|
||||
const defaultTotal = 12;
|
||||
const defaultTargetType = 1;
|
||||
const total = ref(defaultTotal);
|
||||
@@ -253,17 +255,23 @@ const trainingCopy = computed(() => {
|
||||
practiceInfo.value.hitReq,
|
||||
trainingParams.value.hitReq
|
||||
);
|
||||
const arrowsLeft = getPracticeNumber(
|
||||
practiceInfo.value.arrowsLeft,
|
||||
total.value
|
||||
const targetArrows = getPositiveInteger(total.value);
|
||||
const arrowsLeft = Math.min(
|
||||
targetArrows,
|
||||
Math.max(
|
||||
0,
|
||||
getPracticeNumber(practiceInfo.value.arrowsLeft, targetArrows)
|
||||
)
|
||||
);
|
||||
const completedArrows = targetArrows - arrowsLeft;
|
||||
|
||||
return {
|
||||
title: `每箭命中${hitReq}环之上`,
|
||||
inline: true,
|
||||
details: [
|
||||
{ text: "剩余" },
|
||||
{ text: arrowsLeft, highlight: true },
|
||||
{ text: "箭达到条件" },
|
||||
{ text: "计时结束前需要有" },
|
||||
{ text: `(${completedArrows}/${targetArrows})`, highlight: true },
|
||||
{ text: "箭命中" },
|
||||
{ text: `${hitReq}环`, highlight: true },
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -281,35 +289,33 @@ const trainingCopy = computed(() => {
|
||||
const currentRings = getPracticeNumber(practiceInfo.value.currentRings);
|
||||
|
||||
return {
|
||||
title: `完成${targetArrows}箭并累计${targetRings}环`,
|
||||
inline: true,
|
||||
details: [
|
||||
{ text: "已完成" },
|
||||
{ text: currentArrows, highlight: true },
|
||||
{ text: "箭,累计" },
|
||||
{ text: currentRings, highlight: true },
|
||||
{ text: "计时结束前完成" },
|
||||
{ text: `(${currentArrows}/${targetArrows})`, highlight: true },
|
||||
{ text: "箭且累计命中" },
|
||||
{ text: `(${currentRings}/${targetRings})`, highlight: true },
|
||||
{ text: "环" },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (trainingType.value === "precision") {
|
||||
const block = precisionRandomBlock.value;
|
||||
const ring = precisionRandomRingArea.value;
|
||||
const arrowsLeft = getPracticeNumber(
|
||||
practiceInfo.value.arrowsLeft,
|
||||
total.value
|
||||
const targetArrows = getPositiveInteger(total.value);
|
||||
const arrowsLeft = Math.min(
|
||||
targetArrows,
|
||||
Math.max(
|
||||
0,
|
||||
getPracticeNumber(practiceInfo.value.arrowsLeft, targetArrows)
|
||||
)
|
||||
);
|
||||
const title = block
|
||||
? ring
|
||||
? `请命中区域${block}的${ring}环`
|
||||
: `请命中区域${block}`
|
||||
: "等待目标区域";
|
||||
const completedArrows = targetArrows - arrowsLeft;
|
||||
|
||||
return {
|
||||
title,
|
||||
inline: true,
|
||||
details: [
|
||||
{ text: "剩余" },
|
||||
{ text: arrowsLeft, highlight: true },
|
||||
{ text: "射箭命中高亮区域需完成" },
|
||||
{ text: `(${completedArrows}/${targetArrows})`, highlight: true },
|
||||
{ text: "箭" },
|
||||
],
|
||||
};
|
||||
@@ -563,7 +569,10 @@ const commitPrecisionTargetAfterPresentation = async ({
|
||||
) {
|
||||
return;
|
||||
}
|
||||
visiblePrecisionTarget.value = target;
|
||||
applyVisiblePrecisionTarget(target);
|
||||
if (precisionRandomBlock.value > 0) {
|
||||
precisionTargetRefreshToken.value += 1;
|
||||
}
|
||||
};
|
||||
|
||||
const createPracticeEndSnapshot = (message = {}) => {
|
||||
@@ -1329,6 +1338,7 @@ onBeforeUnmount(() => {
|
||||
:sectorCount="precisionBlocks"
|
||||
:activeSector="precisionRandomBlock"
|
||||
:activeRing="precisionRandomRingArea"
|
||||
:highlightRefreshToken="precisionTargetRefreshToken"
|
||||
:showSectorLabels="precisionBlocks > 0"
|
||||
stable-shot-effect
|
||||
@shot-effect-complete="onShotEffectComplete"
|
||||
@@ -1349,7 +1359,7 @@ onBeforeUnmount(() => {
|
||||
重置高亮
|
||||
</button>
|
||||
</view> -->
|
||||
<view class="sound-text-box">
|
||||
<view class="sound-row">
|
||||
<button class="sound-btn" hover-class="none" @click="updateSound">
|
||||
<image
|
||||
class="sound-icon"
|
||||
@@ -1357,6 +1367,8 @@ onBeforeUnmount(() => {
|
||||
mode="aspectFit"
|
||||
/>
|
||||
</button>
|
||||
</view>
|
||||
<view class="sound-text-box">
|
||||
<view class="bat-text-big-box">
|
||||
<image
|
||||
class="dao-icon"
|
||||
@@ -1366,14 +1378,23 @@ onBeforeUnmount(() => {
|
||||
<view v-if="trainingCopy" class="bat-text-box">
|
||||
<view class="bat-text-small-box">
|
||||
<view class="text-round-box">
|
||||
<view class="text1">{{ trainingCopy.title }}</view>
|
||||
<view class="text2">
|
||||
<view v-if="trainingCopy.inline" class="training-copy-inline">
|
||||
<text
|
||||
v-for="(part, index) in trainingCopy.details"
|
||||
:key="index"
|
||||
:class="{ 'text2-yellow': part.highlight }"
|
||||
>{{ part.text }}</text>
|
||||
</view>
|
||||
<view v-else>
|
||||
<view class="text1">{{ trainingCopy.title }}</view>
|
||||
<view class="text2">
|
||||
<text
|
||||
v-for="(part, index) in trainingCopy.details"
|
||||
:key="index"
|
||||
:class="{ 'text2-yellow': part.highlight }"
|
||||
>{{ part.text }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -1386,7 +1407,11 @@ onBeforeUnmount(() => {
|
||||
:enhanced="true"
|
||||
:show-scrollbar="false"
|
||||
>
|
||||
<ScorePanel2 :arrows="scores" :total="total" />
|
||||
<ScorePanel2
|
||||
:arrows="scores"
|
||||
:total="total"
|
||||
:trainingType="trainingType"
|
||||
/>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<ScoreResult
|
||||
@@ -1494,6 +1519,12 @@ onBeforeUnmount(() => {
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.sound-row {
|
||||
height: 70rpx;
|
||||
padding: 0 56rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.sound-text-box{
|
||||
height: 125rpx;
|
||||
padding: 0 56rpx;
|
||||
@@ -1503,6 +1534,9 @@ onBeforeUnmount(() => {
|
||||
.sound-btn {
|
||||
width: 76rpx;
|
||||
height: 70rpx;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
@@ -1515,7 +1549,7 @@ onBeforeUnmount(() => {
|
||||
height: 70rpx;
|
||||
}
|
||||
.bat-text-big-box{
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.dao-icon{
|
||||
@@ -1530,11 +1564,13 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
.bat-text-box{
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
.bat-text-small-box{
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
width: auto;
|
||||
width: 100%;
|
||||
min-width: 100rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 16rpx 60rpx 60rpx 16rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1545,6 +1581,15 @@ onBeforeUnmount(() => {
|
||||
font-size: 30rpx;
|
||||
color: #E7BA80;
|
||||
}
|
||||
.training-copy-inline {
|
||||
width: 100%;
|
||||
color: #FFFFFF;
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
line-height: 40rpx;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
}
|
||||
.text1{
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
|
||||
Reference in New Issue
Block a user