Vue3使用vue-qrcode-reader实现扫码绑定设备功能

server/2024/9/22 12:49:09/
需求描述

移动端进入网站后,登录网站进入设备管理界面。点击添加设备,可以选择直接添加或者扫一扫。点击扫一扫进行扫描二维码获取设备序列号自动填充到添加设备界面的序列号输入框中。然后点击完成进行设备绑定。

  1. 安装vue-qrcode-reader 这里使用的版本是5.5.7
npm install vue-qrcode-reader --save
  1. 扫一扫界面ScanCode.vue
<template><div class="block-main"><div class="head-wrap"><imgsrc="../../assets/images/mobile/icon-arrow-left.png"class="btn-back"@click="goback"/><span class="title">扫一扫</span></div><div class="qr-container"><qrcode-stream@detect="onDecode"@camera-on="onCameraReady"@error="onError"/></div></div>
</template><script>
import { reactive, ref, onMounted } from "vue";
import { useRouter } from "vue-router";
import { QrcodeStream } from "vue-qrcode-reader";
import { showToast } from "vant";
export default {components: {QrcodeStream,showToast,},setup() {const testVice = reactive([]);const router = useRouter();// 扫码成功后的回调const onDecode = (detectedCodes) => {if (detectedCodes.length > 0) {// 跳转到添加设备页面并传递设备编号let deviceCode = detectedCodes[0].rawValue;router.replace({path: "/deviceadd",query: { deviceCode },});} else {showToast("扫码失败");}};const onCameraReady = (res) => {console.log("摄像头准备好了");};const onError = (error) => {if (error.name === "NotAllowedError") {// user denied camera access permissionshowToast("用户拒绝相机访问权限");} else if (error.name === "NotFoundError") {// no suitable camera device installedshowToast("未安装合适的摄像设备");} else if (error.name === "NotSupportedError") {// page is not served over HTTPS (or localhost)showToast("当前网页没有通过 HTTPS 或 localhost 安全协议提供服务");} else if (error.name === "NotReadableError") {// maybe camera is already in useshowToast("相机被占用了)");} else if (error.name === "OverconstrainedError") {// did you request the front camera although there is none?showToast("尝试使用前置摄像头)");} else if (error.name === "StreamApiNotSupportedError") {showToast("浏览器似乎缺少功能)");// browser seems to be lacking features}};onMounted(() => {navigator.mediaDevices.enumerateDevices().then((devices) => {devices.forEach((device) => {if (device.kind === "videoinput") {console.log("Video input device: ",device.label,device.deviceId);}});}).catch((error) => {console.error("Error enumerating devices: ", error);showToast("Error enumerating devices");});});const goback = () => {router.go(-1);};return {testVice,onCameraReady,goback,onDecode,onError,};},
};
</script><style lang="scss" scoped>
.block-main {height: 100%;background: #fff;overflow: hidden;font-size: 16px;.head-wrap {height: 0.96rem;background: #31be7c;font-family: Microsoft YaHei UI;font-weight: 400;color: #fff;display: flex;align-items: center;position: fixed;left: 0;right: 0;.btn-back {margin-left: 0.4rem;width: 0.22rem;height: 0.38rem;}.title {font-size: 0.36rem;text-align: center;flex: 1;}}.qr-container {display: flex;justify-content: center;align-items: center;height: calc(100vh - 0.96rem); // 减去头部的高度margin-top: 0.96rem;}
}
</style>
  1. 添加设备界面DeviceAdd.vue
<template><div class="block-main"><div class="head-wrap"><a href="javascript:void(0)" class="btn-cancel" @click="goBack">取消</a><span class="title"></span><a href="javascript:void(0)" class="btn-sure" @click="deviceReg">完成</a></div><div class="input-wrap" @click="showDeviceType"><span class="input">{{ deviceInfo.devtypeName }}</span><imgsrc="../../assets/images/mobile/icon-arrow-right.png"class="icon-arrow"/></div><div class="input-wrap"><inputtype="text"class="input"v-model="deviceInfo.devsn"placeholder="请输入设备序列号"/><imgv-if="deviceInfo.devsn"src="../../assets/images/mobile/icon-close-gray.png"class="icon-close"@click="clearInput"/></div><div class="error-msg">{{ deviceInfo.devsn_error }}</div><van-popup v-model:show="deviceTypeDialog" position="bottom"><van-picker:columns="devTypes"v-model:value="deviceInfo.devtype"@cancel="deviceTypeDialog = false"@confirm="onConfirm"/></van-popup></div>
</template><script>
import { ref, reactive, onMounted } from "vue";
import { useRoute,useRouter } from "vue-router";
import {Picker,Popup,showFailToast,showSuccessToast,showToast,showLoadingToast,
} from "vant";
import { device_Reg } from "../../api/auth";
export default {components: {"van-picker": Picker,"van-popup": Popup,},setup() {const route = useRoute();const deviceTypeDialog = ref(false);const router = useRouter();const devTypes = [{ value: 1, text: "设备类型1" },{ value: 2, text: "设备类型2" },{ value: 3, text: "设备类型3" },];const deviceInfo = reactive({devtype: null,devtypeName: "请选择需要绑定的设备",devsn: "",devsn_error: "",});const showDeviceType = () => {deviceTypeDialog.value = true;};const clearInput = () => {deviceInfo.devsn = "";deviceInfo.devsn_error = "";};const onConfirm = ({ selectedOptions }) => {deviceTypeDialog.value = false;deviceInfo.devtype = selectedOptions[0].value;deviceInfo.devtypeName = selectedOptions[0].text;};const goBack = () => {router.go(-1);};const deviceReg = async () => {if (deviceInfo.devtype == null) {deviceInfo.devsn_error = "请选择设备类型";return;}if (!deviceInfo.devsn) {deviceInfo.devsn_error = "请填写设备序列号";return;}const toast = showLoadingToast({message: "数据提交中...",forbidClick: true,});let response = await device_Reg({devsn: deviceInfo.devsn,devtype: deviceInfo.devtype,});if (response.isSuccess == true) {toast.close();showSuccessToast("设备绑定成功");setTimeout(() => {router.go(-1);}, 2000);} else {deviceInfo.devsn_error = response.message;toast.close();}};onMounted(() => {const scannedDeviceSn = route.query.deviceCode;if (scannedDeviceSn) {deviceInfo.devsn = scannedDeviceSn;}});return {devTypes,deviceTypeDialog,deviceInfo,showDeviceType,clearInput,onConfirm,goBack,deviceReg,};},
};
</script><style lang="scss" scoped>
.block-main {height: auto;overflow: hidden;.head-wrap {height: 0.96rem;background: #31be7c;font-family: Microsoft YaHei UI;font-weight: 400;color: #fff;display: flex;align-items: center;.btn-cancel {padding-left: 0.4rem;padding-right: 0.4rem;height: 0.96rem;line-height: 0.96rem;font-size: 0.36rem;color: #fff;}.title {font-size: 0.36rem;text-align: center;flex: 1;}.btn-sure {width: 1.15rem;height: 0.55rem;background: #ffffff;border-radius: 5px;font-size: 0.36rem;line-height: 0.55rem;margin-right: 0.4rem;margin-left: 0.4rem;color: #31be7c;}}.input-wrap {height: 1.2rem;line-height: 1.2rem;border-bottom: 1px solid #ced6d2;display: flex;align-items: center;.input {flex: 1;margin-left: 0.58rem;font-size: 0.3rem;width: 0px;border-style: none;outline: none;height: 1rem;text-align: left;}.icon-close {width: 0.36rem;height: 0.36rem;margin-right: 0.4rem;margin-left: 0.4rem;}.icon-arrow {width: 0.2rem;height: 0.3rem;margin-left: 0.04rem;margin-right: 0.4rem;margin-left: 0.4rem;}}.error-msg {padding-left: 0.58rem;margin-top: 0.19rem;font-size: 0.24rem;color: #ff6c00;padding-right: 0.58rem;text-align: left;line-height: 0.24rem;}
}
</style>
效果图

在这里插入图片描述


http://www.ppmy.cn/server/118261.html

相关文章

47.面向对象综合训练-汽车

//题目需求&#xff1a;定义数组存储3个汽车对象 //汽车的属性&#xff1a;品牌&#xff0c;价格&#xff0c;颜色 //创建三个汽车对象&#xff0c;数据通过键盘录入而来&#xff0c;并把数据存入到数组当中 1.标准的JavaBean类 public class Car {private String brand;//品…

【ESP32】Arduino开发 | 中断矩阵+按键输入中断例程

对于中断矩阵的详细介绍会放在ESP-IDF开发文章中&#xff0c;跳转栏目目录可以找到对应文章。 1. API 1.1 绑定GPIO中断 attachInterrupt(uint8_t pin, voidFuncPtr handler, int mode); pin&#xff1a;管脚号&#xff1b;handler&#xff1a;中断处理函数&#xff1b;mode…

递归10小题

注&#xff1a;操作数字的数组均为int [ ]型&#xff0c;操作字符串均为char [ ]型 下面的10个问题很常见&#xff0c;在这里都是用递归解决的。涉及到数组的问题&#xff0c;需要有指针的知识。 1.求1到n的和 int getSum(int n)//求1到n的和 {if(n1){return 1;}return ngetS…

富格林:整理可信技巧应对虚假

富格林指出&#xff0c;投资者进入黄金市场的第一课&#xff0c;应该是学会利用可信的技巧应对市场的交易风险&#xff0c;避免虚假猫腻的捣乱。黄金市场瞬息万变&#xff0c;虽然有交易操作的就会&#xff0c;但也伴随着一定的风险。投资者对于应对预防虚假的措施需求还是比较…

图数据库的力量:深入理解与应用 Neo4j

图数据库的力量&#xff1a;深入理解与应用 Neo4j 文章目录 图数据库的力量&#xff1a;深入理解与应用 Neo4j1、什么是 Neo4j&#xff1f;版本说明 2、Neo4j 的部署和安装Neo4j Web 工具介绍 3、体验 Neo4j加载数据查询数据数据结构 4、Cypher 入门创建数据查询数据关系深度查…

跨界融合:EasyDSS+无人机视频直播推流技术助力行业多场景应用

随着科技的飞速发展&#xff0c;无人机技术与流媒体技术的结合正逐步改变着多个行业的运作模式。其中&#xff0c;EasyDSS互联网视频云服务与无人机视频直播推流技术的结合&#xff0c;更是为警务安防、赛事直播、农业监测等多个领域带来了前所未有的变革。本文将深入探讨EasyD…

【Python】高效图像处理库:pyvips

月亮慢慢变圆&#xff0c;日子慢慢变甜。 在图像处理领域&#xff0c;pyvips 是一个轻量级且高效的库&#xff0c;适合处理大规模图像、实现高性能的操作。相较于其他常见的图像处理库如 PIL 或 OpenCV&#xff0c;pyvips 以其低内存占用和出色的速度脱颖而出。本文将介绍 pyv…

CefSharp_Vue交互(Element UI)_WinFormWeb应用(2)---置顶和取消置顶(含示例代码)

一、预览 获取winform的置顶参数,和设置置顶参数 1.1 置顶(默认不置顶) 1.2 示例代码