Files
shoot-miniprograms/src/pages/training/components/ScorePanel.vue
T
2026-08-26 15:23:02 +08:00

185 lines
3.7 KiB
Vue

<script setup>
import { ref, watch } from "vue";
const props = defineProps({
rowCount: {
type: Number,
default: 0,
},
total: {
type: Number,
default: 0,
},
arrows: {
type: Array,
default: () => [],
},
fontSize: {
type: Number,
default: 25,
},
completeEffect: {
type: Boolean,
default: true,
},
trainingType: {
type: String,
default: "",
},
});
const items = ref(new Array(props.total).fill(9));
const bgImages = [
"../static/complete-light1.png",
"../static/complete-light2.png",
];
const getDisplayText = (arrow) => {
if (!arrow) return "-";
if (arrow.ringX) return "X";
return arrow.ring ?? "-";
};
const isLowScore = (arrow) => {
if (!arrow) return false;
if (["rhythm", "stability"].includes(props.trainingType)) {
return arrow.ok !== true;
}
if (arrow.ringX) return false;
const ring = Number(arrow.ring);
return Number.isFinite(ring) && ring < 6;
};
watch(
() => props.total,
(newValue) => {
items.value = new Array(newValue).fill(9);
}
);
</script>
<template>
<view class="container">
<template v-if="total > 0 && arrows.length === total && completeEffect">
<image
v-for="(image, index) in bgImages"
:key="image"
:src="image"
:class="[
'complete-light',
index === 0 ? 'complete-light--first' : 'complete-light--second',
]"
:style="{
width: `calc(${(100 / (rowCount + 2)) * rowCount}vw + ${
(100 / (total * 2)) * (rowCount * 2 + (total === 12 ? 8 : 24))
}px)`,
height: `calc(${(100 / (rowCount + 2)) * (total / rowCount)}vw + ${
(100 / (total * 2)) *
((total / rowCount) * 2 + (total === 12 ? 7 : 24))
}px)`,
top: `${total === 12 ? -2 : -3}vw`,
}"
/>
</template>
<view
v-for="(_, index) in items"
:key="index"
class="score-item"
>
<image
class="score-item-bg"
:src="
isLowScore(arrows[index])
? 'https://static.shelingxingqiu.com/shootmini/static/training-difficulty-design/block-gray.png'
: 'https://static.shelingxingqiu.com/shootmini/static/training-difficulty-design/block-gold.png'
"
/>
<text
class="score-value"
:class="{ 'score-value--low': isLowScore(arrows[index]) }"
>
{{ getDisplayText(arrows[index]) }}
</text>
</view>
</view>
</template>
<style scoped lang="scss">
.container {
width: 100%;
display: flex;
flex-wrap: wrap;
box-sizing: border-box;
position: relative;
padding: 30rpx 40rpx 0 40rpx;
}
.score-item {
position: relative;
width: 100rpx;
height: 56rpx;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
margin-right: 14rpx;
margin-bottom: 14rpx;
}
.score-item:nth-child(6n) {
margin-right: 0;
}
.score-item-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);
}
.complete-light {
position: absolute;
}
.complete-light--first {
animation: complete-light-first 400ms steps(1, end) infinite;
}
.complete-light--second {
animation: complete-light-second 400ms steps(1, end) infinite;
}
@keyframes complete-light-first {
0%,
49.9% {
opacity: 1;
}
50%,
100% {
opacity: 0;
}
}
@keyframes complete-light-second {
0%,
49.9% {
opacity: 0;
}
50%,
100% {
opacity: 1;
}
}
</style>