Files
shoot-miniprograms/src/pages/practise-one.vue
T

243 lines
6.4 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 { ref, onMounted, onBeforeUnmount } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import ShootProgress from "@/components/ShootProgress.vue";
import BowTarget from "@/components/BowTarget.vue";
import ScorePanel2 from "@/components/ScorePanel2.vue";
import ScoreResult from "@/components/ScoreResult.vue";
import SButton from "@/components/SButton.vue";
import Avatar from "@/components/Avatar.vue";
import BowPower from "@/components/BowPower.vue";
import TestDistance from "@/components/TestDistance.vue";
import BubbleTip from "@/components/BubbleTip.vue";
import audioManager from "@/audioManager";
import {
createPractiseAPI,
endPractiseAPI,
getPractiseAPI,
startPractiseAPI,
} from "@/apis";
import { connectMatchWebSocket, closeMatchWebSocket } from "@/matchWebsocket";
import { sharePractiseData } from "@/canvas";
import { wxShare, debounce } from "@/util";
import { MESSAGETYPESV2, roundsName } from "@/constants";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user, device } = storeToRefs(store);
const start = ref(false);
const scores = ref([]);
const isSvip = ref(false);
const total = 12;
const practiseResult = ref({});
const practiseId = ref("");
const showGuide = ref(false);
const tips = ref("");
const targetType = ref(1);
const sharing = ref(false);
const exiting = ref(false);
const RESULT_TIP_CDN = "https://static.shelingxingqiu.com/shootmini/static";
onLoad((options) => {
if (options.target) {
targetType.value = Number(options.target);
}
});
const createPractise = async () => {
closeMatchWebSocket({ reason: "practice-recreate" });
const result = await createPractiseAPI(
total,
120,
targetType.value,
device.value.deviceId
);
if (result?.id) practiseId.value = result.id;
if (!result?.serverAddr) {
uni.showToast({
title: "练习连接信息异常,请重试",
icon: "none",
});
return null;
}
connectMatchWebSocket({
serverAddr: result.serverAddr,
matchId: result.id,
userId: user.value.id,
});
return result;
};
const beginPractise = async () => {
if (!practiseId.value) {
uni.showToast({
title: "练习未创建,请重试",
icon: "none",
});
return false;
}
await startPractiseAPI(practiseId.value);
return true;
};
const onReady = async () => {
const result = await beginPractise();
if (!result) return;
scores.value = [];
isSvip.value = false;
start.value = true;
audioManager.play("练习开始");
};
const onOver = async (message) => {
practiseResult.value = Array.isArray(message?.details)
? message
: await getPractiseAPI(practiseId.value);
start.value = false;
};
async function onReceiveMessage(msg) {
if (msg.type === MESSAGETYPESV2.ShootResult) {
isSvip.value = msg.sVip === true;
scores.value = Array.isArray(msg.details) ? msg.details : scores.value;
} else if (msg.type === MESSAGETYPESV2.BattleEnd) {
setTimeout(() => onOver(msg), 1500);
}
}
async function onComplete() {
const validArrows = (practiseResult.value.details || []).filter(
(a) => a.x !== -30 && a.y !== -30
);
if (validArrows.length === total) {
uni.navigateBack();
} else {
practiseId.value = "";
practiseResult.value = {};
start.value = false;
scores.value = [];
isSvip.value = false;
await createPractise();
}
}
async function exitPractise() {
if (exiting.value) return;
exiting.value = true;
try {
if (practiseId.value && !practiseResult.value?.details) {
await endPractiseAPI(practiseId.value);
}
} catch (error) {
console.error("Failed to stop practice", error);
} finally {
uni.navigateBack();
}
}
const getResultTipSrc = (result = {}) => {
const validCount = (result.details || []).filter(
(arrow) => arrow.x !== -30 && arrow.y !== -30
).length;
return `${RESULT_TIP_CDN}/${validCount < total ? "un" : ""}finish-tip.png`;
};
const onClickShare = debounce(async () => {
if (sharing.value) return;
sharing.value = true;
try {
await sharePractiseData("shareCanvas", 2, user.value, practiseResult.value);
await wxShare("shareCanvas");
} catch (e) {
uni.showToast({
title: "海报生成失败,请稍后重试",
icon: "none",
});
} finally {
sharing.value = false;
}
});
onMounted(async () => {
void audioManager.warmAll();
// audioManager.play("第一轮");
uni.setKeepScreenOn({
keepScreenOn: true,
});
uni.$on("socket-inbox", onReceiveMessage);
uni.$on("share-image", onClickShare);
await createPractise();
});
onBeforeUnmount(() => {
uni.setKeepScreenOn({
keepScreenOn: false,
});
uni.$off("socket-inbox", onReceiveMessage);
uni.$off("share-image", onClickShare);
audioManager.stopAll();
closeMatchWebSocket({ reason: "practice-leave" });
});
</script>
<template>
<Container
:bgType="1"
title="个人单组练习"
:showBottom="!start && !scores.length"
:onBack="exitPractise"
>
<view>
<TestDistance v-if="!start && !practiseResult.id" />
<block v-else>
<ShootProgress
:tips="`${
!start || scores.length === 12
? ''
: `请开始射箭第${
roundsName[Math.ceil((scores.length + 1) / 3)]
}轮`
}`"
:start="start"
:onStop="onOver"
end-audio-key="练习结束"
/>
<view class="user-row">
<Avatar :src="user.avatar" :size="35" />
<BubbleTip v-if="showGuide" type="normal2">
<text>还有两场坚持</text>
<text>就是胜利!💪</text>
</BubbleTip>
<BowPower />
</view>
<BowTarget
:totalRound="start ? total / 4 : 0"
:currentRound="scores.length % 3"
:scores="scores"
:isSvip="isSvip"
/>
<ScorePanel2 :arrows="scores" />
<ScoreResult
v-if="practiseResult.details"
:rowCount="6"
:total="total"
:onClose="onComplete"
:result="practiseResult"
:tipSrc="getResultTipSrc(practiseResult)"
/>
<canvas class="share-canvas" id="shareCanvas" type="2d"></canvas>
</block>
</view>
<template #bottom>
<SButton :onClick="onReady">准备好了直接开始</SButton>
</template>
</Container>
</template>
<style scoped></style>