完成单组练习接口调试
This commit is contained in:
82
src/apis.js
82
src/apis.js
@@ -1,5 +1,12 @@
|
|||||||
const BASE_URL = "http://120.79.241.5:8000/api/shoot";
|
const BASE_URL = "http://120.79.241.5:8000/api/shoot";
|
||||||
|
|
||||||
|
function getAuthHeader() {
|
||||||
|
const token = uni.getStorageSync("token");
|
||||||
|
return {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// 获取全局配置
|
// 获取全局配置
|
||||||
export const getAppConfig = () => {
|
export const getAppConfig = () => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -72,6 +79,81 @@ export const loginAPI = (nickName, avatarUrl, code) => {
|
|||||||
avatarUrl,
|
avatarUrl,
|
||||||
code,
|
code,
|
||||||
},
|
},
|
||||||
|
success: (res) => {
|
||||||
|
const { code, data } = res.data;
|
||||||
|
if (code === 0) {
|
||||||
|
uni.setStorageSync("token", data.token);
|
||||||
|
uni.setStorageSync("tokenExpire", data.expires + Date.now());
|
||||||
|
resolve(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
reject(err);
|
||||||
|
uni.showToast({
|
||||||
|
title: "获取数据失败",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const bindDeviceAPI = (device) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.request({
|
||||||
|
url: `${BASE_URL}/user/device/bindDevice`,
|
||||||
|
method: "POST",
|
||||||
|
header: getAuthHeader(),
|
||||||
|
data: {
|
||||||
|
device,
|
||||||
|
},
|
||||||
|
success: (res) => {
|
||||||
|
const { code, data } = res.data;
|
||||||
|
if (code === 0) {
|
||||||
|
resolve(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
reject(err);
|
||||||
|
uni.showToast({
|
||||||
|
title: "获取数据失败",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getMyDeviceAPI = () => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.request({
|
||||||
|
url: `${BASE_URL}/user/device/getBinding?deviceId=9ZF9oVXs`,
|
||||||
|
// url: `${BASE_URL}/user/device/getBindings`,
|
||||||
|
method: "GET",
|
||||||
|
header: getAuthHeader(),
|
||||||
|
success: (res) => {
|
||||||
|
resolve(res.data);
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
reject(err);
|
||||||
|
uni.showToast({
|
||||||
|
title: "获取数据失败",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createPractiseAPI = (arrows) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
uni.request({
|
||||||
|
url: `${BASE_URL}/user/practice/create`,
|
||||||
|
method: "POST",
|
||||||
|
header: getAuthHeader(),
|
||||||
|
data: {
|
||||||
|
arrows,
|
||||||
|
},
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
const { code, data } = res.data;
|
const { code, data } = res.data;
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import BowPower from "@/components/BowPower.vue";
|
import BowPower from "@/components/BowPower.vue";
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
totalRound: {
|
totalRound: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
@@ -25,7 +25,20 @@ defineProps({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
scores: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function calcRealX(num) {
|
||||||
|
const len = 20 + num;
|
||||||
|
return `calc(${(len / 40) * 100}% - 10px)`;
|
||||||
|
}
|
||||||
|
function calcRealY(num) {
|
||||||
|
const len = num < 0 ? Math.abs(num) + 20 : 20 - num;
|
||||||
|
return `calc(${(len / 40) * 100}% - 10px)`;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -37,7 +50,19 @@ defineProps({
|
|||||||
}}</text>
|
}}</text>
|
||||||
<BowPower v-if="power > 0" :power="power" />
|
<BowPower v-if="power > 0" :power="power" />
|
||||||
</view>
|
</view>
|
||||||
|
<view class="target">
|
||||||
|
<image
|
||||||
|
v-for="(bow, index) in scores"
|
||||||
|
:key="index"
|
||||||
|
src="../static/hit-icon.png"
|
||||||
|
:class="`hit ${index + 1 === scores.length ? 'pump-in' : ''}`"
|
||||||
|
:style="{
|
||||||
|
left: calcRealX(bow.x),
|
||||||
|
top: calcRealY(bow.y),
|
||||||
|
}"
|
||||||
|
/>
|
||||||
<image src="../static/bow-target.png" mode="widthFix" />
|
<image src="../static/bow-target.png" mode="widthFix" />
|
||||||
|
</view>
|
||||||
<view v-if="avatar" class="footer">
|
<view v-if="avatar" class="footer">
|
||||||
<image :src="avatar" mode="widthFix" />
|
<image :src="avatar" mode="widthFix" />
|
||||||
</view>
|
</view>
|
||||||
@@ -50,9 +75,29 @@ defineProps({
|
|||||||
width: calc(100% - 30px);
|
width: calc(100% - 30px);
|
||||||
margin: 15px;
|
margin: 15px;
|
||||||
}
|
}
|
||||||
.container > image {
|
.target {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.target > image:last-child {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
@keyframes pumpIn {
|
||||||
|
from {
|
||||||
|
transform: scale(2);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pump-in {
|
||||||
|
animation: pumpIn 0.3s ease-out forwards;
|
||||||
|
transform-origin: center center;
|
||||||
|
}
|
||||||
|
.hit {
|
||||||
|
position: absolute;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
.header {
|
.header {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -6,10 +6,6 @@ const props = defineProps({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
content: {
|
|
||||||
type: String,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
onClose: {
|
onClose: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
@@ -38,7 +34,7 @@ const props = defineProps({
|
|||||||
</view>
|
</view>
|
||||||
<IconButton
|
<IconButton
|
||||||
src="../static/close-gold-outline.png"
|
src="../static/close-gold-outline.png"
|
||||||
width="30"
|
:width="30"
|
||||||
:onClick="onClose"
|
:onClick="onClose"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
width: {
|
width: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: "22",
|
default: 22,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
scores: {
|
scores: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@@ -15,7 +14,7 @@ const getSum = (a, b, c) => {
|
|||||||
<view class="container">
|
<view class="container">
|
||||||
<view>
|
<view>
|
||||||
<text>总成绩</text>
|
<text>总成绩</text>
|
||||||
<text>23环</text>
|
<text>{{ scores.reduce((last, next) => last + next, 0) }}环</text>
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
v-for="(title, index) in ['第一轮', '第二轮', '第三轮']"
|
v-for="(title, index) in ['第一轮', '第二轮', '第三轮']"
|
||||||
@@ -24,23 +23,25 @@ const getSum = (a, b, c) => {
|
|||||||
>
|
>
|
||||||
<text>{{ title }}</text>
|
<text>{{ title }}</text>
|
||||||
<text>{{
|
<text>{{
|
||||||
scores[index * 3 + 0] ? scores[index * 3 + 0] + "环" : "-"
|
scores[index * 4 + 0] ? scores[index * 4 + 0] + "环" : "-"
|
||||||
}}</text>
|
}}</text>
|
||||||
<text>{{
|
<text>{{
|
||||||
scores[index * 3 + 1] ? scores[index * 3 + 1] + "环" : "-"
|
scores[index * 4 + 1] ? scores[index * 4 + 1] + "环" : "-"
|
||||||
}}</text>
|
}}</text>
|
||||||
<text>{{
|
<text>{{
|
||||||
scores[index * 3 + 2] ? scores[index * 3 + 2] + "环" : "-"
|
scores[index * 4 + 2] ? scores[index * 4 + 2] + "环" : "-"
|
||||||
}}</text>
|
}}</text>
|
||||||
<text
|
<text>{{
|
||||||
>{{
|
scores[index * 4 + 3] ? scores[index * 4 + 3] + "环" : "-"
|
||||||
|
}}</text>
|
||||||
|
<text>{{
|
||||||
getSum(
|
getSum(
|
||||||
scores[index * 3 + 0],
|
scores[index * 4 + 0],
|
||||||
scores[index * 3 + 1],
|
scores[index * 4 + 1],
|
||||||
scores[index * 3 + 2]
|
scores[index * 4 + 2],
|
||||||
|
scores[index * 4 + 3]
|
||||||
)
|
)
|
||||||
}}</text
|
}}</text>
|
||||||
>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -20,8 +20,11 @@ const props = defineProps({
|
|||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
|
scores: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const items = ref(new Array(props.total).fill(9));
|
|
||||||
const showPanel = ref(true);
|
const showPanel = ref(true);
|
||||||
const showComment = ref(false);
|
const showComment = ref(false);
|
||||||
const closePanel = () => {
|
const closePanel = () => {
|
||||||
@@ -40,14 +43,22 @@ setTimeout(() => {
|
|||||||
<view :class="['container-header', showPanel ? 'scale-in' : 'scale-out']">
|
<view :class="['container-header', showPanel ? 'scale-in' : 'scale-out']">
|
||||||
<image src="../static/finish-tip.png" mode="widthFix" />
|
<image src="../static/finish-tip.png" mode="widthFix" />
|
||||||
<image src="../static/finish-frame.png" mode="widthFix" />
|
<image src="../static/finish-frame.png" mode="widthFix" />
|
||||||
<text>完成36箭,获得36点经验</text>
|
<text
|
||||||
|
>完成{{ total }}箭,获得{{
|
||||||
|
scores.reduce((last, next) => last + next, 0)
|
||||||
|
}}点经验</text
|
||||||
|
>
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
class="container-content"
|
class="container-content"
|
||||||
:style="{ transform: `translateY(${showPanel ? '0%' : '100%'})` }"
|
:style="{ transform: `translateY(${showPanel ? '0%' : '100%'})` }"
|
||||||
>
|
>
|
||||||
<view>
|
<view>
|
||||||
<text>本剧成绩(共{{ total }}环):</text>
|
<text
|
||||||
|
>本剧成绩(共{{
|
||||||
|
scores.reduce((last, next) => last + next, 0)
|
||||||
|
}}环):</text
|
||||||
|
>
|
||||||
<button>
|
<button>
|
||||||
<text>查看靶纸</text>
|
<text>查看靶纸</text>
|
||||||
<image
|
<image
|
||||||
@@ -58,7 +69,7 @@ setTimeout(() => {
|
|||||||
</button>
|
</button>
|
||||||
</view>
|
</view>
|
||||||
<view :style="{ gridTemplateColumns: `repeat(${rowCount}, 1fr)` }">
|
<view :style="{ gridTemplateColumns: `repeat(${rowCount}, 1fr)` }">
|
||||||
<view v-for="(score, index) in items" :key="index">
|
<view v-for="(score, index) in scores" :key="index">
|
||||||
{{ score }}<text>环</text>
|
{{ score }}<text>环</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, watch } from "vue";
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
start: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
tips: {
|
tips: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: "",
|
||||||
@@ -10,18 +14,25 @@ const props = defineProps({
|
|||||||
default: 90,
|
default: 90,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let barColor = "#fed847";
|
let barColor = "#fed847";
|
||||||
if (props.tips.includes("红队")) barColor = "#FF6060";
|
if (props.tips.includes("红队")) barColor = "#FF6060";
|
||||||
if (props.tips.includes("蓝队")) barColor = "#5FADFF";
|
if (props.tips.includes("蓝队")) barColor = "#5FADFF";
|
||||||
const remain = ref(0);
|
const remain = ref(props.total);
|
||||||
onMounted(() => {
|
|
||||||
|
watch(
|
||||||
|
() => props.start,
|
||||||
|
(newVal, oldVal) => {
|
||||||
|
if (oldVal === false && newVal === true) {
|
||||||
remain.value = props.total;
|
remain.value = props.total;
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
if (remain.value > 0) {
|
if (remain.value > 0) {
|
||||||
remain.value--;
|
remain.value--;
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
3
src/constants.js
Normal file
3
src/constants.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export const MESSAGETYPES = {
|
||||||
|
ShootSyncMeArrowID: parseInt("0x789b6b0d"),
|
||||||
|
};
|
||||||
@@ -3,38 +3,17 @@ import Guide from "@/components/Guide.vue";
|
|||||||
import BowTarget from "@/components/BowTarget.vue";
|
import BowTarget from "@/components/BowTarget.vue";
|
||||||
import SButton from "@/components/SButton.vue";
|
import SButton from "@/components/SButton.vue";
|
||||||
import Container from "@/components/Container.vue";
|
import Container from "@/components/Container.vue";
|
||||||
|
import { getMyDeviceAPI } from "@/apis";
|
||||||
|
|
||||||
// 扫描二维码方法
|
const getMyDevice = async () => {
|
||||||
const handleScan = () => {
|
const result = await getMyDeviceAPI();
|
||||||
console.log('开始扫码');
|
console.log("我的设备:", result);
|
||||||
// 调用扫码API
|
|
||||||
uni.scanCode({
|
|
||||||
// 只支持扫码二维码
|
|
||||||
onlyFromCamera: true,
|
|
||||||
scanType: ['qrCode'],
|
|
||||||
success: (res) => {
|
|
||||||
// res.result 为二维码内容
|
|
||||||
console.log('扫码结果:', res.result);
|
|
||||||
uni.showToast({
|
|
||||||
title: '扫码成功',
|
|
||||||
icon: 'success'
|
|
||||||
});
|
|
||||||
// 这里可以处理扫码后的业务逻辑
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.error('扫码失败:', err);
|
|
||||||
uni.showToast({
|
|
||||||
title: '扫码失败',
|
|
||||||
icon: 'error'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Container bgType="1" title="弓箭调试">
|
<Container bgType="1" title="弓箭调试">
|
||||||
<Guide>
|
<!-- <Guide>
|
||||||
<view class="guide-tips">
|
<view class="guide-tips">
|
||||||
<text>请预先射几箭测试</text>
|
<text>请预先射几箭测试</text>
|
||||||
<text>确保射击距离有5米</text>
|
<text>确保射击距离有5米</text>
|
||||||
@@ -44,10 +23,10 @@ const handleScan = () => {
|
|||||||
avatar="../static/avatar.png"
|
avatar="../static/avatar.png"
|
||||||
:power="45"
|
:power="45"
|
||||||
tips="本次射程5.2米,已达距离要求"
|
tips="本次射程5.2米,已达距离要求"
|
||||||
/>
|
/> -->
|
||||||
<view>
|
<view>
|
||||||
<SButton>准备好了直接开始</SButton>
|
<!-- <SButton>准备好了直接开始</SButton> -->
|
||||||
<SButton :onClick="handleScan">扫码</SButton>
|
<SButton :onClick="getMyDevice">获取我的设备</SButton>
|
||||||
</view>
|
</view>
|
||||||
</Container>
|
</Container>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,36 +1,66 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
import { ref, onMounted, onUnmounted } from "vue";
|
||||||
import AppBackground from "@/components/AppBackground.vue";
|
import AppBackground from "@/components/AppBackground.vue";
|
||||||
import Header from "@/components/Header.vue";
|
import Header from "@/components/Header.vue";
|
||||||
import ShootProgress from "@/components/ShootProgress.vue";
|
import ShootProgress from "@/components/ShootProgress.vue";
|
||||||
import BowTarget from "@/components/BowTarget.vue";
|
import BowTarget from "@/components/BowTarget.vue";
|
||||||
import ScorePanel2 from "@/components/ScorePanel2.vue";
|
import ScorePanel2 from "@/components/ScorePanel2.vue";
|
||||||
import ScoreResult from "@/components/ScoreResult.vue";
|
import ScoreResult from "@/components/ScoreResult.vue";
|
||||||
|
import SButton from "@/components/SButton.vue";
|
||||||
|
import { createPractiseAPI } from "@/apis";
|
||||||
|
import { MESSAGETYPES } from "@/constants";
|
||||||
|
import websocket from "@/websocket";
|
||||||
|
const start = ref(false);
|
||||||
const showScore = ref(false);
|
const showScore = ref(false);
|
||||||
|
const scores = ref([]);
|
||||||
|
|
||||||
setTimeout(() => {
|
const onReady = async () => {
|
||||||
|
const result = await createPractiseAPI(12);
|
||||||
|
console.log("result", result);
|
||||||
|
start.value = true;
|
||||||
|
const token = uni.getStorageSync("token");
|
||||||
|
|
||||||
|
websocket.createWebSocket(token, (result) => {
|
||||||
|
const messages = JSON.parse(result).data.updates || [];
|
||||||
|
messages.forEach((msg) => {
|
||||||
|
if (msg.constructor === MESSAGETYPES.ShootSyncMeArrowID) {
|
||||||
|
scores.value.push(msg.target);
|
||||||
|
console.log("msg:", msg.target);
|
||||||
|
if (scores.value.length === 12) {
|
||||||
showScore.value = true;
|
showScore.value = true;
|
||||||
}, 2000);
|
websocket.closeWebSocket();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
websocket.closeWebSocket();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="container">
|
<view class="container">
|
||||||
<AppBackground type="1" />
|
<AppBackground :type="1" />
|
||||||
<Header title="个人单组练习" />
|
<Header title="个人单组练习" />
|
||||||
<ShootProgress tips="请开始射箭第一轮" total="120" />
|
<ShootProgress tips="请开始射箭第一轮" :start="start" :total="120" />
|
||||||
<BowTarget
|
<BowTarget
|
||||||
totalRound="10"
|
:totalRound="12"
|
||||||
currentRound="4"
|
:currentRound="scores.length + 1"
|
||||||
avatar="../static/avatar.png"
|
avatar="../static/avatar.png"
|
||||||
power="45"
|
:power="45"
|
||||||
|
:scores="scores"
|
||||||
/>
|
/>
|
||||||
<ScorePanel2 :scores="[1, 2, 3, 4, 5, 6]" />
|
<ScorePanel2 v-if="start" :scores="scores.map((s) => s.ring)" />
|
||||||
<ScoreResult
|
<ScoreResult
|
||||||
:total="12"
|
:total="12"
|
||||||
:rowCount="6"
|
:rowCount="6"
|
||||||
:show="showScore"
|
:show="showScore"
|
||||||
:onClose="() => (showScore = false)"
|
:onClose="() => (showScore = false)"
|
||||||
|
:scores="scores.map((s) => s.ring)"
|
||||||
/>
|
/>
|
||||||
|
<SButton v-if="!start" :onClick="onReady">准备好了,直接开始</SButton>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ const toRankIntroPage = () => {
|
|||||||
:onClick="toOrderPage"
|
:onClick="toOrderPage"
|
||||||
/>
|
/>
|
||||||
<UserItem title="新手试炼场" :onClick="toFristTryPage">
|
<UserItem title="新手试炼场" :onClick="toFristTryPage">
|
||||||
<text v-if="user.trio" :style="{ color: '#259249' }">已完成</text>
|
<text v-if="user.trio > 1" :style="{ color: '#259249' }">已完成</text>
|
||||||
<text v-else :style="{ color: '#CC311F' }">未完成</text>
|
<text v-else :style="{ color: '#CC311F' }">未完成</text>
|
||||||
</UserItem>
|
</UserItem>
|
||||||
<UserItem title="会员" :onClick="toBeVipPage"> 已赠送6个月会员 </UserItem>
|
<UserItem title="会员" :onClick="toBeVipPage"> 已赠送6个月会员 </UserItem>
|
||||||
|
|||||||
BIN
src/static/hit-icon.png
Normal file
BIN
src/static/hit-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 524 B |
12
src/store.js
12
src/store.js
@@ -8,7 +8,11 @@ export default defineStore("store", {
|
|||||||
id: "",
|
id: "",
|
||||||
nickName: "游客",
|
nickName: "游客",
|
||||||
avatarUrl: "../static/avatar.png",
|
avatarUrl: "../static/avatar.png",
|
||||||
trio: false, // 是否完成新手试炼
|
trio: 0, // 大于1表示完成了新手引导
|
||||||
|
},
|
||||||
|
device: {
|
||||||
|
id: "",
|
||||||
|
deviceName: "",
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@@ -24,6 +28,10 @@ export default defineStore("store", {
|
|||||||
updateUser(user) {
|
updateUser(user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
},
|
},
|
||||||
|
updateDevice(deviceId, deviceName) {
|
||||||
|
this.device.id = deviceId;
|
||||||
|
this.device.deviceName = deviceName;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// 开启数据持久化
|
// 开启数据持久化
|
||||||
@@ -32,7 +40,7 @@ export default defineStore("store", {
|
|||||||
strategies: [
|
strategies: [
|
||||||
{
|
{
|
||||||
storage: uni.getStorageSync,
|
storage: uni.getStorageSync,
|
||||||
paths: ["user"], // 只持久化用户信息
|
paths: ["user", "device"], // 只持久化用户信息
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,31 +1,18 @@
|
|||||||
// utils/websocket.js
|
|
||||||
|
|
||||||
let socket = null;
|
let socket = null;
|
||||||
let messageCallback = null;
|
let heartbeatInterval = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 连接 WebSocket
|
* 建立 WebSocket 连接
|
||||||
* @param {String} url WebSocket 地址
|
|
||||||
* @param {Function} onMessage 消息回调
|
|
||||||
*/
|
*/
|
||||||
function connectWebSocket(token, onMessage) {
|
function createWebSocket(token, onMessage) {
|
||||||
messageCallback = onMessage;
|
|
||||||
|
|
||||||
socket = uni.connectSocket({
|
socket = uni.connectSocket({
|
||||||
url: `ws://120.79.241.5:8000/socket?authorization=${token}`,
|
url: `ws://120.79.241.5:8000/socket?authorization=${token}`,
|
||||||
success: () => {
|
success: () => console.log("websocket 连接成功"),
|
||||||
console.log("连接建立成功");
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.error("连接失败", err);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 接收消息
|
// 接收消息
|
||||||
uni.onSocketMessage((res) => {
|
uni.onSocketMessage((res) => {
|
||||||
if (messageCallback) {
|
if (onMessage) onMessage(res.data);
|
||||||
messageCallback(res.data);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 错误处理
|
// 错误处理
|
||||||
@@ -36,39 +23,46 @@ function connectWebSocket(token, onMessage) {
|
|||||||
// 关闭处理
|
// 关闭处理
|
||||||
uni.onSocketClose(() => {
|
uni.onSocketClose(() => {
|
||||||
console.log("WebSocket 已关闭");
|
console.log("WebSocket 已关闭");
|
||||||
socket = null;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 启动心跳
|
||||||
|
startHeartbeat();
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeWebSocket() {
|
||||||
|
if (socket) socket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送消息
|
* 启动心跳
|
||||||
* @param {String} msg 要发送的消息内容
|
|
||||||
*/
|
*/
|
||||||
function sendWebSocketMessage(msg) {
|
function startHeartbeat() {
|
||||||
|
stopHeartbeat(); // 防止重复启动
|
||||||
|
|
||||||
|
heartbeatInterval = setInterval(() => {
|
||||||
if (socket && uni.sendSocketMessage) {
|
if (socket && uni.sendSocketMessage) {
|
||||||
|
console.log("ping");
|
||||||
uni.sendSocketMessage({
|
uni.sendSocketMessage({
|
||||||
data: msg,
|
data: "ping",
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
console.error("发送消息失败", err);
|
console.error("发送心跳失败", err);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
console.warn("WebSocket 未连接");
|
|
||||||
}
|
}
|
||||||
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关闭连接
|
* 停止心跳
|
||||||
*/
|
*/
|
||||||
function closeWebSocket() {
|
function stopHeartbeat() {
|
||||||
if (socket) {
|
if (heartbeatInterval) {
|
||||||
uni.closeSocket();
|
clearInterval(heartbeatInterval);
|
||||||
socket = null;
|
heartbeatInterval = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
connectWebSocket,
|
createWebSocket,
|
||||||
sendWebSocketMessage,
|
|
||||||
closeWebSocket,
|
closeWebSocket,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user