添加2v2页面

This commit is contained in:
kron
2025-08-13 16:44:25 +08:00
parent 5709035d4b
commit f388270fff
18 changed files with 779 additions and 54 deletions

View File

@@ -37,6 +37,12 @@ onMounted(() => {
src="../static/app-bg3.png"
:style="{ height: capsuleHeight + 'px' }"
/>
<image
class="bg-image"
v-if="type === 3"
src="../static/app-bg4.png"
mode="widthFix"
/>
<view class="bg-overlay" v-if="type === 0"></view>
</view>
</template>

View File

@@ -1,4 +1,6 @@
<script setup>
import BowPower from "@/components/BowPower.vue";
import { RoundImages } from "@/constants";
defineProps({
roundResults: {
type: Array,
@@ -17,6 +19,12 @@ defineProps({
<template>
<view class="container">
<view class="guide-row">
<image src="../static/shooter.png" mode="widthFix" />
<view :style="{ marginBottom: '10px', transform: 'scale(0.8) translateX(10px)' }">
<BowPower :power="20" />
</view>
</view>
<view>
<image src="../static/battle-header-melee.png" mode="widthFix" />
<text>蓝队({{ bluePoints }})</text>
@@ -25,10 +33,7 @@ defineProps({
<view class="players">
<view>
<view v-for="(result, index) in roundResults" :key="index">
<text
>{{ index > 4 ? "决金箭" : "Round" }}
{{ index > 4 ? `R${index - 4}` : index + 1 }}</text
>
<image :src="RoundImages[`round${index + 1}`]" mode="widthFix" />
<view>
<text>{{
result.blueArrows.length
@@ -42,9 +47,9 @@ defineProps({
</view>
<block v-if="roundResults.length < 3">
<view v-for="i in 3 - roundResults.length" :key="i">
<text>Round</text>
<image :src="RoundImages[`round${i}`]" mode="widthFix" />
<view>
<text></text>
<text>{{ i }}</text>
<text></text>
</view>
</view>
@@ -52,10 +57,7 @@ defineProps({
</view>
<view>
<view v-for="(result, index) in roundResults" :key="index">
<text
>{{ index > 4 ? "决金箭" : "Round" }}
{{ index > 4 ? `R${index - 4}` : index + 1 }}</text
>
<image :src="RoundImages[`round${index + 1}`]" mode="widthFix" />
<view>
<text>{{
result.redArrows.length
@@ -69,7 +71,7 @@ defineProps({
</view>
<block v-if="roundResults.length < 3">
<view v-for="i in 3 - roundResults.length" :key="i">
<text>Round</text>
<image :src="RoundImages[`round${i}`]" mode="widthFix" />
<view>
<text></text>
<text></text>
@@ -84,31 +86,25 @@ defineProps({
<style scoped>
.container {
width: 100%;
margin-top: 20px;
overflow: hidden;
margin-top: -40px;
}
.container > view:first-child {
.container > view:nth-child(2) {
position: relative;
display: flex;
justify-content: space-around;
align-items: center;
font-size: 13px;
}
.container > view:first-child > image:first-child {
.container > view:nth-child(2) > image:first-child {
position: absolute;
width: 100%;
top: -6px;
}
.container > view:first-child > text {
.container > view:nth-child(2) > text {
z-index: 1;
margin-top: 2px;
}
.container > view:first-child > text:nth-child(2) {
color: #364469;
}
.container > view:first-child > text:nth-child(3) {
color: #692735;
}
.players {
display: flex;
}
@@ -121,35 +117,42 @@ defineProps({
color: #fff;
padding-top: 5px;
}
.players > view:first-child {
background-color: #364469;
.players > view:first-child > view {
background: linear-gradient(270deg, #172a86 0%, #0006 100%);
}
.players > view:last-child {
background-color: #692735;
.players > view:last-child > view {
background: linear-gradient(270deg, #0006 0%, #6a1212 100%);
}
.players > view > view {
min-height: 28px;
width: 85%;
padding: 5px 20px;
min-height: 25px;
width: calc(100% - 40px);
padding: 2px 20px;
margin-bottom: 5px;
background-image: url("../static/row-bg.png");
background-size: 90% 100%;
background-repeat: no-repeat;
background-position: center;
display: flex;
justify-content: space-between;
align-items: center;
}
.players > view > view > text {
font-size: 14px;
.players > view > view > image:first-child {
width: 72px;
}
.players > view > view > view:last-child {
font-size: 14px;
padding-right: 20px;
font-size: 10px;
}
.players > view > view > view:last-child > text:first-child {
font-size: 20px;
font-size: 16px;
color: #fed847;
margin-right: 5px;
}
.guide-row {
display: flex;
justify-content: space-between;
align-items: flex-end;
padding: 0 5vw;
margin-bottom: -4px;
z-index: 2;
position: relative;
}
.guide-row > image {
width: 18%;
}
</style>

View File

@@ -18,11 +18,11 @@ defineProps({
.container {
display: flex;
align-items: center;
color: #b9b9b9;
color: #ffffffa8;
font-size: 13px;
}
.container > image {
width: 22px;
width: 20px;
margin-right: 5px;
}
</style>

View File

@@ -1,5 +1,6 @@
<script setup>
import { ref, onMounted, onUnmounted } from "vue";
import HeaderProgress from "@/components/HeaderProgress.vue";
const props = defineProps({
title: {
@@ -24,6 +25,7 @@ const onClick = () => {
const loading = ref(false);
const showLoader = ref(false);
const pointBook = ref(null);
const showProgress = ref(false);
const updateLoading = (value) => {
loading.value = value;
};
@@ -42,6 +44,9 @@ onMounted(() => {
showLoader.value = true;
}
uni.$on("update-header-loading", updateLoading);
if (currentPage.route === "pages/team-battle") {
showProgress.value = true;
}
});
onUnmounted(() => {
uni.$off("update-header-loading", updateLoading);
@@ -110,6 +115,9 @@ onUnmounted(() => {
}}</text
>
</view>
<view v-if="showProgress" class="battle-progress">
<HeaderProgress />
</view>
</view>
</template>
@@ -176,4 +184,11 @@ onUnmounted(() => {
padding: 5px 10px;
margin: 3px;
}
.battle-progress {
position: fixed;
width: 60%;
left: 20%;
display: flex;
justify-content: center;
}
</style>

View File

@@ -0,0 +1,134 @@
<script setup>
import { ref, watch, onMounted, onUnmounted } from "vue";
import audioManager from "@/audioManager";
import { MESSAGETYPES } from "@/constants";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user } = storeToRefs(store);
const tips = ref("请蓝队射击");
const battleId = ref("");
const isMelee = ref("");
const timer = ref(null);
const sound = ref(true);
const currentSound = ref("");
const currentRound = ref(1);
const totalRound = ref(6);
const currentRoundEnded = ref(false);
const ended = ref(false);
const halfTime = ref(false);
// watch(
// () => props.tips,
// (newVal) => {
// let key = "";
// if (newVal.includes("红队")) key = "请红方射击";
// if (newVal.includes("蓝队")) key = "请蓝方射击";
// if (key && sound.value) {
// if (currentRoundEnded.value) {
// currentRound.value += 1;
// currentRoundEnded.value = false;
// if (currentRound.value === 1) audioManager.play("第一轮");
// if (currentRound.value === 2) audioManager.play("第二轮");
// if (currentRound.value === 3) audioManager.play("第三轮");
// if (currentRound.value === 4) audioManager.play("第四轮");
// if (currentRound.value === 5) audioManager.play("第五轮");
// setTimeout(() => {
// audioManager.play(key);
// }, 1000);
// } else {
// audioManager.play(key);
// }
// }
// }
// );
const updateSound = () => {
sound.value = !sound.value;
if (!sound.value) audioManager.stop(currentSound.value);
};
async function onReceiveMessage(messages = []) {
if (!sound.value || ended.value) return;
messages.forEach((msg) => {
if (
(battleId.value && msg.constructor === MESSAGETYPES.ShootResult) ||
(!battleId.value && msg.constructor === MESSAGETYPES.ShootSyncMeArrowID)
) {
if (melee.value && msg.userId !== user.value.id) return;
if (!halfTime.value && msg.target) {
currentSound.value = msg.target.ring
? `${msg.target.ring}`
: "未上靶";
audioManager.play(currentSound.value);
}
} else if (msg.constructor === MESSAGETYPES.AllReady) {
audioManager.play("比赛开始");
} else if (msg.constructor === MESSAGETYPES.MeleeAllReady) {
halfTime.value = false;
audioManager.play("比赛开始");
} else if (msg.constructor === MESSAGETYPES.CurrentRoundEnded) {
currentRoundEnded.value = true;
} else if (msg.constructor === MESSAGETYPES.HalfTimeOver) {
halfTime.value = true;
audioManager.play("中场休息");
} else if (msg.constructor === MESSAGETYPES.MatchOver) {
audioManager.play("比赛结束");
} else if (msg.constructor === MESSAGETYPES.FinalShoot) {
audioManager.play("决金箭轮");
} else if (msg.constructor === MESSAGETYPES.ShootSyncMePracticeID) {
ended.value = true;
} else if (msg.constructor === MESSAGETYPES.MatchOver) {
ended.value = true;
}
});
}
const playSound = (key) => {
currentSound.value = key;
audioManager.play(key);
};
onMounted(() => {
uni.$on("socket-inbox", onReceiveMessage);
uni.$on("play-sound", playSound);
});
onUnmounted(() => {
uni.$off("socket-inbox", onReceiveMessage);
uni.$off("play-sound", playSound);
if (timer.value) clearInterval(timer.value);
});
</script>
<template>
<view class="container">
<text>{{ tips }}</text>
<text>({{ currentRound }}/{{ totalRound }})</text>
<button hover-class="none" @click="updateSound">
<image
:src="`../static/sound${sound ? '' : '-off'}-yellow.png`"
mode="widthFix"
/>
</button>
</view>
</template>
<style scoped>
.container {
width: 50vw;
color: #fed847;
display: flex;
align-items: center;
justify-content: center;
}
.container > button:last-child {
width: 36px;
height: 36px;
}
.container > button:last-child > image {
width: 36px;
min-height: 36px;
}
</style>

View File

@@ -0,0 +1,115 @@
<script setup>
import { ref, watch, onMounted, onUnmounted } from "vue";
import { RoundGoldImages } from "@/constants";
const props = defineProps({
tips: {
type: String,
default: "",
},
total: {
type: Number,
default: 15,
},
currentRound: {
type: String,
default: "round1",
},
});
const barColor = ref("linear-gradient( 180deg, #FFA0A0 0%, #FF6060 100%)");
const remain = ref(10);
const timer = ref(null);
watch(
() => props.tips,
(newVal) => {
if (newVal.includes("红队"))
barColor.value = "linear-gradient( 180deg, #FFA0A0 0%, #FF6060 100%)";
if (newVal.includes("蓝队"))
barColor.value = "linear-gradient( 180deg, #9AB3FF 0%, #4288FF 100%)";
if (newVal.includes("红队") || newVal.includes("蓝队")) {
if (timer.value) clearInterval(timer.value);
remain.value = props.total;
timer.value = setInterval(() => {
if (remain.value > 0) remain.value--;
}, 1000);
}
console.log(barColor.value);
},
{
immediate: true,
}
);
const updateRemain = (value) => {
if (timer.value) clearInterval(timer.value);
remain.value = Math.round(value);
if (remain.value > 0) {
timer.value = setInterval(() => {
if (remain.value > 0) remain.value--;
}, 1000);
}
};
onMounted(() => {
uni.$on("update-ramain", updateRemain);
});
onUnmounted(() => {
uni.$off("update-ramain", updateRemain);
if (timer.value) clearInterval(timer.value);
});
</script>
<template>
<view class="container">
<image :src="RoundGoldImages[props.currentRound]" mode="widthFix" />
<view>
<view
:style="{
width: `${(remain / total) * 100}%`,
background: barColor,
right: tips.includes('红队') ? 0 : 'unset',
}"
/>
<text>剩余{{ remain }}</text>
</view>
</view>
</template>
<style scoped>
.container {
width: 50vw;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 12vw;
}
.container > image {
width: 100%;
transform: translateY(7px);
}
.container > view:last-child {
width: 100%;
text-align: center;
background-color: #444444;
border-radius: 20px;
font-size: 12px;
height: 15px;
line-height: 15px;
position: relative;
overflow: hidden;
}
.container > view:last-child > view {
position: absolute;
height: 15px;
border-radius: 15px;
transition: all 1s linear;
}
.container > view:last-child > text {
font-size: 10px;
line-height: 15px;
color: #fff;
position: relative;
}
</style>

View File

@@ -0,0 +1,94 @@
<script setup>
import { ref, watch } from "vue";
const props = defineProps({
isRed: {
type: Boolean,
default: true,
},
team: {
type: Array,
default: () => [],
},
youTurn: {
type: Boolean,
default: false,
},
});
const players = ref(props.team);
watch(
() => props.youTurn,
(newVal) => {
players.value = props.team;
}
);
</script>
<template>
<view class="container">
<view
v-for="(item, index) in team"
:key="index"
class="player"
:style="{
width: (youTurn ? 40 - index * 5 : 30) + 'px',
height: (youTurn ? 40 - index * 5 : 30) + 'px',
borderColor: isRed ? '#ff6060' : '#5fadff',
zIndex: players.length - index,
top: youTurn ? index * 2 + 'px' : '6px',
left: (isRed ? index * 20 : 35 - index * 15) + 'px',
}"
>
<image :src="item.avatar || '../static/user-icon.png'" mode="widthFix" />
<text
v-if="youTurn && index === 0"
:style="{ backgroundColor: isRed ? '#ff6060' : '#5fadff' }"
>{{ isRed ? "红队" : "蓝队" }}</text
>
</view>
<text
v-if="youTurn"
class="truncate"
:style="{
color: isRed ? '#ff6060' : '#5fadff',
[isRed ? 'left' : 'right']: 0,
}"
>{{ team[0].name }}</text
>
</view>
</template>
<style scoped>
.container {
display: flex;
align-items: center;
position: relative;
width: 20vw;
height: 45px;
}
.container > text {
position: absolute;
font-size: 10px;
text-align: center;
width: 40px;
bottom: -12px;
}
.player {
transition: all 0.3s ease;
position: absolute;
border-radius: 50%;
overflow: hidden;
border: 1px solid;
}
.player > image {
width: 100%;
}
.player > text {
position: absolute;
font-size: 8px;
text-align: center;
width: 40px;
left: 0px;
bottom: 0px;
color: #fff;
}
</style>