2025-08-06 15:14:41 +08:00

588 lines
15 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.

<template>
<view class="page">
<BackBar title="扫码绑定" />
<view class="content">
<view class="robotCont">
<image :src="`${OSS_URL}/urm/bindDevice/jqr.png`" class="robot" mode="scaleToFill" />
<image :src="`${OSS_URL}/urm/bindDevice/jqrMsg.png`" class="robotMsg" mode="scaleToFill" />
</view>
<!-- 流程 -->
<view class="process">
<view class="title">
<image
class="UnionLeft"
:src="`${OSS_URL}/urm/bindDevice/Union.png`"
mode="scaleToFill"
/>
<text>扫码绑定流程</text>
<image
class="UnionRight"
:src="`${OSS_URL}/urm/bindDevice/Union.png`"
mode="scaleToFill"
/>
</view>
<!-- list -->
<view v-for="(item, index) in stepList" :key="index" class="list">
<view class="num">{{ item.num }}</view>
<view class="cont">
<view class="contentRight">
<text class="lest">{{ item.title }}</text>
<text class="lestCont">{{ item.content }}</text>
</view>
</view>
</view>
<!--扫码 -->
<view class="QRbutton" @click="queryBinding">
<wd-icon name="scan1" size="22px"></wd-icon>
<text class="QRbuttonText">扫描二维码</text>
</view>
</view>
<wd-popup v-model="show" custom-style="border-radius: 10px;">
<view
class="popuCont"
:style="{
height:
bindProject[0].bindFlag === 0 && bindProject[1].bindFlag === 0 ? '592rpx' : '648rpx',
}"
>
<text class="title">请确定您要绑定的项目</text>
<view class="option">
<view v-for="(item2, index2) in bindProject" :key="index2" class="bind_info">
<view :class="{ Select: item2.show, item: true }" @click="SelectProject(item2)">
<text>{{ item2.name }}</text>
<image :src="item2.icon" class="icons" mode="scaleToFill" />
<image
class="SelectIcon"
:src="
item2.show
? `${OSS_URL}/urm/bindDevice/SelectIcon.png`
: `${OSS_URL}/urm/bindDevice/NoSelectIcon.png`
"
mode="scaleToFill"
/>
</view>
<text v-if="item2.bindFlag === 1" class="bind_text">{{ item2.bindText }}</text>
</view>
</view>
<view v-for="(itCont, itIndex) in contList" :key="itIndex" class="contList">
<text>·</text>
<view>{{ itCont }}</view>
</view>
<!-- 确认 -->
<view class="confirm" @click="determine">确定</view>
</view>
</wd-popup>
<!-- 设备孩子账号已绑定 -->
<wd-popup v-model="showAllBindPopup" custom-style="border-radius: 10px;">
<view class="all_bind">
<text class="title">您已绑定当前设备和账号无需重复操作</text>
<!-- 确认 -->
<view class="confirm" @click="showAllBindPopup = false">确定</view>
</view>
</wd-popup>
<TipPopup
v-model="showSuccessPopup"
showBtn
:text="bindType === 'success' ? '绑定成功' : '绑定失败'"
:type="bindType"
:messageTip="successPopupText"
:btnText="bindType === 'success' ? '确定' : '好的'"
@backBtn="successPopupBtn"
/>
</view>
<!-- 扫码 -->
<!-- #ifdef H5 -->
<cshaptx4869-scancode
v-if="h5ScanCode"
@success="handleSuccess"
@fail="handleFail"
@close="handleClose"
></cshaptx4869-scancode>
<!-- #endif -->
</view>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { getCurInfo, parentBindAdmin } from '@/api'
import { userStore, shareConfigStore } from '@/store'
import { storeToRefs } from 'pinia'
import TipPopup from '@/components/TipPopup/index.vue'
import router from '@/router/router'
import jWeixin from 'weixin-js-sdk'
const { initWeixinShareConfig } = shareConfigStore()
const { userInfo } = storeToRefs(userStore())
const OSS_URL = import.meta.env.VITE_OSS_HOST
const show = ref(false)
const showAllBindPopup = ref(false)
const QRCode = ref() // 扫码结果
const childUserIds = ref()
const h5ScanCode = ref(false)
const resData = ref()
const showSuccessPopup = ref(false)
const bindType = ref('success')
const successPopupText = ref('')
const stepList = ref([
{
num: '01',
title: '第一步',
content: '打开学习机,来到「设置」界面;',
},
{
num: '02',
title: '第二步',
content: '点击「设备」,获取二维码,先扫码关注公众号;',
},
{
num: '03',
title: '第三步',
content: '进入公众号,点击「家长助手」扫码绑定,扫描二维码选择绑定设备/账号。',
},
])
// 绑定项目
const bindProject = ref([
{
name: '当前设备',
show: true,
icon: `${OSS_URL}/urm/bindDevice/bindIcon1.png`,
bindFlag: 0, // 0-未绑定 1-已绑定
adminFlag: false, // 是否已有管理员
bindText: '已绑定当前设备',
},
{
name: '孩子账号',
show: true,
icon: `${OSS_URL}/urm/bindDevice/bindIcon2.png`,
bindFlag: 0, // 0-未绑定 1-已绑定
adminFlag: false, // 是否已有管理员
bindText: '已绑定当前孩子账号',
},
])
const contList = ref([
'第一个绑定设备/帐号的用户自动成为管理员',
'绑定账号可以查看孩子的学情报告;',
'绑定设备可以对设备进行管控。',
])
async function queryBinding() {
const ua = navigator?.userAgent?.toLowerCase()
const isWechatBrowser = ua?.indexOf('micromessenger') !== -1
if (!isWechatBrowser) {
// 非微信环境
h5ScanCode.value = true
return
}
jWeixin.scanQRCode({
needResult: 1, // 默认为0扫描结果由微信处理1则直接返回扫描结果
scanType: ['qrCode', 'barCode'], // 可以指定扫二维码还是一维码,默认二者都有
success: async function (res) {
let result = res.resultStr
resData.value = JSON.parse(result)
QRCode.value = resData.value.simSerialNumber
childUserIds.value = resData.value.userId
await fetchCurBindInfo()
},
fail: function (response) {
uni.showToast({
title: '扫码失败',
icon: 'none',
})
},
})
}
function SelectProject(e: any) {
// 无管理员必选
if (!e.adminFlag) return
// 已绑定
if (e.bindFlag === 1) return
e.show = !e.show
}
// 只绑定设备-弹窗
function successPopupBtn() {
showSuccessPopup.value = false
router.navigateTo('/pages/home/index')
}
async function determine() {
// 暂时
// QRCode.value = 'AADXM1010061';
// childUserIds.value = '1753596310747975684';
// fetchCurBindInfo();
// ------
if (!bindProject.value[0].show && !bindProject.value[1].show) return
let obj = {
childDto: {},
deviceDto: {},
simSerialNumber: '',
childUserId: '',
}
if (bindProject.value[0].show) {
obj.deviceDto = bindProject.value[0]
obj.simSerialNumber = QRCode.value
}
if (bindProject.value[1].show) {
obj.childDto = bindProject.value[1]
obj.childUserId = childUserIds.value
}
show.value = false
if (obj.simSerialNumber && !obj.childUserId) {
let _obj: any
if (bindProject.value[0].adminFlag) {
// 设备已有管理员
_obj = {
simSerialNumber: obj.simSerialNumber,
type: 1,
}
router.navigateTo({
path: '/pages/home/bindDevice/applyForBinding',
query: { parameter: JSON.stringify(_obj) },
})
} else {
_obj = {
deviceDto: {
simSerialNumber: obj.simSerialNumber,
},
}
const res = await parentBindAdmin(_obj)
showSuccessPopup.value = true
successPopupText.value = res === 6 ? '您已成为设备管理员' : '请稍后再进行尝试~'
bindType.value = res === 6 ? 'success' : 'fail'
}
} else {
router.navigateTo({
path: './familyRelationships',
query: { parameter: JSON.stringify(obj) },
})
}
}
// 扫码部分
async function handleSuccess(res: string) {
h5ScanCode.value = false
resData.value = JSON.parse(res)
QRCode.value = resData.value.simSerialNumber
childUserIds.value = resData.value.userId
await fetchCurBindInfo()
}
function handleFail(err: any) {
uni.showModal({
title: err.errName,
content: err.errMsg,
complete: () => {
h5ScanCode.value = false
},
})
}
function handleClose() {
h5ScanCode.value = false
}
// 绑定前获取当前绑定情况
async function fetchCurBindInfo() {
try {
uni.showLoading({
title: '加载中...',
mask: true,
})
const data = await getCurInfo({
parentUerId: userInfo.value.id,
simSerialNumber: QRCode.value, // 设备序列号
childUserId: childUserIds.value, // 孩子账号
})
bindProject.value[0].bindFlag = data.device.bindFlag
bindProject.value[0].adminFlag = data.deviceAdminFlag
bindProject.value[0].show = data.device.bindFlag === 0
bindProject.value[1].bindFlag = data.child.bindFlag
bindProject.value[1].adminFlag = data.childAdminFlag
bindProject.value[1].show = data.child.bindFlag === 0
if (Number(data.device.bindFlag) === 1 && Number(data.child.bindFlag) === 1) {
// 两者都绑定了
showAllBindPopup.value = true
show.value = false
} else {
showAllBindPopup.value = false
show.value = true
}
} finally {
uni.hideLoading()
}
}
onMounted(async () => {
uni.showLoading({
title: '加载中...',
icon: 'none',
mask: true,
})
await initWeixinShareConfig()
uni.hideLoading()
})
</script>
<style lang="scss" scoped>
.page {
height: 100vh;
box-sizing: border-box;
}
.content {
width: 100%;
display: flex;
flex-direction: column;
height: calc(100vh - 44rpx);
background-image: url('https://xuexiaole-1313840333.cos.ap-guangzhou.myqcloud.com/xuexiaoleClient/urm/bindDevice/bindDeviceBgImg.png');
background-size: cover;
box-sizing: border-box;
.robotCont {
width: 100%;
// margin-top: 40upx;
display: flex;
z-index: 1;
.robot {
width: 250upx;
height: 312upx;
}
.robotMsg {
width: 486upx;
height: 220upx;
margin-top: 30upx;
margin-left: -30upx;
}
}
.process {
width: 690upx;
height: 1016upx;
margin-left: 30upx;
display: flex;
z-index: 0;
flex-direction: column;
align-items: center;
border-radius: 20upx;
border: 1upx rgba(255, 255, 255, 0.3);
background: rgba(255, 255, 255, 0.5);
backdrop-filter: blur(2.9000000953674316upx);
margin-top: -77upx;
padding: 40upx 30upx;
box-sizing: border-box;
.title {
display: flex;
align-items: center;
gap: 20upx;
.UnionLeft {
width: 35upx;
height: 8upx;
}
text {
font-size: 36upx;
color: #333;
font-weight: 500;
}
.UnionRight {
width: 35upx;
height: 8upx;
transform: rotate(180deg);
}
}
.list {
width: 630upx;
// height: 157upx;
display: flex;
align-items: center;
position: relative;
margin-top: 40upx;
.num {
width: 125upx;
height: 60upx;
display: flex;
align-items: center;
justify-content: center;
font-size: 48upx;
color: #fff;
background: #615dff;
border-radius: 0 50upx 50upx 0;
position: absolute;
left: 0upx;
top: 20upx;
}
.cont {
width: 610upx;
// height: 157upx;
display: flex;
flex-direction: column;
align-items: center;
border-radius: 20upx;
background: rgba(255, 255, 255, 0.8);
margin-left: 20upx;
padding: 30upx;
box-sizing: border-box;
.contentRight {
width: 450upx;
display: flex;
flex-direction: column;
margin-left: 90upx;
.lest {
font-size: 32upx;
height: 48upx;
}
.lestCont {
color: #666;
font-size: 30upx;
}
}
}
}
.QRbutton {
width: 610upx;
height: 90upx;
background: #615dff;
border-radius: 20upx;
margin-top: 60upx;
color: #fff;
font-size: 30upx;
display: flex;
align-items: center;
justify-content: center;
.QRbuttonText {
margin-left: 10upx;
font-size: 30upx;
}
}
}
}
.all_bind {
display: flex;
justify-content: center;
height: 330rpx;
width: 594upx;
background: #fff;
border-radius: 20upx;
.title {
margin-top: 66rpx;
display: inline-block;
width: 448rpx;
font-size: 32rpx;
font-weight: 400;
line-height: 44rpx;
}
.confirm {
position: absolute;
bottom: 0upx;
width: 100%;
height: 108upx;
border-top: solid 1upx rgba(238, 238, 238, 1);
display: flex;
align-items: center;
justify-content: center;
font-size: 34upx;
color: rgba(51, 51, 51, 1);
}
}
.popuCont {
width: 594upx;
height: 592upx;
background: #fff;
border-radius: 20upx;
padding: 50upx;
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: center;
// justify-content: center;
.title {
font-size: 34upx;
color: #333;
}
.option {
width: 100%;
display: flex;
align-items: flex-start;
gap: 30upx;
margin-top: 50upx;
margin-bottom: 20upx;
.bind_info {
flex: 1;
border-radius: 20rpx;
border: 2rpx solid transparent;
.item {
height: 120upx;
background-image: url('https://xuexiaole-1313840333.cos.ap-guangzhou.myqcloud.com/xuexiaoleClient/urm/bindDevice/bindPorjectBgimg.png');
background-size: 100%;
display: flex;
align-items: center;
justify-content: center;
color: rgba(65, 78, 145, 1);
font-size: 26upx;
border-radius: 20upx;
position: relative;
.icons {
width: 54upx;
height: 54upx;
margin-left: 20upx;
}
.SelectIcon {
width: 40upx;
height: 40upx;
position: absolute;
bottom: 0;
right: 0;
}
}
.bind_text {
margin-top: 20rpx;
display: inline-block;
width: 100%;
color: #a9aab5;
font-size: 24rpx;
line-height: 36rpx;
text-align: center;
}
}
.Select {
border: 2rpx solid #615dff;
}
}
.contList {
width: 100%;
display: flex;
font-size: 26upx;
color: rgba(51, 51, 51, 1);
line-height: 39upx;
}
.confirm {
position: absolute;
bottom: 0upx;
width: 100%;
height: 108upx;
border-top: solid 1upx rgba(238, 238, 238, 1);
display: flex;
align-items: center;
justify-content: center;
font-size: 34upx;
color: rgba(51, 51, 51, 1);
}
}
</style>