Files
shoot-miniprograms/src/pages/first-try.vue
2026-02-10 14:48:07 +08:00

305 lines
9.3 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 Guide from "@/components/Guide.vue";
import SButton from "@/components/SButton.vue";
import Swiper from "@/components/Swiper.vue";
import BowTarget from "@/components/BowTarget.vue";
import ShootProgress from "@/components/ShootProgress.vue";
import ScoreResult from "@/components/ScoreResult.vue";
import ScorePanel from "@/components/ScorePanel.vue";
import Container from "@/components/Container.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,
startPractiseAPI,
endPractiseAPI,
getPractiseAPI,
} from "@/apis";
import { sharePractiseData } from "@/canvas";
import { wxShare, debounce } from "@/util";
import { MESSAGETYPESV2 } from "@/constants";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user } = storeToRefs(store);
const scores = ref([]);
const step = ref(0);
const total = 12;
const stepButtonTexts = [
"开始",
"进入下一个任务",
"进入下一个任务",
"我准备好了,开始",
"",
"退出新手试炼",
];
const title = ref("新手试炼场");
const start = ref(false);
const practiseResult = ref({});
const btnDisabled = ref(false);
const practiseId = ref("");
const showGuide = ref(false);
const guideImages = [
"https://static.shelingxingqiu.com/attachment/2026-02-08/dg9ev0wwdpgwt9e6du.png",
"https://static.shelingxingqiu.com/attachment/2026-02-08/dg9ev0wvv9sw4zioqk.png",
"https://static.shelingxingqiu.com/attachment/2026-02-08/dg9ev0ww3khaycallu.png",
"https://static.shelingxingqiu.com/attachment/2026-02-08/dg9ev0wtkcvaxxv0s8.png",
"https://static.shelingxingqiu.com/attachment/2026-02-08/dg9ev0wry5tw7ltmxr.png",
"https://static.shelingxingqiu.com/attachment/2026-02-08/dg9ev0wu3kcdrwzwpd.png",
"https://static.shelingxingqiu.com/attachment/2026-02-08/dg9ev0wwr6hfjhyfn5.png",
];
const onSwiperIndexChange = (index) => {
if (index + 1 === guideImages.length) {
showGuide.value = true;
}
};
const createPractise = async (arrows) => {
const result = await createPractiseAPI(arrows, 1);
if (result) practiseId.value = result.id;
};
const onOver = async () => {
practiseResult.value = await getPractiseAPI(practiseId.value);
start.value = false;
};
async function onReceiveMessage(msg) {
if (msg.type === MESSAGETYPESV2.ShootResult) {
scores.value = msg.details;
} else if (msg.type === MESSAGETYPESV2.BattleEnd) {
setTimeout(onOver, 1500);
} else if (msg.type === MESSAGETYPESV2.TestDistance) {
if (msg.shootData.distance / 100 >= 5) {
audioManager.play("距离合格");
btnDisabled.value = false;
showGuide.value = true;
} else audioManager.play("距离不足");
}
// messages.forEach((msg) => {
// if (msg.constructor === MESSAGETYPES.ShootSyncMeArrowID) {
// if (step.value === 2 && msg.target.dst / 100 >= 5) {
// btnDisabled.value = false;
// showGuide.value = true;
// } else if (scores.value.length < total) {
// scores.value.push(msg.target);
// }
// if (scores.value.length === total) {
// setTimeout(onOver, 1500);
// }
// }
// });
}
const onClickShare = debounce(async () => {
await sharePractiseData("shareCanvas", 1, user.value, practiseResult.value);
await wxShare("shareCanvas");
});
onMounted(() => {
uni.setKeepScreenOn({
keepScreenOn: true,
});
uni.$on("socket-inbox", onReceiveMessage);
uni.$on("share-image", onClickShare);
});
onBeforeUnmount(() => {
uni.setKeepScreenOn({
keepScreenOn: false,
});
uni.$off("socket-inbox", onReceiveMessage);
uni.$off("share-image", onClickShare);
audioManager.stopAll();
endPractiseAPI();
});
const nextStep = async () => {
if (step.value === 0) {
step.value = 1;
title.value = "-凹造型";
} else if (step.value === 1) {
showGuide.value = false;
btnDisabled.value = true;
step.value = 2;
title.value = "-感知距离";
const result = await createPractiseAPI(total, 120);
if (result) practiseId.value = result.id;
} else if (step.value === 2) {
showGuide.value = false;
step.value = 3;
title.value = "-小试牛刀";
} else if (step.value === 3) {
title.value = "小试牛刀";
await startPractiseAPI();
scores.value = [];
step.value = 4;
start.value = true;
setTimeout(() => {
uni.$emit("play-sound", "请开始射击");
}, 300);
} else if (step.value === 5) {
uni.navigateBack({
delta: 1,
});
}
};
const onClose = async () => {
const validArrows = (practiseResult.value.details || []).filter(
(a) => a.x !== -30 && a.y !== -30
);
if (validArrows.length === total) {
setTimeout(() => {
practiseResult.value = {};
showGuide.value = false;
step.value = 5;
}, 500);
} else {
practiseResult.value = {};
start.value = false;
scores.value = [];
step.value = 3;
const result = await createPractiseAPI(total, 120);
if (result) practiseId.value = result.id;
}
};
</script>
<template>
<Container :bgType="1" :title="title" :showBottom="step !== 4">
<view class="container">
<Guide
v-if="step !== 4"
:type="
step === 2
? 2
: step === 5 || (step === 0 && user.nickName.length > 6)
? 1
: 0
"
>
<text
v-if="step === 0"
:style="{
fontSize: '28rpx',
marginTop: user.nickName.length > 6 ? '-10rpx' : '0',
}"
>
hi<text :style="{ color: '#fed847' }">{{ user.nickName }}</text>
这是新人必刷小任务0基础小白也能快速掌握弓箭技巧和游戏规则哦~
</text>
<text v-if="step === 1" :style="{ fontSize: '28rpx' }"
>这是我们人帅技高的高教练首先请按教练示范尝试自己去做这些动作和手势吧</text
>
<view
class="guide-tips"
:style="{ marginTop: '8rpx' }"
v-if="step === 2"
>
<text>你知道5米射程有多远吗</text>
<text>
在我们的排位赛中射程小于5米的成绩无效建议平时练习距离至少5米现在来边射箭边调整你的站位点吧
</text>
</view>
<view class="guide-tips" v-if="step === 3">
<text>一切准备就绪</text>
<text :style="{ fontSize: '28rpx' }"
>试着完成一个真正的弓箭手任务吧</text
>
</view>
<view class="guide-tips" v-if="step === 5">
<text>新手试炼场通关啦优秀</text>
<text :style="{ fontSize: '28rpx' }"
>反曲弓运动基本知识和射灵世界系统规则你已Get是不是挺容易呀</text
>
</view>
</Guide>
<image
src="https://static.shelingxingqiu.com/attachment/2025-07-01/db0ehhek5yutxsetyi.png"
class="try-tip"
mode="widthFix"
v-if="step === 0"
/>
<image
src="https://static.shelingxingqiu.com/attachment/2025-11-17/deas80ef1sf9td0leq.png"
class="try-tip"
mode="widthFix"
v-if="step === 3"
/>
<image
src="https://static.shelingxingqiu.com/attachment/2025-07-01/db0ehpz9lav58g5drl.png"
class="try-tip"
mode="widthFix"
v-if="step === 5"
/>
<view style="height: 570px" v-if="step === 1">
<Swiper :onChange="onSwiperIndexChange" :data="guideImages" />
</view>
<ShootProgress v-if="step === 4" tips="请开始连续射箭" :start="start" />
<TestDistance v-if="step === 2" :guide="false" />
<view
class="user-row"
v-if="step === 4"
:style="{ marginBottom: step === 2 ? '40px' : '0' }"
>
<Avatar :src="user.avatar" :size="35" />
<BowPower />
</view>
<BowTarget
v-if="step === 4"
:currentRound="step === 4 ? scores.length : 0"
:totalRound="step === 4 ? total : 0"
:scores="scores"
/>
<ScorePanel
v-if="step === 4"
:total="total"
:rowCount="6"
:arrows="scores"
/>
<ScoreResult
v-if="practiseResult.details"
:rowCount="6"
:total="total"
:onClose="onClose"
:result="practiseResult"
:tipSrc="`../static/${
practiseResult.details.filter(
(arrow) => arrow.x !== -30 && arrow.y !== -30
).length < total
? 'un'
: ''
}finish-tip.png`"
/>
<canvas class="share-canvas" id="shareCanvas" type="2d"></canvas>
</view>
<template #bottom>
<SButton :onClick="nextStep" :disabled="btnDisabled">
<BubbleTip v-if="showGuide" :type="step === 1 ? 'long' : 'short'">
<text :style="{ transform: 'translateY(-18rpx)' }">{{
step === 1 ? "学会了,我摆得比教练还帅" : "我找到合适的点位了"
}}</text>
</BubbleTip>
{{ stepButtonTexts[step] }}
</SButton>
</template>
</Container>
</template>
<style scoped>
.container {
width: 100%;
}
.try-tip {
width: calc(100% - 20px);
margin: 0 10px;
}
</style>