添加气泡消息交互
This commit is contained in:
@@ -192,5 +192,6 @@ button::after {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 0 15px;
|
padding: 0 15px;
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
43
src/components/BubbleTip.vue
Normal file
43
src/components/BubbleTip.vue
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: "normal",
|
||||||
|
},
|
||||||
|
location: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view :class="`container ${type}`" :style="{ ...location }">
|
||||||
|
<slot />
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.container {
|
||||||
|
position: absolute;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.normal {
|
||||||
|
background-image: url("../static/bubble-tip.png");
|
||||||
|
width: 190rpx;
|
||||||
|
height: 105rpx;
|
||||||
|
padding-top: 5px;
|
||||||
|
padding-left: 49rpx;
|
||||||
|
}
|
||||||
|
.long {
|
||||||
|
background-image: url("../static/bubble-tip2.png");
|
||||||
|
width: 370rpx;
|
||||||
|
height: 70rpx;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -81,6 +81,7 @@ const onBtnClick = debounce(async () => {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
overflow: initial;
|
||||||
}
|
}
|
||||||
.loading {
|
.loading {
|
||||||
width: 25px;
|
width: 25px;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import Container from "@/components/Container.vue";
|
|||||||
import Avatar from "@/components/Avatar.vue";
|
import Avatar from "@/components/Avatar.vue";
|
||||||
import BowPower from "@/components/BowPower.vue";
|
import BowPower from "@/components/BowPower.vue";
|
||||||
import TestDistance from "@/components/TestDistance.vue";
|
import TestDistance from "@/components/TestDistance.vue";
|
||||||
|
import BubbleTip from "@/components/BubbleTip.vue";
|
||||||
import { createPractiseAPI } from "@/apis";
|
import { createPractiseAPI } from "@/apis";
|
||||||
import { generateCanvasImage } from "@/util";
|
import { generateCanvasImage } from "@/util";
|
||||||
import { MESSAGETYPES } from "@/constants";
|
import { MESSAGETYPES } from "@/constants";
|
||||||
@@ -34,8 +35,26 @@ const title = ref("新手试炼场");
|
|||||||
const start = ref(false);
|
const start = ref(false);
|
||||||
const practiseResult = ref({});
|
const practiseResult = ref({});
|
||||||
const power = ref(0);
|
const power = ref(0);
|
||||||
// const btnDisabled = ref(false);
|
const btnDisabled = ref(false);
|
||||||
const practiseId = ref("");
|
const practiseId = ref("");
|
||||||
|
const showGuide = ref(false);
|
||||||
|
|
||||||
|
const guideImages = [
|
||||||
|
"https://api.shelingxingqiu.com/attachment/2025-07-09/db77x68bs7z5elwvw7.png",
|
||||||
|
"https://api.shelingxingqiu.com/attachment/2025-07-09/db77x68qmi7grgreen.png",
|
||||||
|
"https://api.shelingxingqiu.com/attachment/2025-07-09/db77x68hgrw1ip4wae.png",
|
||||||
|
"https://api.shelingxingqiu.com/attachment/2025-07-09/db77x684x8zmfrmbla.png",
|
||||||
|
"https://api.shelingxingqiu.com/attachment/2025-07-09/db77x67sding7fodnk.png",
|
||||||
|
"https://api.shelingxingqiu.com/attachment/2025-07-09/db77x68mpug7cac4yt.png",
|
||||||
|
"https://api.shelingxingqiu.com/attachment/2025-07-09/db77x68my783mlmgxv.png",
|
||||||
|
"https://api.shelingxingqiu.com/attachment/2025-07-09/db77x68p48ylzirtb0.png",
|
||||||
|
];
|
||||||
|
|
||||||
|
const onSwiperIndexChange = (index) => {
|
||||||
|
if (index + 1 === guideImages.length) {
|
||||||
|
showGuide.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const createPractise = async (arrows) => {
|
const createPractise = async (arrows) => {
|
||||||
const result = await createPractiseAPI(arrows);
|
const result = await createPractiseAPI(arrows);
|
||||||
@@ -49,9 +68,9 @@ async function onReceiveMessage(messages = []) {
|
|||||||
scores.value.push(msg.target);
|
scores.value.push(msg.target);
|
||||||
}
|
}
|
||||||
power.value = msg.target.battery;
|
power.value = msg.target.battery;
|
||||||
// if (step.value === 2 && msg.target.dst / 100 > 5) {
|
// if (step.value === 2 && msg.target.dst / 100 >= 5) {
|
||||||
// if (step.value === 2 && msg.target.dst > 5) {
|
btnDisabled.value = false;
|
||||||
// btnDisabled.value = false;
|
showGuide.value = true;
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
if (msg.constructor === MESSAGETYPES.ShootSyncMePracticeID) {
|
if (msg.constructor === MESSAGETYPES.ShootSyncMePracticeID) {
|
||||||
@@ -87,10 +106,12 @@ const nextStep = async () => {
|
|||||||
step.value = 1;
|
step.value = 1;
|
||||||
title.value = "-凹造型";
|
title.value = "-凹造型";
|
||||||
} else if (step.value === 1) {
|
} else if (step.value === 1) {
|
||||||
// btnDisabled.value = true;
|
showGuide.value = false;
|
||||||
|
btnDisabled.value = true;
|
||||||
step.value = 2;
|
step.value = 2;
|
||||||
title.value = "-感知距离";
|
title.value = "-感知距离";
|
||||||
} else if (step.value === 2) {
|
} else if (step.value === 2) {
|
||||||
|
showGuide.value = false;
|
||||||
step.value = 3;
|
step.value = 3;
|
||||||
title.value = "-小试牛刀";
|
title.value = "-小试牛刀";
|
||||||
} else if (step.value === 3) {
|
} else if (step.value === 3) {
|
||||||
@@ -189,18 +210,7 @@ const onClose = () => {
|
|||||||
v-if="step === 5"
|
v-if="step === 5"
|
||||||
/>
|
/>
|
||||||
<view style="height: 570px" v-if="step === 1">
|
<view style="height: 570px" v-if="step === 1">
|
||||||
<Swiper
|
<Swiper :onChange="onSwiperIndexChange" :data="guideImages" />
|
||||||
:data="[
|
|
||||||
'https://api.shelingxingqiu.com/attachment/2025-07-09/db77x68bs7z5elwvw7.png',
|
|
||||||
'https://api.shelingxingqiu.com/attachment/2025-07-09/db77x68qmi7grgreen.png',
|
|
||||||
'https://api.shelingxingqiu.com/attachment/2025-07-09/db77x68hgrw1ip4wae.png',
|
|
||||||
'https://api.shelingxingqiu.com/attachment/2025-07-09/db77x684x8zmfrmbla.png',
|
|
||||||
'https://api.shelingxingqiu.com/attachment/2025-07-09/db77x67sding7fodnk.png',
|
|
||||||
'https://api.shelingxingqiu.com/attachment/2025-07-09/db77x68mpug7cac4yt.png',
|
|
||||||
'https://api.shelingxingqiu.com/attachment/2025-07-09/db77x68my783mlmgxv.png',
|
|
||||||
'https://api.shelingxingqiu.com/attachment/2025-07-09/db77x68p48ylzirtb0.png',
|
|
||||||
]"
|
|
||||||
/>
|
|
||||||
</view>
|
</view>
|
||||||
<ShootProgress
|
<ShootProgress
|
||||||
v-if="step === 4"
|
v-if="step === 4"
|
||||||
@@ -243,9 +253,18 @@ const onClose = () => {
|
|||||||
<canvas class="share-canvas" canvas-id="shareCanvas"></canvas>
|
<canvas class="share-canvas" canvas-id="shareCanvas"></canvas>
|
||||||
</view>
|
</view>
|
||||||
<view :style="{ marginBottom: '20px' }">
|
<view :style="{ marginBottom: '20px' }">
|
||||||
<SButton v-if="step !== 4" :onClick="nextStep">{{
|
<SButton v-if="step !== 4" :onClick="nextStep" :disabled="btnDisabled">
|
||||||
stepButtonTexts[step]
|
<BubbleTip
|
||||||
}}</SButton>
|
v-if="showGuide"
|
||||||
|
type="long"
|
||||||
|
:location="{ top: '-50%', left: '50%' }"
|
||||||
|
>
|
||||||
|
<text :style="{ transform: 'translateY(-18rpx)' }">{{
|
||||||
|
step === 1 ? "学会了,我摆得比教练还帅" : "我找到合适的点位了"
|
||||||
|
}}</text>
|
||||||
|
</BubbleTip>
|
||||||
|
{{ stepButtonTexts[step] }}
|
||||||
|
</SButton>
|
||||||
</view>
|
</view>
|
||||||
</Container>
|
</Container>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import AppBackground from "@/components/AppBackground.vue";
|
|||||||
import UserHeader from "@/components/UserHeader.vue";
|
import UserHeader from "@/components/UserHeader.vue";
|
||||||
import SModal from "@/components/SModal.vue";
|
import SModal from "@/components/SModal.vue";
|
||||||
import Signin from "@/components/Signin.vue";
|
import Signin from "@/components/Signin.vue";
|
||||||
|
import BubbleTip from "@/components/BubbleTip.vue";
|
||||||
import {
|
import {
|
||||||
getAppConfig,
|
getAppConfig,
|
||||||
getHomeData,
|
getHomeData,
|
||||||
@@ -21,6 +22,7 @@ const { updateConfig, updateUser, updateDevice, updateRank } = store;
|
|||||||
const { user, device, rankData } = storeToRefs(store);
|
const { user, device, rankData } = storeToRefs(store);
|
||||||
const showModal = ref(false);
|
const showModal = ref(false);
|
||||||
const isIos = ref(true);
|
const isIos = ref(true);
|
||||||
|
const showGuide = ref(false);
|
||||||
|
|
||||||
const toPage = (path) => {
|
const toPage = (path) => {
|
||||||
if (!user.value.id) {
|
if (!user.value.id) {
|
||||||
@@ -67,6 +69,12 @@ onMounted(async () => {
|
|||||||
console.log("首页数据:", result);
|
console.log("首页数据:", result);
|
||||||
if (result.user) {
|
if (result.user) {
|
||||||
updateUser(result.user);
|
updateUser(result.user);
|
||||||
|
if (result.user.trio <= 0) {
|
||||||
|
showGuide.value = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
showGuide.value = false;
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
const devices = await getMyDevicesAPI();
|
const devices = await getMyDevicesAPI();
|
||||||
if (devices.bindings && devices.bindings.length) {
|
if (devices.bindings && devices.bindings.length) {
|
||||||
updateDevice(
|
updateDevice(
|
||||||
@@ -114,6 +122,13 @@ const comingSoon = () => {
|
|||||||
mode="widthFix"
|
mode="widthFix"
|
||||||
@click="() => toPage('/pages/first-try')"
|
@click="() => toPage('/pages/first-try')"
|
||||||
/>
|
/>
|
||||||
|
<BubbleTip
|
||||||
|
v-if="showGuide"
|
||||||
|
:location="{ top: '60%', left: '40%', fontSize: '14px' }"
|
||||||
|
>
|
||||||
|
<text>新人必刷!</text>
|
||||||
|
<text>快来报到吧~</text>
|
||||||
|
</BubbleTip>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="practice-card" @click="() => toPage('/pages/practise')">
|
<view class="practice-card" @click="() => toPage('/pages/practise')">
|
||||||
@@ -263,8 +278,8 @@ const comingSoon = () => {
|
|||||||
transform: scaleY(1.08) translateY(9px);
|
transform: scaleY(1.08) translateY(9px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.bow-card > image:last-child {
|
.bow-card > image:nth-child(3) {
|
||||||
transform: translateY(12px);
|
transform: translateY(11px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.practice-card {
|
.practice-card {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import SButton from "@/components/SButton.vue";
|
|||||||
import Avatar from "@/components/Avatar.vue";
|
import Avatar from "@/components/Avatar.vue";
|
||||||
import BowPower from "@/components/BowPower.vue";
|
import BowPower from "@/components/BowPower.vue";
|
||||||
import TestDistance from "@/components/TestDistance.vue";
|
import TestDistance from "@/components/TestDistance.vue";
|
||||||
|
import BubbleTip from "@/components/BubbleTip.vue";
|
||||||
import { createPractiseAPI, getHomeData } from "@/apis";
|
import { createPractiseAPI, getHomeData } from "@/apis";
|
||||||
import { generateCanvasImage } from "@/util";
|
import { generateCanvasImage } from "@/util";
|
||||||
import { MESSAGETYPES, roundsName } from "@/constants";
|
import { MESSAGETYPES, roundsName } from "@/constants";
|
||||||
@@ -17,13 +18,14 @@ import { storeToRefs } from "pinia";
|
|||||||
const store = useStore();
|
const store = useStore();
|
||||||
const { user } = storeToRefs(store);
|
const { user } = storeToRefs(store);
|
||||||
const { updateUser } = store;
|
const { updateUser } = store;
|
||||||
const start = ref(false);
|
const start = ref(true);
|
||||||
const scores = ref([]);
|
const scores = ref([]);
|
||||||
const total = 12;
|
const total = 12;
|
||||||
const currentRound = ref(0);
|
const currentRound = ref(0);
|
||||||
const practiseResult = ref({});
|
const practiseResult = ref({});
|
||||||
const power = ref(0);
|
const power = ref(0);
|
||||||
const practiseId = ref("");
|
const practiseId = ref("");
|
||||||
|
const showGuide = ref(false);
|
||||||
|
|
||||||
const onReady = async () => {
|
const onReady = async () => {
|
||||||
const result = await createPractiseAPI(total);
|
const result = await createPractiseAPI(total);
|
||||||
@@ -42,6 +44,12 @@ async function onReceiveMessage(messages = []) {
|
|||||||
if (currentRound.value === 4) {
|
if (currentRound.value === 4) {
|
||||||
currentRound.value = 1;
|
currentRound.value = 1;
|
||||||
}
|
}
|
||||||
|
if (scores.value.length === total / 2) {
|
||||||
|
showGuide.value = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
showGuide.value = false;
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
power.value = msg.target.battery;
|
power.value = msg.target.battery;
|
||||||
}
|
}
|
||||||
@@ -100,6 +108,13 @@ onUnmounted(() => {
|
|||||||
<view class="user-row">
|
<view class="user-row">
|
||||||
<Avatar :src="user.avatar" :size="35" />
|
<Avatar :src="user.avatar" :size="35" />
|
||||||
<BowPower :power="power" />
|
<BowPower :power="power" />
|
||||||
|
<BubbleTip
|
||||||
|
v-if="showGuide"
|
||||||
|
:location="{ top: '40%', left: '5%', paddingLeft: '16rpx' }"
|
||||||
|
>
|
||||||
|
<text>还有两场,坚持</text>
|
||||||
|
<text>就是胜利!💪</text>
|
||||||
|
</BubbleTip>
|
||||||
</view>
|
</view>
|
||||||
<BowTarget
|
<BowTarget
|
||||||
:start="start"
|
:start="start"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import SButton from "@/components/SButton.vue";
|
|||||||
import Avatar from "@/components/Avatar.vue";
|
import Avatar from "@/components/Avatar.vue";
|
||||||
import BowPower from "@/components/BowPower.vue";
|
import BowPower from "@/components/BowPower.vue";
|
||||||
import TestDistance from "@/components/TestDistance.vue";
|
import TestDistance from "@/components/TestDistance.vue";
|
||||||
|
import BubbleTip from "@/components/BubbleTip.vue";
|
||||||
import { createPractiseAPI, getHomeData } from "@/apis";
|
import { createPractiseAPI, getHomeData } from "@/apis";
|
||||||
import { generateCanvasImage } from "@/util";
|
import { generateCanvasImage } from "@/util";
|
||||||
import { MESSAGETYPES } from "@/constants";
|
import { MESSAGETYPES } from "@/constants";
|
||||||
@@ -23,6 +24,7 @@ const total = 36;
|
|||||||
const practiseResult = ref({});
|
const practiseResult = ref({});
|
||||||
const power = ref(0);
|
const power = ref(0);
|
||||||
const practiseId = ref("");
|
const practiseId = ref("");
|
||||||
|
const showGuide = ref(false);
|
||||||
|
|
||||||
const onReady = async () => {
|
const onReady = async () => {
|
||||||
const result = await createPractiseAPI(total);
|
const result = await createPractiseAPI(total);
|
||||||
@@ -36,6 +38,12 @@ async function onReceiveMessage(messages = []) {
|
|||||||
if (msg.constructor === MESSAGETYPES.ShootSyncMeArrowID) {
|
if (msg.constructor === MESSAGETYPES.ShootSyncMeArrowID) {
|
||||||
if (scores.value.length < total) {
|
if (scores.value.length < total) {
|
||||||
scores.value.push(msg.target);
|
scores.value.push(msg.target);
|
||||||
|
if (scores.value.length === total / 2) {
|
||||||
|
showGuide.value = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
showGuide.value = false;
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
power.value = msg.target.battery;
|
power.value = msg.target.battery;
|
||||||
}
|
}
|
||||||
@@ -94,6 +102,13 @@ onUnmounted(() => {
|
|||||||
<view class="user-row">
|
<view class="user-row">
|
||||||
<Avatar :src="user.avatar" :size="35" />
|
<Avatar :src="user.avatar" :size="35" />
|
||||||
<BowPower :power="power" />
|
<BowPower :power="power" />
|
||||||
|
<BubbleTip
|
||||||
|
v-if="showGuide"
|
||||||
|
:location="{ top: '40%', left: '5%', paddingLeft: '16rpx' }"
|
||||||
|
>
|
||||||
|
<text>完成过半,胜利</text>
|
||||||
|
<text>在望!💪</text>
|
||||||
|
</BubbleTip>
|
||||||
</view>
|
</view>
|
||||||
<BowTarget
|
<BowTarget
|
||||||
:start="start"
|
:start="start"
|
||||||
|
|||||||
BIN
src/static/bubble-tip.png
Normal file
BIN
src/static/bubble-tip.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/static/bubble-tip2.png
Normal file
BIN
src/static/bubble-tip2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Reference in New Issue
Block a user