添加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

@@ -17,7 +17,12 @@ const room = ref({});
const roomNumber = ref("");
const owner = ref({});
const opponent = ref({});
const players = ref([]);
const players = ref([
{ name: "player1", team: 1 },
{ name: "player2", team: 0 },
{ name: "player3", team: 2 },
{ name: "player4", team: 1 },
]);
const showModal = ref(false);
const battleType = ref(0);
@@ -225,6 +230,8 @@ onHide(() => {});
<view :style="{ display: 'flex', flexDirection: 'column' }">
<text :style="{ color: '#fed847' }">弓箭手们人都到齐了吗?</text>
<text v-if="battleType === 1">1v1比赛即将开始! </text>
<text v-if="battleType === 3">2v2比赛即将开始! </text>
<text v-if="battleType === 4">3v3比赛即将开始! </text>
<text v-if="battleType === 2">大乱斗即将开始! </text>
</view>
<view @click="setClipboardData">复制房间号</view>
@@ -263,6 +270,41 @@ onHide(() => {});
:total="room.count || 10"
:players="players"
/>
<view class="all-players">
<image
src="https://static.shelingxingqiu.com/attachment/2025-08-13/dc0x1p59iab6cvbhqc.png"
mode="widthFix"
/>
<image src="../static/title-2v2.png" mode="widthFix" />
<view>
<view v-for="(item, index) in players" :key="index">
<Avatar :src="item.avatar" :size="36" />
<text v-if="index === 0">创建者</text>
</view>
</view>
</view>
<view class="choose-side">
<view>
<view v-for="i in 3" :key="i" class="choose-side-left-item">
<button hover-class="none">
<image src="../static/close-grey.png" mode="widthFix" />
</button>
<text class="truncate">23232323232</text>
<Avatar :size="36" />
</view>
</view>
<view>
<view v-for="i in 3" :key="i" class="choose-side-right-item">
<button hover-class="none">
<image src="../static/add-grey.png" mode="widthFix" />
</button>
<text class="truncate">22222</text>
<button hover-class="none">
<image src="../static/close-grey.png" mode="widthFix" />
</button>
</view>
</view>
</view>
<view>
<SButton
v-if="user.id === owner.id && battleType === 1"
@@ -408,4 +450,144 @@ onHide(() => {});
border-radius: 20px;
position: relative;
}
.all-players {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
height: 83vw;
}
.all-players > image:first-child {
position: absolute;
width: 100%;
}
.all-players > image:nth-child(2) {
width: 25vw;
position: relative;
}
.all-players > view {
position: relative;
width: 42vw;
height: 42vw;
margin-top: 7vw;
}
.all-players > view > view {
position: absolute;
left: 50%;
top: 50%;
}
/* 4个头像 - 正方形排列 */
.all-players > view > view:nth-child(1):nth-last-child(4) {
transform: translate(-50%, -50%) rotate(-30deg) translateY(-21.5vw)
rotate(30deg);
}
.all-players > view > view:nth-child(2):nth-last-child(3) {
transform: translate(-50%, -50%) rotate(-120deg) translateY(-21.5vw)
rotate(120deg);
}
.all-players > view > view:nth-child(3):nth-last-child(2) {
transform: translate(-50%, -50%) rotate(-210deg) translateY(-21.5vw)
rotate(210deg);
}
.all-players > view > view:nth-child(4):nth-last-child(1) {
transform: translate(-50%, -50%) rotate(-300deg) translateY(-21.5vw)
rotate(300deg);
}
/* 6个头像 - 六边形排列 */
.all-players > view > view:nth-child(1):nth-last-child(6) {
transform: translate(-50%, -50%) rotate(-30deg) translateY(-21vw)
rotate(30deg);
}
.all-players > view > view:nth-child(2):nth-last-child(5) {
transform: translate(-50%, -50%) rotate(-90deg) translateY(-21vw)
rotate(90deg);
}
.all-players > view > view:nth-child(3):nth-last-child(4) {
transform: translate(-50%, -50%) rotate(-150deg) translateY(-21vw)
rotate(150deg);
}
.all-players > view > view:nth-child(4):nth-last-child(3) {
transform: translate(-50%, -50%) rotate(-210deg) translateY(-21vw)
rotate(210deg);
}
.all-players > view > view:nth-child(5):nth-last-child(2) {
transform: translate(-50%, -50%) rotate(-270deg) translateY(-21vw)
rotate(270deg);
}
.all-players > view > view:nth-child(6):nth-last-child(1) {
transform: translate(-50%, -50%) rotate(-330deg) translateY(-21vw)
rotate(330deg);
}
.all-players > view > view > text {
position: absolute;
background-color: #fed847;
font-size: 8px;
border-radius: 10px;
padding: 1px 0px;
bottom: -20%;
left: calc(50% - 15px);
width: 30px;
text-align: center;
}
.choose-side {
display: flex;
}
.choose-side > view {
width: 50%;
}
.choose-side > view:first-child > view {
background: linear-gradient(270deg, #6a1212 0%, rgba(74, 0, 0, 0) 100%);
}
.choose-side > view:last-child > view {
background: linear-gradient(270deg, rgba(13, 0, 74, 0) 0%, #172a86 100%);
}
.choose-side-left-item,
.choose-side-right-item {
display: flex;
align-items: center;
color: #fff;
border-radius: 12px;
padding: 10px;
align-items: center;
margin: 10px 5px;
position: relative;
}
.choose-side-left-item {
justify-content: flex-end;
}
.choose-side-left-item > text,
.choose-side-right-item > text {
margin: 10px;
max-width: 100px;
}
.choose-side-left-item > button:first-child,
.choose-side-right-item > button:last-child {
position: absolute;
top: 0;
}
.choose-side-left-item > button:first-child > image,
.choose-side-right-item > button:last-child > image {
width: 28px;
}
.choose-side-left-item > button:first-child {
left: 0;
}
.choose-side-right-item > button:last-child {
right: 0;
}
.choose-side-left-item > button:last-child,
.choose-side-right-item > button:first-child {
background-color: #fff;
border-radius: 50%;
width: 38px;
height: 38px;
display: flex;
justify-content: center;
align-items: center;
}
.choose-side-left-item > button:last-child > image,
.choose-side-right-item > button:first-child > image {
width: 18px;
}
</style>

View File

@@ -117,6 +117,7 @@ const onCreateRoom = async () => {
src="https://static.shelingxingqiu.com/attachment/2025-07-15/dbcejys872iyun92h6.png"
mode="widthFix"
/>
<image src="../static/room-notfound-title.png" mode="widthFix" />
<view>
<image :src="user.avatar" mode="widthFix" />
<image src="../static/versus.png" mode="widthFix" />
@@ -190,39 +191,43 @@ const onCreateRoom = async () => {
position: absolute;
width: 100%;
}
.create-room > view {
margin: 0 30px;
padding-top: 12%;
.create-room > image:nth-of-type(2) {
padding: 15px;
width: 25vw;
position: relative;
}
.create-room > view:nth-child(3) {
margin: 12vw auto;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.create-room > view > image:first-child {
width: 18vw;
transform: translateX(30%);
width: 19vw;
transform: translateY(-60%);
border-radius: 50%;
position: relative;
}
.create-room > view > image:nth-child(2) {
width: 36vw;
transform: translateX(55%) translateY(-40px);
width: 37vw;
position: relative;
}
.create-room > view > view:nth-child(3) {
width: 18vw;
height: 18vw;
position: relative;
width: 19vw;
height: 19vw;
border-radius: 50%;
background-color: #ccc;
display: flex;
justify-content: center;
align-items: center;
transform: translate(290%, -75px);
transform: translateY(60%);
}
.create-room > view > view:nth-child(3) > image {
width: 20px;
margin-right: 2px;
}
.create-room > view:last-child {
transform: translateY(-100%);
}
.warnning {
width: 100%;
height: 100%;

146
src/pages/team-battle.vue Normal file
View File

@@ -0,0 +1,146 @@
<script setup>
import { ref, onMounted, onUnmounted, nextTick } from "vue";
import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import BattleHeader from "@/components/BattleHeader.vue";
import BowTarget from "@/components/BowTarget.vue";
import ShootProgress from "@/components/ShootProgress.vue";
import PlayersRow from "@/components/PlayersRow.vue";
import Timer from "@/components/Timer.vue";
import BattleFooter from "@/components/BattleFooter.vue";
import ScreenHint from "@/components/ScreenHint.vue";
import SButton from "@/components/SButton.vue";
import RoundEndTip from "@/components/RoundEndTip.vue";
import TestDistance from "@/components/TestDistance.vue";
import TeamAvatars from "@/components/TeamAvatars.vue";
import ShootProgress2 from "@/components/ShootProgress2.vue";
import { getCurrentGameAPI } from "@/apis";
import { isGameEnded } from "@/util";
import { MESSAGETYPES, roundsName } from "@/constants";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user } = storeToRefs(store);
const start = ref(true);
const battleId = ref("");
const currentRound = ref(1);
const currentRedPoint = ref(0);
const currentBluePoint = ref(0);
const totalRounds = ref(0);
const power = ref(0);
const scores = ref([]);
const blueScores = ref([]);
const redTeam = ref([]);
const blueTeam = ref([
{
name: "选手1",
},
{
name: "选手2",
},
{
name: "选手3",
},
]);
const currentShooterId = ref(0);
const tips = ref("即将开始...");
const roundResults = ref([]);
const redPoints = ref(0);
const bluePoints = ref(0);
const showRoundTip = ref(false);
const isFinalShoot = ref(false);
const isEnded = ref(false);
const onBack = () => {
uni.$showHint(2);
};
function onReceiveMessage() {}
onLoad(async (options) => {
if (options.battleId) {
battleId.value = options.battleId;
redTeam.value = uni.getStorageSync("red-team");
blueTeam.value = uni.getStorageSync("blue-team");
const battleInfo = uni.getStorageSync("current-battle");
if (battleInfo) {
await nextTick(() => {
recoverData(battleInfo);
});
setTimeout(getCurrentGameAPI, 2000);
}
}
});
onMounted(() => {
uni.setKeepScreenOn({
keepScreenOn: true,
});
uni.$on("socket-inbox", onReceiveMessage);
});
onUnmounted(() => {
uni.setKeepScreenOn({
keepScreenOn: false,
});
uni.$off("socket-inbox", onReceiveMessage);
});
</script>
<template>
<Container :bgType="3" :onBack="onBack">
<view class="container">
<BattleHeader v-if="!start" :redTeam="redTeam" :blueTeam="blueTeam" />
<TestDistance v-if="!start" :guide="false" />
<view class="players-row">
<TeamAvatars :team="blueTeam" :isRed="false" :youTurn="true" />
<ShootProgress2 :tips="tips" />
<TeamAvatars :team="blueTeam" :youTurn="true" />
</view>
<BowTarget
v-if="start"
mode="team"
:power="start ? power : 0"
:scores="scores"
:blueScores="blueScores"
/>
<BattleFooter
v-if="start"
:roundResults="roundResults"
:redPoints="redPoints"
:bluePoints="bluePoints"
/>
<Timer v-if="!start" />
<ScreenHint
:show="showRoundTip"
:onClose="() => (showRoundTip = false)"
:mode="isFinalShoot ? 'tall' : 'normal'"
>
<RoundEndTip
v-if="showRoundTip"
:isFinal="isFinalShoot"
:round="currentRound - 1"
:bluePoint="currentBluePoint"
:redPoint="currentRedPoint"
:roundData="
roundResults[roundResults.length - 2]
? roundResults[roundResults.length - 2]
: []
"
:onAutoClose="() => (showRoundTip = false)"
/>
</ScreenHint>
</view>
</Container>
</template>
<style scoped>
.container {
width: 100%;
height: 100%;
}
.players-row {
display: flex;
align-items: center;
justify-content: space-around;
margin-bottom: -7vw;
margin-top: -3vw;
}
</style>