Files
shoot-miniprograms/src/pages/friend-battle.vue

458 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import { computed, ref } from "vue";
import { onLoad, onShow } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import GuideTwo from "@/components/GuideTwo.vue";
import SButton from "@/components/SButton.vue";
import SModal from "@/components/SModal.vue";
import Signin from "@/components/Signin.vue";
import CreateRoom from "@/components/CreateRoom.vue";
import Avatar from "@/components/Avatar.vue";
import ModalDialog from "@/components/ModalDialog.vue";
import { getRoomAPI, joinRoomAPI, getBattleDataAPI, getDailyCountAPI } from "@/apis";
import { debounce, canEenter, getLimitCountText, isLimitReached } from "@/util";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user, device, online, game, dailyCount } = storeToRefs(store);
const { updateDailyCount } = store;
const showModal = ref(false);
const showSignin = ref(false);
const warnning = ref("");
const roomNumber = ref("");
const data = ref({});
const roomID = ref("");
const loading = ref(false);
const showLimitModal = ref(false);
const isSVip = computed(() => user.value.sVip === true);
const isVip = computed(() => user.value.vip === true && !isSVip.value);
const challengeLimitText = computed(() =>
getLimitCountText("约战", dailyCount.value.challenge)
);
const enterRoom = debounce(async (number) => {
if (loading.value) return;
if (!canEenter(user.value, device.value, online.value)) return;
if (game.value.inBattle) {
uni.$showHint(1);
return;
}
if (!number) {
warnning.value = "请输入房间号";
showModal.value = true;
return;
}
try {
const room = await getRoomAPI(number);
if (!room.number) {
warnning.value = room.started ? "该房间对战已开始,无法加入" : "查无此房";
showModal.value = true;
return;
}
const alreadyIn = room.members.find(
(item) => item.userInfo.id === user.value.id
);
if (!alreadyIn) {
const result = await joinRoomAPI(number);
if (result.full) {
warnning.value = "房间已满员";
showModal.value = true;
return;
}
}
loading.value = true;
uni.navigateTo({
url: "/pages/battle-room?roomNumber=" + number,
});
} finally {
loading.value = false;
}
});
const onCreateRoom = async () => {
if (!canEenter(user.value, device.value, online.value)) return;
const countData = await loadDailyCount();
if (isLimitReached(countData.challenge)) {
showLimitModal.value = true;
return;
}
warnning.value = "";
showModal.value = true;
};
const closeLimitModal = () => {
showLimitModal.value = false;
};
const goVipPage = () => {
showLimitModal.value = false;
uni.navigateTo({
url: "/pages/member/be-vip",
});
};
const loadDailyCount = async () => {
if (!user.value.id) return dailyCount.value;
try {
const result = await getDailyCountAPI();
updateDailyCount(result);
return result || dailyCount.value;
} catch (error) {
console.log("load daily count error", error);
return dailyCount.value;
}
};
const onSignin = () => {
if (roomID.value && user.value.id) enterRoom(roomID.value);
showSignin.value = false;
};
/** 跳转到我的战绩页面默认展示「好友约战」tab */
const goMyRecord = () => {
uni.navigateTo({
url: '/pages/my-growth?tab=1',
});
};
onShow(async () => {
if (user.value.id) {
const [result] = await Promise.all([
getBattleDataAPI(),
loadDailyCount(),
]);
data.value = result;
}
});
onLoad(async (options) => {
if (options.roomID) {
roomID.value = options.roomID;
if (user.value.id) enterRoom(options.roomID);
else showSignin.value = true;
}
});
</script>
<template>
<Container title="好友约战" :showBackToGame="true">
<view :style="{ width: '100%', height: '100%' }">
<GuideTwo>
<view class="guide-tips">
<text class="guide-tips__main">约上朋友开几局欢乐多不寂寞</text>
<text class="guide-tips__sub">一起练升级更快早日加入全国排位赛</text>
</view>
</GuideTwo>
<view class="my-data">
<view>
<Avatar :rankLvl="user.rankLvl" :src="user.avatar" :size="30" />
<view
:class="[
'member-nickname',
isVip ? 'member-nickname--vip' : '',
isSVip ? 'member-nickname--svip' : '',
]"
>
<text class="member-nickname__text">{{ user.nickName }}</text>
<text v-if="isSVip" class="member-nickname__shine">{{
user.nickName
}}</text>
</view>
<text class="my-record-btn" @click="goMyRecord">我的战绩</text>
</view>
<view>
<view>
<view>
<text>{{ data.TotalBattle }}</text>
<text></text>
</view>
<text>约战数量</text>
</view>
<view>
<view>
<text>{{ data.totalArrow }}</text>
<text></text>
</view>
<text>射箭量</text>
</view>
<view>
<view class="stars">
<block v-for="i in 5" :key="i">
<image v-if="data.totalWinningRate >= i * 0.2" src="../static/star-full.png" mode="widthFix" />
<image v-else-if="data.totalWinningRate >= (i - 1) * 0.2 + 0.1" src="../static/star-half.png"
mode="widthFix" />
<image v-else src="../static/star-empty.png" mode="widthFix" />
</block>
</view>
<text>挑战难度</text>
</view>
</view>
</view>
<view class="founded-room">
<image src="../static/founded-room.png" mode="widthFix" />
<view>
<input placeholder="输入房间号" v-model="roomNumber" placeholder-style="color: #ccc" />
<view @click="$clickSound(() => enterRoom(roomNumber))">进入房间</view>
</view>
</view>
<view class="create-room">
<image 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" />
<view>
<image src="../static/question-mark.png" mode="widthFix" />
</view>
</view>
<view>
<view v-if="challengeLimitText" class="pp-text">
{{ challengeLimitText }}
</view>
<SButton width="80%" :rounded="30" :onClick="() => $clickSound(onCreateRoom)">
创建约战房
</SButton>
</view>
</view>
<SModal :show="showModal" :onClose="() => (showModal = false)" height="716rpx">
<view v-if="warnning" class="warnning">
{{ warnning }}
</view>
<!-- showModal 关闭时立即销毁组件重开时重建确保选项重置为 0 -->
<CreateRoom v-if="!warnning && showModal" :onConfirm="() => (showModal = false)" />
</SModal>
<Signin :show="showSignin" :onClose="onSignin" />
</view>
</Container>
<ModalDialog
:show="showLimitModal"
:content="'今日约战次数已经用完\n开通会员可增加次数'"
cancelText="知道了"
confirmText="去开通"
:onCancel="closeLimitModal"
:onConfirm="goVipPage"
/>
</template>
<style scoped>
.guide-tips {
display: flex;
flex-direction: column;
padding-left: 112rpx;
width: 100%;
}
.guide-tips__main {
font-weight: 400;
font-size: 26rpx;
color: rgba(255, 217, 71, 0.8);
}
.guide-tips__sub {
font-weight: 400;
font-size: 22rpx;
color: rgba(255, 255, 255, 0.8);
margin-top: 6rpx;
}
.founded-room {
display: flex;
flex-direction: column;
align-items: flex-start;
background-color: #54431d33;
border: 1px solid #54431d;
margin: 15px;
border-radius: 10px;
padding: 15px;
}
.founded-room>image {
width: 16vw;
}
.founded-room>view {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 15px;
background-color: #fff;
border-radius: 30px;
width: 100%;
overflow: hidden;
}
.founded-room>view>input {
width: 70%;
text-align: center;
font-size: 14px;
height: 40px;
color: #000;
}
.founded-room>view>view {
background-color: #fed847;
width: 30%;
line-height: 40px;
border-radius: 30px;
font-size: 14px;
padding: 3px 0;
font-weight: bold;
color: #000;
text-align: center;
}
.create-room {
position: relative;
margin: 15px;
height: 50vw;
}
.create-room>image:first-of-type {
position: absolute;
width: 100%;
}
.create-room>image:nth-of-type(2) {
padding: 15px;
width: 25vw;
position: relative;
}
.create-room>view:nth-child(3) {
margin: 12vw auto 5vw auto;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.create-room>view>image:first-child {
width: 19vw;
transform: translateY(-60%);
border-radius: 50%;
position: relative;
}
.create-room>view>image:nth-child(2) {
width: 37vw;
position: relative;
}
.create-room>view>view:nth-child(3) {
position: relative;
width: 19vw;
height: 19vw;
border-radius: 50%;
background-color: #ccc;
display: flex;
justify-content: center;
align-items: center;
transform: translateY(60%);
}
.create-room>view>view:nth-child(3)>image {
width: 20px;
margin-right: 2px;
}
.pp-text{
color: #fff;
text-align: center;
font-size: 22rpx;
margin-bottom: 20rpx;
}
.warnning {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
color: #fff9;
}
.my-data {
width: calc(100% - 30px);
margin: 15px;
margin-top: 0;
border-radius: 10px;
border: 1px solid #54431d;
overflow: hidden;
background-color: #54431d33;
}
.my-data>view {
width: 100%;
display: flex;
color: #fff9;
}
.my-data>view:first-child {
width: calc(100% - 30px);
align-items: flex-end;
padding-bottom: 15px;
border-bottom: 1px solid #48494e;
margin: 15px;
margin-bottom: 0;
}
.my-data>view:first-child>.my-record-btn {
font-weight: 400;
font-size: 24rpx;
color: #76D4FF;
text-align: center;
font-style: normal;
width: auto;
margin-left: auto;
}
.my-data>view:first-child>.member-nickname {
color: #fff;
margin-left: 10px;
width: 120px;
}
.my-data>view:first-child>.member-nickname__text,
.my-data>view:first-child>.member-nickname__shine {
font-size: 17px;
}
.my-data>view:last-child {
margin-bottom: 15px;
}
.my-data>view:last-child>view {
width: 33%;
margin-top: 15px;
display: flex;
flex-direction: column;
align-items: center;
font-size: 12px;
}
.my-data>view:last-child>view>view {
margin-bottom: 5px;
}
.my-data>view:last-child>view>view>text:first-child {
color: #fff;
font-size: 20px;
margin-right: 5px;
transform: translateY(4px);
}
.my-data>view:last-child>view:nth-child(2) {
border-left: 1px solid #48494e;
border-right: 1px solid #48494e;
}
.my-data>view:last-child>view>view {
display: flex;
align-items: flex-end;
height: 20px;
}
.stars>image {
width: 4vw;
height: 4vw;
margin: 0 1px;
}
</style>