update:对接个人训练改版
This commit is contained in:
@@ -27,6 +27,14 @@ const props = defineProps({
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
trainingType: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
difficultyLevel: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
result: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
@@ -60,12 +68,6 @@ function onClickShare() {
|
||||
uni.$emit("share-image");
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (props.result.lvl > user.value.lvl) {
|
||||
showUpgrade.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
const details = computed(() => props.result.details || []);
|
||||
|
||||
const arrows = computed(() => {
|
||||
@@ -81,25 +83,89 @@ const totalRing = computed(() =>
|
||||
details.value.reduce((last, next) => last + (Number(next.ring) || 0), 0)
|
||||
);
|
||||
|
||||
const gainedExp = computed(
|
||||
() => props.result.exp || props.result.experience || validArrows.value
|
||||
const hasResultValue = (...keys) =>
|
||||
keys.some((key) => {
|
||||
const value = props.result[key];
|
||||
return value !== undefined && value !== null && value !== "";
|
||||
});
|
||||
|
||||
const readResultNumber = (keys, fallback = 0) => {
|
||||
for (const key of keys) {
|
||||
const value = props.result[key];
|
||||
if (value === undefined || value === null || value === "") continue;
|
||||
const numberValue = Number(value);
|
||||
if (Number.isFinite(numberValue)) return numberValue;
|
||||
}
|
||||
return fallback;
|
||||
};
|
||||
|
||||
const beforeExp = computed(() =>
|
||||
readResultNumber(["beforeExp", "before_exp"])
|
||||
);
|
||||
|
||||
const currentLevel = computed(
|
||||
() => props.result.lvl || user.value.lvl || user.value.rankLvl || 1
|
||||
);
|
||||
const currentExp = computed(() => {
|
||||
const userScores = Number(user.value.scores);
|
||||
return readResultNumber(
|
||||
["currentExp", "current_exp", "score"],
|
||||
Number.isFinite(userScores) ? userScores : 0
|
||||
);
|
||||
});
|
||||
|
||||
const currentExp = computed(
|
||||
() => props.result.currentExp || props.result.score || user.value.scores || 0
|
||||
);
|
||||
// 新版练习结算返回练习前后累计经验,本局经验由两者相减得到。
|
||||
const gainedExp = computed(() => {
|
||||
if (
|
||||
hasResultValue("beforeExp", "before_exp") &&
|
||||
hasResultValue("currentExp", "current_exp")
|
||||
) {
|
||||
return Math.max(0, currentExp.value - beforeExp.value);
|
||||
}
|
||||
return Math.max(0, readResultNumber(["exp", "experience"]));
|
||||
});
|
||||
|
||||
const nextExp = computed(
|
||||
() => props.result.nextExp || props.result.upgradeScore || 100
|
||||
const beforeLevel = computed(() => {
|
||||
const currentUserLevel = Number(user.value.lvl);
|
||||
return readResultNumber(
|
||||
["beforeLevel", "before_level"],
|
||||
Number.isFinite(currentUserLevel) ? currentUserLevel : 0
|
||||
);
|
||||
});
|
||||
|
||||
const userLevel = computed(() => {
|
||||
const fallbackLevel = Number(user.value.lvl ?? user.value.rankLvl ?? 1);
|
||||
const level = readResultNumber(
|
||||
["level", "lvl"],
|
||||
Number.isFinite(fallbackLevel) ? fallbackLevel : 1
|
||||
);
|
||||
return Math.max(1, Math.trunc(level));
|
||||
});
|
||||
|
||||
const resultDifficultyLevel = computed(() => {
|
||||
const level = Number(props.difficultyLevel);
|
||||
return Number.isInteger(level) && level > 0 ? level : "--";
|
||||
});
|
||||
|
||||
const upgradeExp = computed(() =>
|
||||
Math.max(
|
||||
0,
|
||||
readResultNumber(
|
||||
["upgradeExp", "upgrade_exp", "nextExp", "upgradeScore"],
|
||||
100
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const expPercent = computed(() => {
|
||||
if (!nextExp.value) return 0;
|
||||
return Math.min(100, Math.max(0, (currentExp.value / nextExp.value) * 100));
|
||||
if (!upgradeExp.value) return 0;
|
||||
return Math.min(
|
||||
100,
|
||||
Math.max(0, (currentExp.value / upgradeExp.value) * 100)
|
||||
);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (userLevel.value > beforeLevel.value) {
|
||||
showUpgrade.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
const findValue = (...keys) => {
|
||||
@@ -108,83 +174,184 @@ const findValue = (...keys) => {
|
||||
};
|
||||
|
||||
const formatDuration = (value) => {
|
||||
const seconds = Number(value || 0);
|
||||
if (!seconds) return "--";
|
||||
const valueNumber = Number(value);
|
||||
const seconds = Number.isFinite(valueNumber)
|
||||
? Math.max(0, Math.round(valueNumber))
|
||||
: 0;
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const rest = seconds % 60;
|
||||
return minutes ? `${minutes}分${rest}秒` : `${rest}秒`;
|
||||
};
|
||||
|
||||
const usedTime = computed(() =>
|
||||
findValue("duration", "usedTime", "shootTime", "time")
|
||||
const formatMetricNumber = (value) => {
|
||||
const valueNumber = Number(value);
|
||||
if (!Number.isFinite(valueNumber)) return "0";
|
||||
return String(Number(valueNumber.toFixed(2)));
|
||||
};
|
||||
|
||||
const readMetricNumber = (keys, fallback = 0) => {
|
||||
const value = findValue(...keys);
|
||||
const valueNumber = Number(value);
|
||||
return Number.isFinite(valueNumber) ? valueNumber : fallback;
|
||||
};
|
||||
|
||||
const resultTrainingType = computed(
|
||||
() => props.result.trainingType || props.trainingType || "precision"
|
||||
);
|
||||
|
||||
const hitCompare = computed(
|
||||
() => Number(findValue("hitCompare", "hitDiff", "hitDelta") || 0)
|
||||
const metricConfigs = {
|
||||
base: [
|
||||
{
|
||||
label: "平均环数",
|
||||
valueKeys: ["averageRing", "average_ring"],
|
||||
unit: "环",
|
||||
deltaKeys: ["deltaAverageRing", "delta_average_ring"],
|
||||
deltaUnit: "环",
|
||||
},
|
||||
{
|
||||
label: "稳定性",
|
||||
valueKeys: ["stability"],
|
||||
unit: "",
|
||||
deltaKeys: ["deltaStability", "delta_stability"],
|
||||
deltaUnit: "",
|
||||
},
|
||||
],
|
||||
rhythm: [
|
||||
{
|
||||
label: "最高连击次数",
|
||||
valueKeys: ["maxCombo", "max_combo"],
|
||||
unit: "连",
|
||||
deltaKeys: ["deltaMaxCombo", "delta_max_combo"],
|
||||
deltaUnit: "连",
|
||||
},
|
||||
{
|
||||
label: "共命中环数",
|
||||
valueKeys: ["currentRings", "current_rings"],
|
||||
unit: "环",
|
||||
deltaKeys: ["deltaTotalRings", "delta_total_rings"],
|
||||
deltaUnit: "环",
|
||||
},
|
||||
],
|
||||
endurance: [
|
||||
{
|
||||
label: "完成箭数",
|
||||
valueKeys: ["totalArrows", "total_arrows"],
|
||||
unit: "支",
|
||||
deltaKeys: ["deltaTotalArrows", "delta_total_arrows"],
|
||||
deltaUnit: "支",
|
||||
},
|
||||
{
|
||||
label: "命中环数",
|
||||
valueKeys: ["currentRings", "current_rings"],
|
||||
unit: "环",
|
||||
deltaKeys: ["deltaTotalRings", "delta_total_rings"],
|
||||
deltaUnit: "环",
|
||||
},
|
||||
],
|
||||
precision: [
|
||||
{
|
||||
label: "共命中目标",
|
||||
valueKeys: ["totalHits", "total_hits"],
|
||||
fallback: () => 0,
|
||||
unit: "次",
|
||||
deltaKeys: [
|
||||
"deltaTotalHits",
|
||||
"delta_total_hits",
|
||||
"hitCompare",
|
||||
"hitDiff",
|
||||
"hitDelta",
|
||||
],
|
||||
deltaUnit: "次",
|
||||
},
|
||||
{
|
||||
label: "用时",
|
||||
valueKeys: ["duration", "usedTime", "shootTime", "time"],
|
||||
unit: "",
|
||||
deltaKeys: [
|
||||
"deltaDuration",
|
||||
"delta_duration",
|
||||
"timeCompare",
|
||||
"timeDiff",
|
||||
"durationDiff",
|
||||
],
|
||||
deltaUnit: "",
|
||||
duration: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const resultRows = computed(() => {
|
||||
const configs = metricConfigs[resultTrainingType.value] || metricConfigs.precision;
|
||||
return configs.map((config) => {
|
||||
const fallback = config.fallback ? config.fallback() : 0;
|
||||
const value = readMetricNumber(config.valueKeys, fallback);
|
||||
const delta = readMetricNumber(config.deltaKeys);
|
||||
const formatter = config.duration ? formatDuration : formatMetricNumber;
|
||||
|
||||
return {
|
||||
...config,
|
||||
valueText: formatter(value),
|
||||
delta,
|
||||
deltaText: formatter(Math.abs(delta)),
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const advancesDifficulty = computed(() =>
|
||||
["base", "endurance"].includes(resultTrainingType.value)
|
||||
);
|
||||
const primaryText = computed(() =>
|
||||
advancesDifficulty.value ? "下一难度" : "再来一次"
|
||||
);
|
||||
|
||||
const timeCompare = computed(
|
||||
() => Number(findValue("timeCompare", "timeDiff", "durationDiff") || 0)
|
||||
);
|
||||
const handlePrimary = () => {
|
||||
if (advancesDifficulty.value) {
|
||||
closePanel();
|
||||
return;
|
||||
}
|
||||
retryPractice();
|
||||
};
|
||||
|
||||
const calories = computed(
|
||||
() => Number(findValue("calories", "calorie", "kcal") || 0)
|
||||
() => formatMetricNumber(readMetricNumber(["calories", "calorie", "kcal"]))
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view :class="['result-mask', showPanel ? 'result-mask--show' : 'result-mask--hide']">
|
||||
<image class="hero-glow" src="/static/training-difficulty-design/result-bg.png" mode="widthFix" />
|
||||
<image class="hero-glow" src="../static/training-difficulty-design/result-bg.png" mode="widthFix" />
|
||||
<view class="result-title">
|
||||
<image class="result-title-bg" src="/static/training-difficulty-design/result-t-bg.png" mode="widthFix" />
|
||||
<view class="result-title-text">Lv{{ currentLevel }}</view>
|
||||
<image class="result-title-bg" src="../static/training-difficulty-design/result-t-bg.png" mode="widthFix" />
|
||||
<view class="result-title-text">Lv{{ resultDifficultyLevel }}</view>
|
||||
</view>
|
||||
|
||||
<view class="result-panel">
|
||||
<view class="line-top"></view>
|
||||
<view class="line-bottom"></view>
|
||||
<view class="stats">
|
||||
<view class="stat-row">
|
||||
<image class="stat-bg" src="/static/training-difficulty-design/result-c-bg.png" mode="scaleToFill" />
|
||||
<view v-for="row in resultRows" :key="row.label" class="stat-row">
|
||||
<image class="stat-bg" src="../static/training-difficulty-design/result-c-bg.png" mode="scaleToFill" />
|
||||
<view class="stat-cell">
|
||||
<text class="stat-label">共命中目标</text>
|
||||
<text class="stat-label">{{ row.label }}</text>
|
||||
<view class="stat-value">
|
||||
<text>{{ validArrows }}</text>
|
||||
<text class="stat-unit">次</text>
|
||||
<text>{{ row.valueText }}</text>
|
||||
<text v-if="row.unit" class="stat-unit">{{ row.unit }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-cell stat-cell--compare">
|
||||
<text class="stat-label">对比上次</text>
|
||||
<view class="stat-value">
|
||||
<text>{{ Math.abs(hitCompare) }}</text>
|
||||
<text class="stat-unit">次</text>
|
||||
<image class="trend-icon" :class="{ 'trend-icon--down': hitCompare < 0 }"
|
||||
src="/static/training-difficulty-design/result-up.png" mode="widthFix" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="stat-row">
|
||||
<image class="stat-bg" src="/static/training-difficulty-design/result-c-bg.png" mode="scaleToFill" />
|
||||
<view class="stat-cell">
|
||||
<text class="stat-label">用时</text>
|
||||
<view class="stat-value">
|
||||
<text>{{ formatDuration(usedTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stat-divider"></view>
|
||||
<view class="stat-cell stat-cell--compare">
|
||||
<text class="stat-label">对比上次</text>
|
||||
<view class="stat-value">
|
||||
<text>{{ formatDuration(Math.abs(timeCompare)) }}</text>
|
||||
<image class="trend-icon" :class="{ 'trend-icon--down': timeCompare <= 0 }"
|
||||
src="/static/training-difficulty-design/result-up.png" mode="widthFix" />
|
||||
<view v-if="row.delta !== 0" class="stat-value">
|
||||
<text>{{ row.delta > 0 ? "+" : "-" }}{{ row.deltaText }}</text>
|
||||
<text v-if="row.deltaUnit" class="stat-unit">{{ row.deltaUnit }}</text>
|
||||
<image class="trend-icon" :class="{ 'trend-icon--down': row.delta < 0 }"
|
||||
src="../static/training-difficulty-design/result-up.png" mode="widthFix" />
|
||||
</view>
|
||||
<view v-else class="stat-value">--</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="stat-row">
|
||||
<image class="stat-bg" src="/static/training-difficulty-design/result-c-bg.png" mode="scaleToFill" />
|
||||
<image class="stat-bg" src="../static/training-difficulty-design/result-c-bg.png" mode="scaleToFill" />
|
||||
<view class="stat-cell">
|
||||
<text class="stat-label">消耗卡路里</text>
|
||||
<view class="stat-value">
|
||||
@@ -196,7 +363,7 @@ const calories = computed(
|
||||
<view class="stat-cell stat-cell--compare">
|
||||
<view class="stat-value">
|
||||
<image v-for="index in 3" :key="index" class="rice-icon"
|
||||
src="/static/training-difficulty-design/result-rice.png" mode="widthFix" />
|
||||
src="../static/training-difficulty-design/result-rice.png" mode="widthFix" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -204,15 +371,15 @@ const calories = computed(
|
||||
|
||||
<view class="actions">
|
||||
<view class="action-item" @click="() => (showBowData = true)">
|
||||
<image class="action-icon" src="/static/training-difficulty-design/result-icon-1.png" mode="widthFix" />
|
||||
<image class="action-icon" src="../static/training-difficulty-design/result-icon-1.png" mode="widthFix" />
|
||||
<text>查看靶纸</text>
|
||||
</view>
|
||||
<view v-if="validArrows === total" class="action-item" @click="() => (showComment = true)">
|
||||
<image class="action-icon" src="/static/training-difficulty-design/result-icon-2.png" mode="widthFix" />
|
||||
<view class="action-item" @click="() => (showComment = true)">
|
||||
<image class="action-icon" src="../static/training-difficulty-design/result-icon-2.png" mode="widthFix" />
|
||||
<text>教练点评</text>
|
||||
</view>
|
||||
<view v-if="validArrows === total" class="action-item" @click="onClickShare">
|
||||
<image class="action-icon" src="/static/training-difficulty-design/result-icon-3.png" mode="widthFix" />
|
||||
<view class="action-item" @click="onClickShare">
|
||||
<image class="action-icon" src="../static/training-difficulty-design/result-icon-3.png" mode="widthFix" />
|
||||
<text>分享成绩</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -222,20 +389,20 @@ const calories = computed(
|
||||
<view class="exp-area">
|
||||
<text class="exp-gain">+{{ gainedExp }}经验</text>
|
||||
<view class="level-progress">
|
||||
<text class="level-text">LV.{{ currentLevel }}</text>
|
||||
<text class="level-text">LV.{{ userLevel }}</text>
|
||||
<view class="progress-track">
|
||||
<view class="progress-fill" :style="{ width: `${expPercent}%` }"></view>
|
||||
</view>
|
||||
<text class="progress-text">{{ currentExp }} / {{ nextExp }}</text>
|
||||
<text class="progress-text">{{ currentExp }} / {{ upgradeExp }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer-actions">
|
||||
<view class="result-btn result-btn--muted" @click="closePanel">
|
||||
<text>{{ validArrows === total ? "完成" : "返回" }}</text>
|
||||
<text>完成</text>
|
||||
</view>
|
||||
<view class="result-btn result-btn--primary" @click="retryPractice">
|
||||
<text>再来一次</text>
|
||||
<view class="result-btn result-btn--primary" @click="handlePrimary">
|
||||
<text>{{ primaryText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -268,7 +435,7 @@ const calories = computed(
|
||||
</ScreenHint>
|
||||
<BowData :total="arrows.length" :arrows="result.details" :show="showBowData"
|
||||
:onClose="() => (showBowData = false)" />
|
||||
<UserUpgrade :show="showUpgrade" :onClose="() => (showUpgrade = false)" :lvl="result.lvl" />
|
||||
<UserUpgrade :show="showUpgrade" :onClose="() => (showUpgrade = false)" :lvl="userLevel" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user