update:新手教程流程优化

This commit is contained in:
2026-05-27 17:42:43 +08:00
parent b8d1654476
commit d932ce3dea
5 changed files with 194 additions and 68 deletions

View File

@@ -142,8 +142,8 @@ onBeforeUnmount(() => {
</view> </view>
<block <block
v-if=" v-if="
'-凹造型-感知距离-小试牛刀'.indexOf(title) === -1 || '-箭前准备-感知距离-小试牛刀'.indexOf(title) === -1 ||
'-凹造型-感知距离-小试牛刀'.indexOf(title) === 10 '-箭前准备-感知距离-小试牛刀'.indexOf(title) === 11
" "
> >
<text>{{ title }}</text> <text>{{ title }}</text>
@@ -151,12 +151,12 @@ onBeforeUnmount(() => {
<block <block
v-if=" v-if="
title && title &&
'-凹造型-感知距离-小试牛刀'.indexOf(title) !== -1 && '-箭前准备-感知距离-小试牛刀'.indexOf(title) !== -1 &&
'-凹造型-感知距离-小试牛刀'.indexOf(title) !== 10 '-箭前准备-感知距离-小试牛刀'.indexOf(title) !== 11
" "
> >
<view class="first-try-steps"> <view class="first-try-steps">
<text :class="title === '-凹造型' ? 'current-step' : ''">凹造型</text> <text :class="title === '-箭前准备' ? 'current-step' : ''">箭前准备</text>
<text>-</text> <text>-</text>
<text :class="title === '-感知距离' ? 'current-step' : ''" <text :class="title === '-感知距离' ? 'current-step' : ''"
>感知距离</text >感知距离</text

View File

@@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref, watch } from "vue";
const props = defineProps({ const props = defineProps({
interval: { interval: {
@@ -14,13 +14,24 @@ const props = defineProps({
type: Array, type: Array,
default: () => [], default: () => [],
}, },
current: {
type: Number,
default: 0,
},
onChange: { onChange: {
type: Function, type: Function,
default: (index) => {}, default: (index) => {},
}, },
}); });
const currentIndex = ref(0); const currentIndex = ref(props.current);
watch(
() => props.current,
(index) => {
currentIndex.value = index;
}
);
const handleChange = (e) => { const handleChange = (e) => {
currentIndex.value = e.detail.current; currentIndex.value = e.detail.current;
@@ -75,7 +86,7 @@ const handleChange = (e) => {
.dots { .dots {
position: absolute; position: absolute;
bottom: 5%; bottom: 2%;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
display: flex; display: flex;

View File

@@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, onMounted, onBeforeUnmount } from "vue"; import { computed, ref, onMounted, onBeforeUnmount } from "vue";
import Guide from "@/components/Guide.vue"; import Guide from "@/components/Guide.vue";
import SButton from "@/components/SButton.vue"; import SButton from "@/components/SButton.vue";
import Swiper from "@/components/Swiper.vue"; import Swiper from "@/components/Swiper.vue";
@@ -18,6 +18,8 @@ import {
startPractiseAPI, startPractiseAPI,
endPractiseAPI, endPractiseAPI,
getPractiseAPI, getPractiseAPI,
laserAimAPI,
laserCloseAPI,
} from "@/apis"; } from "@/apis";
import { sharePractiseData } from "@/canvas"; import { sharePractiseData } from "@/canvas";
import { wxShare, debounce } from "@/util"; import { wxShare, debounce } from "@/util";
@@ -32,6 +34,7 @@ const total = 12;
const stepButtonTexts = [ const stepButtonTexts = [
"开始", "开始",
"进入下一个任务", "进入下一个任务",
"我已校准",
"进入下一个任务", "进入下一个任务",
"我准备好了,开始", "我准备好了,开始",
"", "",
@@ -43,8 +46,11 @@ const practiseResult = ref({});
const btnDisabled = ref(false); const btnDisabled = ref(false);
const practiseId = ref(""); const practiseId = ref("");
const showGuide = ref(false); const showGuide = ref(false);
const laserActive = ref(false);
const guideSwiperIndex = ref(0);
const guideImages = [ const guideImages = [
"https://static.shelingxingqiu.com/shootmini/static/target.png",
"https://static.shelingxingqiu.com/attachment/2026-02-08/dg9ev0wwdpgwt9e6du.png", "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/dg9ev0wvv9sw4zioqk.png",
"https://static.shelingxingqiu.com/attachment/2026-02-08/dg9ev0ww3khaycallu.png", "https://static.shelingxingqiu.com/attachment/2026-02-08/dg9ev0ww3khaycallu.png",
@@ -54,10 +60,45 @@ const guideImages = [
"https://static.shelingxingqiu.com/attachment/2026-02-08/dg9ev0wwr6hfjhyfn5.png", "https://static.shelingxingqiu.com/attachment/2026-02-08/dg9ev0wwr6hfjhyfn5.png",
]; ];
const calibrationGuides = [
{
title: "箭头面向靶子",
src: "https://static.shelingxingqiu.com/attachment/2025-10-30/ddv9p5fk5wscg7hrfo.png",
},
{
title: "摆出拉弓姿势",
src: "https://static.shelingxingqiu.com/attachment/2025-10-30/ddv9p5fk5b7ljrhx3o.png",
},
{
title: "调整瞄准器",
src: "https://static.shelingxingqiu.com/attachment/2025-10-29/dduexjgrcxf9wjaiv4.png",
},
];
const onSwiperIndexChange = (index) => { const onSwiperIndexChange = (index) => {
if (index + 1 === guideImages.length) { guideSwiperIndex.value = index;
showGuide.value = true; showGuide.value = index + 1 === guideImages.length;
} };
const isGuideLastImage = computed(
() => guideSwiperIndex.value + 1 === guideImages.length
);
const currentStepButtonText = computed(() => {
if (step.value === 1 && isGuideLastImage.value) return "去校准智能弓";
return stepButtonTexts[step.value];
});
const openCalibrationLaser = async () => {
if (laserActive.value) return;
await laserAimAPI();
laserActive.value = true;
};
const closeCalibrationLaser = async () => {
if (!laserActive.value) return;
await laserCloseAPI();
laserActive.value = false;
}; };
const createPractise = async (arrows) => { const createPractise = async (arrows) => {
@@ -75,7 +116,7 @@ async function onReceiveMessage(msg) {
scores.value = msg.details; scores.value = msg.details;
} else if (msg.type === MESSAGETYPESV2.BattleEnd) { } else if (msg.type === MESSAGETYPESV2.BattleEnd) {
setTimeout(onOver, 1500); setTimeout(onOver, 1500);
} else if (msg.type === MESSAGETYPESV2.TestDistance) { } else if (msg.type === MESSAGETYPESV2.TestDistance && step.value === 3) {
if (msg.shootData.distance / 100 >= 5) { if (msg.shootData.distance / 100 >= 5) {
audioManager.play("距离合格"); audioManager.play("距离合格");
btnDisabled.value = false; btnDisabled.value = false;
@@ -110,12 +151,13 @@ onMounted(() => {
uni.$on("share-image", onClickShare); uni.$on("share-image", onClickShare);
}); });
onBeforeUnmount(() => { onBeforeUnmount(async () => {
uni.setKeepScreenOn({ uni.setKeepScreenOn({
keepScreenOn: false, keepScreenOn: false,
}); });
uni.$off("socket-inbox", onReceiveMessage); uni.$off("socket-inbox", onReceiveMessage);
uni.$off("share-image", onClickShare); uni.$off("share-image", onClickShare);
await closeCalibrationLaser();
audioManager.stopAll(); audioManager.stopAll();
endPractiseAPI(); endPractiseAPI();
}); });
@@ -123,28 +165,39 @@ onBeforeUnmount(() => {
const nextStep = async () => { const nextStep = async () => {
if (step.value === 0) { if (step.value === 0) {
step.value = 1; step.value = 1;
title.value = "-凹造型"; title.value = "-箭前准备";
} else if (step.value === 1) { } else if (step.value === 1) {
if (!isGuideLastImage.value) {
guideSwiperIndex.value += 1;
showGuide.value = guideSwiperIndex.value + 1 === guideImages.length;
return;
}
showGuide.value = false;
step.value = 2;
// title.value = "-校准智能弓";
await openCalibrationLaser();
} else if (step.value === 2) {
await closeCalibrationLaser();
showGuide.value = false; showGuide.value = false;
btnDisabled.value = true; btnDisabled.value = true;
step.value = 2; step.value = 3;
title.value = "-感知距离"; title.value = "-感知距离";
const result = await createPractiseAPI(total, 120); const result = await createPractiseAPI(total, 120);
if (result) practiseId.value = result.id; if (result) practiseId.value = result.id;
} else if (step.value === 2) {
showGuide.value = false;
step.value = 3;
title.value = "-小试牛刀";
} else if (step.value === 3) { } else if (step.value === 3) {
showGuide.value = false;
step.value = 4;
title.value = "-小试牛刀";
} else if (step.value === 4) {
title.value = "小试牛刀"; title.value = "小试牛刀";
await startPractiseAPI(); await startPractiseAPI();
scores.value = []; scores.value = [];
step.value = 4; step.value = 5;
start.value = true; start.value = true;
setTimeout(() => { setTimeout(() => {
uni.$emit("play-sound", "请开始射击"); uni.$emit("play-sound", "请开始射击");
}, 300); }, 300);
} else if (step.value === 5) { } else if (step.value === 6) {
uni.navigateBack({ uni.navigateBack({
delta: 1, delta: 1,
}); });
@@ -159,13 +212,13 @@ const onClose = async () => {
setTimeout(() => { setTimeout(() => {
practiseResult.value = {}; practiseResult.value = {};
showGuide.value = false; showGuide.value = false;
step.value = 5; step.value = 6;
}, 500); }, 500);
} else { } else {
practiseResult.value = {}; practiseResult.value = {};
start.value = false; start.value = false;
scores.value = []; scores.value = [];
step.value = 3; step.value = 4;
const result = await createPractiseAPI(total, 120); const result = await createPractiseAPI(total, 120);
if (result) practiseId.value = result.id; if (result) practiseId.value = result.id;
} }
@@ -173,14 +226,14 @@ const onClose = async () => {
</script> </script>
<template> <template>
<Container :bgType="1" :title="title" :showBottom="step !== 4"> <Container :bgType="1" :title="title" :showBottom="step !== 5">
<view class="container"> <view class="container">
<Guide <Guide
v-if="step !== 4" v-if="step !== 5"
:type=" :type="
step === 2 step === 3
? 2 ? 2
: step === 5 || (step === 0 && user.nickName.length > 6) : step === 6 || (step === 0 && user.nickName.length > 6)
? 1 ? 1
: 0 : 0
" "
@@ -196,25 +249,28 @@ const onClose = async () => {
这是新人必刷小任务0基础小白也能快速掌握弓箭技巧和游戏规则哦~ 这是新人必刷小任务0基础小白也能快速掌握弓箭技巧和游戏规则哦~
</text> </text>
<text v-if="step === 1" :style="{ fontSize: '28rpx' }" <text v-if="step === 1" :style="{ fontSize: '28rpx' }"
>这是我们人帅技高的高教练首先请按教练示范尝试自己去做这些动作和手势吧</text >位就是人帅技高的高教练接下来请跟随教练指引做好射箭前期准备</text
>
<text v-if="step === 2" :style="{ fontSize: '28rpx' }"
>请按下方步骤完成智能弓校准让瞄准器和靶子保持对齐</text
> >
<view <view
class="guide-tips" class="guide-tips"
:style="{ marginTop: '8rpx' }" :style="{ marginTop: '8rpx' }"
v-if="step === 2" v-if="step === 3"
> >
<text>你知道5米射程有多远吗</text> <text>你知道5米射程有多远吗</text>
<text> <text>
在我们的排位赛中射程小于5米的成绩无效建议平时练习距离至少5米现在来边射箭边调整你的站位点吧 在我们的排位赛中射程小于5米的成绩无效建议平时练习距离至少5米现在来边射箭边调整你的站位点吧
</text> </text>
</view> </view>
<view class="guide-tips" v-if="step === 3"> <view class="guide-tips" v-if="step === 4">
<text>一切准备就绪</text> <text>一切准备就绪</text>
<text :style="{ fontSize: '28rpx' }" <text :style="{ fontSize: '28rpx' }"
>试着完成一个真正的弓箭手任务吧</text >试着完成一个真正的弓箭手任务吧</text
> >
</view> </view>
<view class="guide-tips" v-if="step === 5"> <view class="guide-tips" v-if="step === 6">
<text>新手试炼场通关啦优秀</text> <text>新手试炼场通关啦优秀</text>
<text :style="{ fontSize: '28rpx' }" <text :style="{ fontSize: '28rpx' }"
>反曲弓运动基本知识和射灵世界系统规则你已Get是不是挺容易呀</text >反曲弓运动基本知识和射灵世界系统规则你已Get是不是挺容易呀</text
@@ -231,35 +287,53 @@ const onClose = async () => {
src="https://static.shelingxingqiu.com/attachment/2025-11-17/deas80ef1sf9td0leq.png" src="https://static.shelingxingqiu.com/attachment/2025-11-17/deas80ef1sf9td0leq.png"
class="try-tip" class="try-tip"
mode="widthFix" mode="widthFix"
v-if="step === 3" v-if="step === 4"
/> />
<image <image
src="https://static.shelingxingqiu.com/attachment/2025-07-01/db0ehpz9lav58g5drl.png" src="https://static.shelingxingqiu.com/attachment/2025-07-01/db0ehpz9lav58g5drl.png"
class="try-tip" class="try-tip"
mode="widthFix" mode="widthFix"
v-if="step === 5" v-if="step === 6"
/> />
<view style="height: 570px" v-if="step === 1"> <view style="height: 570px" v-if="step === 1">
<Swiper :onChange="onSwiperIndexChange" :data="guideImages" /> <Swiper
:current="guideSwiperIndex"
:onChange="onSwiperIndexChange"
:data="guideImages"
/>
</view> </view>
<ShootProgress v-if="step === 4" tips="请开始连续射箭" :start="start" /> <view class="calibration-container" v-if="step === 2">
<TestDistance v-if="step === 2" :guide="false" /> <view
v-for="(guide, index) in calibrationGuides"
:key="guide.title"
class="calibration-guide"
>
<view>
<text>{{ index + 1 }}</text>
<text>{{ guide.title }}</text>
</view>
<image :src="guide.src" mode="widthFix" />
</view>
<text>请完成以上步骤校准智能弓</text>
</view>
<ShootProgress v-if="step === 5" tips="请开始连续射箭" :start="start" />
<TestDistance v-if="step === 3" :guide="false" />
<view <view
class="user-row" class="user-row"
v-if="step === 4" v-if="step === 5"
:style="{ marginBottom: step === 2 ? '40px' : '0' }" :style="{ marginBottom: '0' }"
> >
<Avatar :src="user.avatar" :size="35" /> <Avatar :src="user.avatar" :size="35" />
<BowPower /> <BowPower />
</view> </view>
<BowTarget <BowTarget
v-if="step === 4" v-if="step === 5"
:currentRound="step === 4 ? scores.length : 0" :currentRound="step === 5 ? scores.length : 0"
:totalRound="step === 4 ? total : 0" :totalRound="step === 5 ? total : 0"
:scores="scores" :scores="scores"
/> />
<ScorePanel <ScorePanel
v-if="step === 4" v-if="step === 5"
:total="total" :total="total"
:rowCount="6" :rowCount="6"
:arrows="scores" :arrows="scores"
@@ -287,7 +361,7 @@ const onClose = async () => {
step === 1 ? "学会了,我摆得比教练还帅" : "我找到合适的点位了" step === 1 ? "学会了,我摆得比教练还帅" : "我找到合适的点位了"
}}</text> }}</text>
</BubbleTip> </BubbleTip>
{{ stepButtonTexts[step] }} {{ currentStepButtonText }}
</SButton> </SButton>
</template> </template>
</Container> </Container>
@@ -301,4 +375,43 @@ const onClose = async () => {
width: calc(100% - 20px); width: calc(100% - 20px);
margin: 0 10px; margin: 0 10px;
} }
.calibration-container {
display: flex;
flex-direction: column;
align-items: center;
}
.calibration-guide {
display: flex;
flex-direction: column;
align-items: center;
font-size: 26rpx;
color: #ffffff;
margin-bottom: 15rpx;
}
.calibration-guide > view {
width: 100%;
margin: 25rpx 0;
display: flex;
align-items: center;
}
.calibration-guide > view > text:first-child {
font-size: 24rpx;
background: #e89024;
border-radius: 50%;
width: 32rpx;
height: 32rpx;
line-height: 32rpx;
display: block;
text-align: center;
margin-right: 15rpx;
}
.calibration-guide > image {
width: 630rpx;
height: 250rpx;
}
.calibration-container > text {
font-size: 24rpx;
color: #fff9;
margin: 30rpx;
}
</style> </style>

View File

@@ -212,33 +212,35 @@ onShow(() => {
<text>{{ user.nickName }}</text> <text>{{ user.nickName }}</text>
</view> </view>
</view> </view>
<block v-if="calibration"> <!-- <block v-if="calibration"> -->
<SButton :onClick="toFristTryPage" width="60vw" :rounded="40"
>进入新手试炼</SButton
>
<view :style="{ marginTop: '15px' }">
<SButton
:onClick="backToHome"
backgroundColor="#fff3"
color="#fff"
width="60vw"
:rounded="40"
>返回首页</SButton
>
</view>
</block>
<block v-else>
<view> <view>
<text>恭喜你的弓箭和账号已成功绑定</text> <text>恭喜你的弓箭和账号已成功绑定</text>
<text :style="{ color: '#fed847' }">已赠送6个月射灵世界会员</text> <text :style="{ color: '#fed847' }">已赠送6个月射灵世界会员</text>
</view> </view>
<SButton :onClick="goCalibration" width="60vw" :rounded="40"> <!-- <SButton :onClick="goCalibration" width="60vw" :rounded="40">
开启智能弓进行校准 开启智能弓进行校准
</SButton> </SButton>
<text :style="{ marginTop: '20rpx', fontSize: '24rpx', color: '#fff9' }" <text :style="{ marginTop: '20rpx', fontSize: '24rpx', color: '#fff9' }"
>校准时弓箭激光将开启请勿直视激光</text >校准时弓箭激光将开启请勿直视激光</text
> > -->
</block>
<view>
<SButton
:onClick="backToHome"
backgroundColor="#fff3"
color="#fff"
width="60vw"
:rounded="40"
>返回首页</SButton
>
</view>
<view :style="{ marginTop: '15px' }">
<SButton :onClick="toFristTryPage" width="60vw" :rounded="40">进入新手试炼</SButton>
</view>
<!-- </block> -->
<!-- <block v-else>
</block> -->
</view> </view>
<view v-if="device.deviceId && !justBind" class="has-device"> <view v-if="device.deviceId && !justBind" class="has-device">
<view class="device-binded"> <view class="device-binded">

View File

@@ -142,8 +142,8 @@ onBeforeUnmount(() => {
</view> </view>
<block <block
v-if=" v-if="
'-凹造型-感知距离-小试牛刀'.indexOf(title) === -1 || '-箭前准备-感知距离-小试牛刀'.indexOf(title) === -1 ||
'-凹造型-感知距离-小试牛刀'.indexOf(title) === 10 '-箭前准备-感知距离-小试牛刀'.indexOf(title) === 11
" "
> >
<text>{{ title }}</text> <text>{{ title }}</text>
@@ -151,12 +151,12 @@ onBeforeUnmount(() => {
<block <block
v-if=" v-if="
title && title &&
'-凹造型-感知距离-小试牛刀'.indexOf(title) !== -1 && '-箭前准备-感知距离-小试牛刀'.indexOf(title) !== -1 &&
'-凹造型-感知距离-小试牛刀'.indexOf(title) !== 10 '-箭前准备-感知距离-小试牛刀'.indexOf(title) !== 11
" "
> >
<view class="first-try-steps"> <view class="first-try-steps">
<text :class="title === '-凹造型' ? 'current-step' : ''">凹造型</text> <text :class="title === '-箭前准备' ? 'current-step' : ''">箭前准备</text>
<text>-</text> <text>-</text>
<text :class="title === '-感知距离' ? 'current-step' : ''" <text :class="title === '-感知距离' ? 'current-step' : ''"
>感知距离</text >感知距离</text