Files
shoot-miniprograms/src/pages/my-device.vue
2025-10-31 10:22:02 +08:00

456 lines
12 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 } from "vue";
import { onShow } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import ScreenHint from "@/components/ScreenHint.vue";
import SButton from "@/components/SButton.vue";
import {
bindDeviceAPI,
getMyDevicesAPI,
unbindDeviceAPI,
laserAimAPI,
} from "@/apis";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const showTip = ref(false);
const confirmBindTip = ref(false);
const addDevice = ref();
const store = useStore();
const { updateDevice } = store;
const { user, device } = storeToRefs(store);
const justBind = ref(false);
const calibration = ref(false);
// 扫描二维码方法
const handleScan = () => {
// 调用扫码API
uni.scanCode({
// 只支持扫码二维码
onlyFromCamera: true,
scanType: ["qrCode"],
success: async (res) => {
try {
const base64Decode = (str) => {
// 将 base64 转换为 utf8 字符串
const bytes = wx.base64ToArrayBuffer(str);
return String.fromCharCode.apply(null, new Uint8Array(bytes));
};
addDevice.value = JSON.parse(base64Decode(res.result));
confirmBindTip.value = true;
} catch (err) {
uni.showToast({
title: "无效二维码",
icon: "none",
duration: 2000,
});
}
},
fail: (err) => {
console.error("扫码失败:", err);
uni.showToast({
title: "扫码失败",
icon: "error",
});
},
});
};
const confirmBind = async () => {
if (!justBind.value && addDevice.value.id) {
const result = await bindDeviceAPI(addDevice.value);
confirmBindTip.value = false;
if (result.binded) {
return uni.showToast({
title: "设备已绑定其他账号,请解绑后再绑定",
icon: "none",
});
}
updateDevice(addDevice.value.id, addDevice.value.name);
justBind.value = true;
uni.showToast({
title: "绑定成功",
icon: "success",
});
}
};
const toFristTryPage = () => {
uni.navigateTo({
url: "/pages/first-try",
});
};
const unbindDevice = async () => {
await unbindDeviceAPI(device.value.deviceId);
uni.setStorageSync("calibration", false);
uni.showToast({
title: "解绑成功",
icon: "success",
});
device.value = {};
};
const toDeviceIntroPage = () => {
uni.navigateTo({
url: "/pages/device-intro",
});
};
const backToHome = () => {
uni.navigateBack();
};
const copyEmail = () => {
uni.setClipboardData({
data: "shelingxingqiu@163.com",
success: () => {
uni.showToast({
title: "邮箱已复制",
icon: "success",
});
},
});
};
const goCalibration = async () => {
await laserAimAPI();
uni.navigateTo({
url: "/pages/calibration",
});
};
onShow(() => {
calibration.value = uni.getStorageSync("calibration");
});
</script>
<template>
<Container title="弓箭绑定">
<view v-if="!device.deviceId" class="scan-code">
<button hover-class="none" @click="handleScan">
<image src="../static/scan.png" mode="widthFix" />
</button>
<button hover-class="none" @click="showTip = true">
<text></text>
<text :style="{ color: '#fed847' }">射灵弓箭</text>
<text>上的二维码</text>
<image src="../static/s-question-mark-white.png" mode="widthFix" />
</button>
<text>射灵智能弓箭三模传感系统与独创靶环算法</text>
<text>毫秒级在线实时对战让你拥有全球约战的乐趣</text>
<button hover-class="none" @click="toDeviceIntroPage">
<image src="../static/have-no-device.png" mode="widthFix" />
</button>
<ScreenHint
mode="square"
:show="showTip"
:onClose="() => (showTip = false)"
>
<view class="scan-tips">
<text>扫码绑定设灵弓箭</text>
<image
src="https://static.shelingxingqiu.com/attachment/2025-08-05/dbuacrelri7jr3axiy.png"
mode="widthFix"
/>
<text>已被绑定的弓箭无法再次绑定</text>
<view>
<text>如有任何疑问请随时联系</text>
<button hover-class="none" @click="copyEmail">
shelingxingqiu@163.com
</button>
</view>
</view>
</ScreenHint>
<ScreenHint
:show="confirmBindTip"
:onClose="() => (confirmBindTip = false)"
>
<view class="confirm-bind">
<text
>智能弓箭和系统账号需一一对应你确定要将<text
:style="{ color: '#fed847' }"
>当前登录用户账号</text
>绑定<text :style="{ color: '#fed847' }">这把弓箭</text>
绑定后不可随意更换</text
>
<view>
<view @click="confirmBind">确认绑定</view>
<view @click="() => (confirmBindTip = false)">取消</view>
</view>
</view>
</ScreenHint>
</view>
<view v-if="justBind" class="just-bind">
<view
class="device-binded"
:style="{ marginBottom: calibration ? '250rpx' : '100rpx' }"
>
<view>
<image src="../static/device-icon.png" mode="widthFix" />
<text>{{ device.deviceName }}</text>
<view class="calibration" v-if="calibration">
<button hover-class="none" @click="goCalibration">
<text>重新校准</text>
<image src="../static/enter-arrow-blue.png" mode="widthFix" />
</button>
<view>
<image src="../static/calibration-tip.png" mode="widthFix" />
<text>如有场地/距离变化需重新校准以保证智能弓射箭精准度</text>
</view>
</view>
</view>
<image src="../static/bind-success.png" mode="widthFix" />
<view>
<image
:src="user.avatar || '../static/user-icon.png'"
mode="widthFix"
:style="{ borderRadius: '50%' }"
/>
<text>{{ user.nickName }}</text>
</view>
</view>
<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>
<text>恭喜你的弓箭和账号已成功绑定</text>
<text :style="{ color: '#fed847' }">已赠送6个月射灵世界会员</text>
</view>
<SButton :onClick="goCalibration" width="60vw" :rounded="40">
开启智能弓进行校准
</SButton>
<text :style="{ marginTop: '20rpx', fontSize: '20rpx', color: '#fff9' }"
>校准时弓箭激光将开启请勿直视激光</text
>
</block>
</view>
<view v-if="device.deviceId && !justBind" class="has-device">
<view class="device-binded">
<view>
<image src="../static/device-icon.png" mode="widthFix" />
<text>{{ device.deviceName }}</text>
<view class="calibration">
<button hover-class="none" @click="goCalibration">
<text>去校准</text>
<image src="../static/enter-arrow-blue.png" mode="widthFix" />
</button>
<view>
<image src="../static/calibration-tip.png" mode="widthFix" />
<text
>首次绑定智能弓或场地/距离变化时应进行校准以确保射箭精度</text
>
</view>
</view>
</view>
<image src="../static/bind.png" mode="widthFix" />
<view>
<image
:src="user.avatar || '../static/user-icon.png'"
mode="widthFix"
:style="{ borderRadius: '50%' }"
/>
<text>{{ user.nickName }}</text>
</view>
</view>
<view :style="{ marginTop: '240rpx' }">
<SButton :onClick="unbindDevice" width="80vw" :rounded="40"
>解绑</SButton
>
</view>
</view>
</Container>
</template>
<style scoped>
.scan-code,
.just-bind,
.has-device {
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-around;
width: 100%;
height: 100%;
}
.scan-code {
justify-content: flex-start;
}
.scan-code > button:first-child {
margin-top: 22%;
}
.scan-code > button:first-child > image {
width: 300rpx;
}
.scan-code > button:nth-child(2) {
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
color: #ffffff;
margin: 50rpx;
}
.scan-code > button:nth-child(2) > image {
width: 28rpx;
margin-left: 10rpx;
}
.scan-code > text {
font-size: 24rpx;
color: #fff9;
}
.scan-code > button:nth-child(5) {
margin-top: 25%;
}
.scan-code > button:nth-child(5) > image {
width: 380rpx;
}
.scan-tips {
display: flex;
flex-direction: column;
font-size: 14px;
width: 90%;
margin-top: 20%;
}
.scan-tips > text {
margin-bottom: 2px;
color: #fff;
font-size: 24rpx;
}
.scan-tips > text:first-child {
color: #fed847;
margin-bottom: 10px;
font-size: 32rpx;
}
.scan-tips > view {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.scan-tips > view:last-child {
margin-top: 5px;
font-size: 26rpx;
}
.scan-tips > view:last-child > button {
font-size: 30rpx;
color: #39a8ff;
}
.scan-tips > image {
width: 100%;
margin-bottom: 10px;
}
.confirm-bind {
color: #fff9;
font-size: 14px;
}
.confirm-bind > view:last-child {
display: flex;
justify-content: space-between;
margin-top: 10px;
}
.confirm-bind > view:last-child > view {
width: 48%;
border-radius: 20px;
background-color: #fed847;
color: #000;
line-height: 40px;
text-align: center;
}
.confirm-bind > view:last-child > view:nth-child(2) {
color: #fff;
background-color: #fff3;
}
.device-binded {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 14px;
margin-top: 200rpx;
}
.device-binded > view {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.device-binded > view > image {
width: 140rpx;
height: 140rpx;
margin-bottom: 5px;
border-radius: 10px;
}
.device-binded > view > text {
width: 120px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
}
.device-binded > image {
width: 100rpx;
margin: 0 20px;
}
.has-device,
.just-bind {
justify-content: flex-start;
}
.has-device > view:nth-child(2),
.just-bind > view:nth-child(2) {
color: #fff9;
display: flex;
flex-direction: column;
align-items: center;
font-size: 14px;
margin-bottom: 100rpx;
}
.has-device > view:nth-child(2) > text,
.just-bind > view:nth-child(2) > text {
margin: 5px;
}
.calibration {
position: absolute;
bottom: -145rpx;
left: 20rpx;
}
.calibration > button {
font-size: 22rpx;
color: #287fff;
display: flex;
align-items: center;
padding-bottom: 15rpx;
padding-left: 50rpx;
}
.calibration > button > image {
width: 28rpx;
height: 28rpx;
}
.calibration > view {
position: relative;
font-size: 20rpx;
color: #fff9;
padding-top: 40rpx;
padding-left: 35rpx;
width: 300rpx;
}
.calibration > view > image {
position: absolute;
top: 0;
left: 0;
width: 370rpx;
}
</style>