一、将新添加的字体图标引入项目
1.方案一:
(1) 打开阿里图标库中,字体图标的css代码。
(2) 复制css代码,在项目的css文件夹里新建一个新的css文件,将代码粘贴。
(3) 在 index.css 文件中用 import 关键字 引入新建的css文件。
2.引入阿里图标库的字体图标(推荐!)
(1)先在阿里图标库的项目中点击 font-class 模式,再点击下载至本地,解压后,将所有文件 替换 到项目中。
(2)在main.js中引入字体图标:import './assets/font/iconfont';
(3)使用时,直接在页面中 <i class="iconfont xxx" />。其中,前缀iconfont 是UI在阿里图标库创建项目时自定义的前缀名。
注意:下面几点并非通用属性
(4)使用svg格式的图标:
<svg class="icon" aria-hidden="true">
<use xlink:href={`#${index === roleData.activeIndex ? 'iconjingbanren' : 'iconjiaose'}`}/>
</svg>
(5)用户运营平台,没有下载图标依赖,使用element-plus组件库自带的图标:
<el-popconfirm icon="el-icon-house">
<template #reference>
<el-button>点击</el-button>
</template>
</el-popconfirm>
(6)运营平台使用的element-plus依赖版本太低,评分组件的 void-icon 属性不起作用,使用 void-icon-class="el-cion-house"代替。
3.下载图标依赖,使用element-plus提供的图标
(1)npm install @element-plus/icons-vue
(2)main.js中引入
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
(3)这是svg图标,需要用import 引入。
import { StarFilled } from '@element-plus/icons-vue'
二、滚动条样式
1.方案一
(1)用scss语法 @mixin 定义一个 混入 的样式。
// 滚动条样式
@mixin scroll-style {
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-track {
width: 4px;
background-color: #FFF;
}
&::-webkit-scrollbar-thumb {
background-color: #B2B8C9;
background-clip: padding-box;
min-height: 49px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
&::-webkit-scrollbar-thumb:hover {
background-color: #B2B8C9;
}
&::-webkit-scrollbar-track:hover {
background-color: #fff;
}
}
(2)用 @include 为真正滚动的元素添加混入样式。
.el-table__body-wrapper{
@include scroll-style;
}
(3)名称 "scroll-style" 随意自定义;& 符号取值是真正添加样式的元素 .el-table__body-wrapper,而非scroll-style。
2.方案二
(1)less语法中,在公共样式中定义滚动条样式
// 滚动条
.yc-scoll(){
&::-webkit-scrollbar {
width:4px;
}
&::-webkit-scrollbar-track {
width: 4px;
background-color:#FFF;
}
&::-webkit-scrollbar-thumb {
background-color:#B2B8C9;
background-clip:padding-box;
min-height:49px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius:5px;
}
&::-webkit-scrollbar-thumb:hover{
background-color:#B2B8C9;
}
&::-webkit-scrollbar-track:hover{
background-color:#E2E4E9;
}
}
(2)在需要用到的页面引入公共样式,并为真正滚动的元素添加样式
<style lang="less">
@import '@/assets/style/yc.less';
.box{
.yc-scoll();
}
</style>
三、复制
方案一:点击按钮,将某些内容复制到电脑的剪切板中。
//HTML按钮
<el-button type="text" @click="copyBtn(row)">复制链接</el-button>
// 复制链接的方法
copyBtn() {
var input = document.createElement('input') // 直接构建input
input.value = '123456789' // 设置内容
document.body.appendChild(input) // 添加临时实例
input.select() // 选择实例内容
document.execCommand('Copy') // 执行复制
this.$baseMessage(
'复制成功',
'success',
false,
'vab-hey-message-success'
)
document.body.removeChild(input) // 删除临时实例
},
方案二:使用插件 copy-to-clipboard
import copy from 'copy-to-clipboard';
const str = 'abc'
copy(str)
四、下载图片到本地
https://www.jianshu.com/p/cdbd87541b30
downloadIamge(imgsrc, name) {//下载图片地址和图片名
var image = new Image();
// 解决跨域 Canvas 污染问题
image.setAttribute("crossOrigin", "anonymous");
image.onload = function() {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0, image.width, image.height);
var url = canvas.toDataURL("image/png"); //得到图片的base64编码数据
var a = document.createElement("a"); // 生成一个a元素
var event = new MouseEvent("click"); // 创建一个单击事件
a.download = name || "photo"; // 设置图片名称
a.href = url; // 将生成的URL设置为a.href属性
a.dispatchEvent(event); // 触发a的单击事件
};
image.src = imgsrc;
},
五、下载 后端处理好的.xlsx文件到本地
1. 如果后端传的是base64格式的内容,首先将base64转换为 blob对象。
// base64转换blob对象
toBLob(data) {
const raw = window.atob(data);
const rawLength = raw.length;
const uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; i += 1) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array]);
},
转换之后应该是一个blob对象
2. 根据blob对象生成.xlsx表格并自动下载。入参res就是blob对象。
// 下载.xlsx文件
fileDownload(res) {
// 创建blob对象
const blod = new Blob([res], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
const fileName = `${this.activeCustonerName}_${this.formatTime(new Date())}`; // 这里的文件名需要根据实际情况修改!!!
// 创建a标签
const elink = document.createElement('a');
elink.style.display = 'none';
elink.setAttribute('download', fileName);
elink.href = URL.createObjectURL(blod);
document.body.appendChild(elink);
elink.click();
window.URL.revokeObjectURL(elink.href); // 释放URL 对象
document.body.removeChild(elink); // 移除a标签
},
3. 如果后端返的不是base64,而是直接是文档流,则接口请求时需带上responseType: 'blob'。
// 模板下载
downloadTemplate: (orgId) => axios.get(`/api/admin/organization/detail/import/template/${orgId}`, { responseType: 'blob' }),
从接口获取的值会被直接转化为Blob对象。
六、keep-alive结合vue3
https://juejin.cn/post/6844903846901186574
1.路由占位符的配置
(1)对需要被缓存的列表页 所对应的 <router-view /> 进行修改。
(2)web端项目,通常是在Home组件中,因为Home组件包括了左侧菜单和 右侧路由页面。
(3)将路由占位符修改为:
<router-view v-slot="{ Component }">
<keep-alive :include="include">
<component :is="Component" />
</keep-alive>
</router-view>
(4)这是vue3写法,其中include属性可以是 数组 或配置函数。
(5)在Home组件中 监听 $route 的路由变化,实时修改include数组的值,从而决定哪些组件被缓存。
data() {
return {
include: [],
};
},
watch: {
$route: {
handler: function (to, from) {
// 如果 要 to(进入) 的页面是需要 keepAlive 缓存的,把 name push 进 include数组
if (to?.meta.keepAlive) {
!this.include.includes(to.name) && this.include.push(to.name);
}
// 如果 要 form(离开) 的页面是 keepAlive缓存的,
// 再根据 deepth 来判断是前进还是后退
// 如果是后退
if (from?.meta.keepAlive && to.meta.deepth <= from.meta.deepth) {
const index = this.include.indexOf(from.name);
index !== -1 && this.include.splice(index, 1);
}
},
immediate: true,
},
},
2.路由对象的配置
(1)给每个 渲染到Home组件的路由对象,添加meta属性。
(2)需要被缓存的列表页路由,keepAlive设置为 true,deepth设置为 中间值。
{
path: '/grabOverdue',
name: 'GrabOverdue',
title: '列表页',
component: () => import('@/views/survey-web-grab//overdue/index.vue'),
meta: {
keepAlive: true,
deepth: 1,
},
},
(3)详情页路由,keepAlive设置为 false,deepth设置为 最大值。
{
path: '/surveyMySurvey/&detail/:id',
name: 'SurveyMySurveyDetail',
title: '详情页',
component: () => import('@/views/web-data-survey/survey-follow/index.vue'),
meta: {
keepAlive: false,
deepth: 2,
},
},
(4)其余同级别路由,keepAlive设置为 false,deepth设置为 最小值。
{
path: '/grabTaskPool',
name: 'GrabTaskPool',
title: '其他页面',
component: () => import('@/views/survey-web-grab/task-pool/index.vue'),
meta: {
keepAlive: false,
deepth: 0,
},
},
3.被缓存的 列表页 的配置
(1)被缓存的列表页对应的路由组件 必须有name属性,属性值和 路由对象的name属性 保持一致。
(2)从详情页回到列表页时,使用的是被缓存的页面,所以不会触发onMounted等钩子函数。
(3)如果想在回来时请求某些接口数据,可以在 onActivated() 钩子函数中执行。
(4)onActivated()钩子函数只有被缓存的页面才会调用。并且,无论是直接刷新页面,或是从详情页回到列表页时,都会调用。最好和onMounted一起使用,避免出现初始化时没调接口的问题。
4.详情页的配置
从详情页回到列表页,使用 $router.push() 方法。
七、请求截断-axios
在axios中是通过 axios.CancelToken.source() 方法取消请求。https://blog.csdn.net/wopelo/article/details/79802585
(1)在配置axios的js文件中,引入CancekToken;
(2)自定义数组AxiosRequestArr, 用于存放 可取消的请求的配置项;
(3)在请求拦截器中将所有 cancel属性为true 的请求的配置项,放到AxiosRequestArr 数组中;
(4)定义方法CancelRequest,取消所有请求 并 清空AxiosRequestArr 数组;
(5)在请求方法页面中,给每个需要取消请求的方法,添加 cancel:true属性,并在 发送本次请求前取消上次的请求。
import axios from 'axios';
//(1)引入CancekToken
const CancelToken = axios.CancelToken;
//(2)定义存放 可取消请求的配置项 的数组
const AxiosRequestArr: any = [];
axios.interceptors.request.use(
(config) => {
const dynamicConfig = config;
//(3)向数组中添加 可取消请求的配置项
if (dynamicConfig.cancel) {
config.cancelToken = new CancelToken((cancel) => {
AxiosRequestArr.push({
type: dynamicConfig.url,
cancel,
});
});
}
return config;
},
(err) => {}
);
//(4)定义方法:取消所有请求并清空数组
const CancelRequest = () => {
AxiosRequestArr.forEach((item: any, index: any) => {
item.cancel();
delete AxiosRequestArr[index];
});
};
export { CancelRequest };// 导出方法供请求方法页面使用
import axios from '../index';
import { CancelRequest } from '../index'; //导入自定义方法,用于取消所有请求
myTableList: (tag: number, params: any) => {
//(5)发起本次请求前,先取消上次请求;将这个请求标识为可取消的请求
CancelRequest();
return axios.post(`/api/research/main/my/list/${tag}`, clearEmpty(params), { cancel: true });
},
注意:同一页面内被截断的请求应当在 同一步骤发起,因为每次请求前,取消了 全部未完成的请求。 (尝试在本次请求前,只中断同类型请求)
八、样式
1.display:table-row样式
父元素设置这个样式后,内外边距都会失效。 border-collapse:separate; border-spacing:10px 50px; 来设置每个单元格之间的距离。
2.absolute定位
虽然absolute定位的子元素 脱离了标准流,但是依然可以 让父元素具有滚动条:
当父元素有高度,absolute定位的子元素的高度更大时,可以给父元素设置overflow:auto,父元素具有滚动条。
3.scss
在scss中用global{}修改antd样式;也可以在某个类名里加.abc{:global{xxx}},global中的内容只在这个类名里生效。
九、方法
1.Object.assing()
将源对象(sourse)的属性添加到目标对象(target)中
Object(target,sourse)
const target = {a:1, b:2}
const sourse = {a:10, c:3}
Object.assign(target,sourse)
console.log(target) // {a:10, b:2, c:3}
console.log(sourse) // {a:10, c:3}
十、项目引入mock
1.基于Easy Mock平台已经配置好的内容。(不是我能搞定的)
2.在平台上创建新的项目:http://172.18.255.8:9999/
3.项目中,对需要mock的接口进行改造。如果页面中传递了mock=true参数,则接口地址在最前面加上"/mock"。
4.vue.confing.js中添加对于mock接口的本地反向代理。
① 一个项目可以添加多个反向代理,比如对 "/api" 的反向代理、对 "/mock/api" 的反向代理。
② 接口实际请求用的 基础地址是 http://localhost:8080,然后拼接上"/mock/api/xxx"。
③ "/mock/api"配置对象 就是要把接口地址中,"/mock/api"之前的 基础地址 替换为 "target"属性值。
④ target属性值就是 真实的基础地址。对于"/api"配置对象而言,要请求的是 后端基础地址,对于"/mock/api"而言要请求的是 mock基础地址。
十一、自定义图片上传-粘贴上传
1.Html元素
(1)根据base64格式的图片数组,循环渲染已上传的图片
(2)一个input框,通过监听input框的paste事件,获取到粘贴板中的图片信息。
<div class="upload-pic-wrapper">
// 1.pictures数组用于存放base64格式的图片数据,base64格式的图片赋值给img标签,可以直接显示。
<div class="pic" v-for="(it, index) in pictures" :key="index">
<img :src="it" alt="" />
</div>
// 2.paste事件,获取到粘贴的内容;change事件,清空input框输入的内容,只用来粘贴上传;
<a-input
v-model:value="uselessValue"
@paste="paste"
@change="uselessValue = ''"
/>
</div>
2. js方法
(1)通过getAsFile方法,将粘贴板中的图片信息转为file文件。
(2)通过自定义方法,将file文件转为base64格式,用于本地展示。
// 图片转base64,入参是file格式的数据
const getBase64 = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
};
(3)通过formData构造函数,将file文件转为formData格式,用于接口传参。
// 图片粘贴上传
const paste = async (e) => {
const it = e.clipboardData.items[0]; // 1.获取剪切板中的有效数据
const pasteFile = it.getAsFile(); // 2.生成file文件
// 3.对上传的文件进行大小和类型限制
if (it.kind !== 'file' || !fileType.includes(it.type)) {
proxy.$message.error('请上传后缀为.png或.jpeg图片文件');
return;
}
if (pasteFile.size / 1048576 > 2) {
proxy.$message.error('文件不能超过2M');
return;
}
const pic = await getBase64(pasteFile); // 4.根据file文件生成base64图片,用于本地展示
// 5.根据file文件生成formData格式的数据,用于接口请求
const form = new FormData();
form.append('file', pasteFile);
// 6.调用上传图片的接口
......
};
3.文件和普通参数同时传递
如果接口入参包含了文件和其他参数,则需要把所有参数都放到formData对象中。
const form = new FormData() // 创建formData格式的对象===>form,这就是要传给后端的数据
form.append('file',file) // 向form中添加文件
form.appdne('id',id) // 向form中添加id
十二、vue3中h函数的使用
1.引入h函数
import {h} from 'vue'
2.h函数接收三个参数:节点类型、配置对象、节点内容。
① 节点类型:div、span等任意标签。
② 配置对象:可以是class类名、其他属性、方法等。
③ 节点内容:可以是字符串;也可以是一个 数组,数组中就是 子节点。
const btnClick = () => {
const myDiv = h('div', [
h('span', '存在'),
h(
'span',
{
class: 'yc-verify-yes',
onClick:()=>{}
},
'审核不通过'),
h('span', '的映射,点击确定,将退回至处理人修改'),
])
Modal.confirm({
content: myDiv
});
}
十三、动态添加的表单实现校验
一组可以增加或删除的卡片,每个卡片中有多个表单元素,此时也可以用一个<Form></Form>标签包裹所有卡片。
点击增加按钮,一次增加一个下拉选择框、一个input框
const state = reactive({
form:{
dataLinks:[{dataType:1, url: ''},{dataType:2, url: ''}]
}
})
<el-form>
<div v-for="(item,index) in form.dataLinks" :key="index" class="myUrl"> // 一、循环的数组:form.dataLinks
<el-form-item
label="数据类型:"
label-width="81px"
:prop="`dataLinks.${index}.dataType`" // 二、绑定的props:dataLinks数组中第index个元素的dataType属性
:rules="{ required: true, message: '请选择数据类型', trigger: 'change' }" // 三、绑定单独的校验规则
>
<el-select v-model="item.dataType" style="width: 120px;margin-right: 20px"> // 四、下拉框绑定item中的dataType属性
<el-option
v-for="it in dataTypeOptions"
:key="it.value"
:label="it.label"
:value="it.value"
/>
</el-select>
</el-form-item>
<el-form-item
label="数据链接:"
:prop="`dataLinks.${index}.url`" // 二、绑定的props:dataLinks数组中第index个元素的url属性
:rules="{ required: true, message: '请输入数据链接并以http开头', trigger: 'change' }" // 三、绑定单独的校验规则
>
<el-input
v-model="item.url" // 四、输入框绑定item中的url属性
@blur="limitUrl(index)"
:maxlength="1000"
placeholder="请输入数据链接并以http开头"
style="width:440px;margin-right: 30px"
/>
</el-form-item>
</div>
</el-form>
十四、计算文本长度
/**
* 计算文本宽度
* @param {*} text String 文案
* @param {*} options Object 样式
* @returns 宽度 Number
*/
const getWordLength = (text, options = {}) => {
const { size = 14, family = 'Microsoft YaHei', weight = 400 } = options;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.font = `${weight} ${size}px ${family}`;
const metrics = ctx.measureText(text);
const actual = Math.abs(metrics.actualBoundingBoxLeft) + Math.abs(metrics.actualBoundingBoxRight);
return Math.max(metrics.width, actual);
};
十五、下载文件时让用户选择下载路径
1.直接用a链接的 download 下载属性即可。
<a href="#" download >下载</a>
2.不嫌麻烦可以用这个:
① 点击按钮调用接口,后端返回blob文件流,前端将blob文件流下载到本地。详情见 第五项。
② 如果想要点击按钮后打开文件选择框,让用户选择下载路径的话,就要这样:⬇⬇⬇
// 监听按钮点击事件
async handleClick() {
const handle = await this.fileSaveAs("Hello File Access Api") // 1.打开文件选择框。参数只是个提示信息
await this.writeFile(handle, this.downloadFile) // 2.向文件中写入数据。
},
// 1.自定义方法:调用showSaveFilePicker方法打开文件选择框并传入相关配置项。(会生成一个空文件)
async fileSaveAs(description) {
const opts = {
types: [
{
description: description,
accept: {
"text/plain": [".txt"],
"application/octet-stream":[".bin"],
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":[".xlsx"] ,
"application/vnd.ms-excel":[".xls"],
"application/msword":[".doc"],
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":[".docx"],
"application/vnd.ms-powerpoint":[".ppt"],
},
},
],
};
return await window.showSaveFilePicker(opts);
},
// 2.在第一步的基础上向空文件中写入数据。(contents可以是文件流)
async writeFile(fileHandle,contents) {
const writable = await fileHandle.createWritable();
await writable.write(contents);
await writable.close();
},
很详细的讲解:https://juejin.cn/post/7057802776805392392
十六、前端实现导出excel表格
三种方式:https://juejin.cn/post/7280006996572586043
选择第二种vue-json-excel:https://blog.csdn.net/m0_59023970/article/details/123427008
十七、浏览器调用摄像头
核心方法:window.navigator.mediaDevices.getUserMedia()
1.实例演示:https://developer.mozilla.org/zh-CN/docs/Web/API/Media_Capture_and_Streams_API/Taking_still_photos#%E6%BC%94%E7%A4%BA
2.语法规范:https://developer.mozilla.org/zh-CN/docs/Web/API/MediaDevices/getUserMedia#%E8%A7%84%E8%8C%83
<div class="contentarea">
<div class="camera">
<video id="video">视频流目前不可用。</video>
<button id="startbutton"></button>
</div>
<canvas id="canvas"> </canvas>
</div>
<script>
const width = 320; // 视频和画布的宽
let height = 0; // 视频和画布的高
let video = null;
let canvas = null;
let photo = null;
let startbutton = null; // 拍照按钮
// 如果当前处于 iframe 内,则隐藏内容并显示一个按钮,用于返回浏览器窗口
function showViewLiveResultButton() {
if (window.self !== window.top) {
document.querySelector(".contentarea").remove();
const button = document.createElement("button");
button.textContent = "返回浏览器窗口";
document.body.append(button);
button.addEventListener("click", () => window.open(location.href));
return true;
}
return false;
}
// 将视频流初始化
function startup() {
if (showViewLiveResultButton()) {
return;
}
video = document.getElementById("video");
canvas = document.getElementById("canvas");
photo = document.getElementById("photo");
startbutton = document.getElementById("startbutton");
if(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
// 先关闭所有摄像头
if(window.stream){
window.stream.getTracks().forEach(track => {
track.stop();
})
}
// 获取视频流
navigator.mediaDevices
.getUserMedia({ video: { facingMode: { exact: "environment" } }, audio: false })
.then((stream) => {
video.srcObject = stream;
video.play();
})
.catch((err) => {
console.error("未找到可用摄像头:", err);
alert("未找到可用摄像头:", err)
});
}else {
console.log("视频流初始化错误,未找到mediaDevices属性,navigator: ", navigator);
alert(`视频流初始化错误`)
}
// 视频流加载完成事件监听
video.addEventListener(
"canplay",
(ev) => {
$(".contentarea").removeClass("hide") // 显示video视频
height = video.videoHeight / (video.videoWidth / width);
if (isNaN(height)) {
height = width / (4 / 3);
}
video.setAttribute("width", width);
video.setAttribute("height", height);
canvas.setAttribute("width", width);
canvas.setAttribute("height", height);
},
false,
);
// 拍照按钮点击事件
startbutton.addEventListener( "click", (ev) => {
takepicture();
ev.preventDefault();
},
false,
);
clearphoto();
}
// 清空上次拍照的内容并将新照片显示到页面
function clearphoto() {
const context = canvas.getContext("2d");
context.fillStyle = "#AAA";
context.fillRect(0, 0, canvas.width, canvas.height);
const data = canvas.toDataURL("image/png");
photo.setAttribute("src", data);
imgData = new FormData().append("img", data)
}
// 将canvas内容作为照片
function takepicture() {
const context = canvas.getContext("2d");
if (width && height) {
canvas.width = width;
canvas.height = height;
context.drawImage(video, 0, 0, width, height);
const data = canvas.toDataURL("image/png");
photo.setAttribute("src", data);
imgData = new FormData().append("img", data)
} else {
clearphoto();
}
}
</script>
十八、前端扫码识别二维码
1.在原生js中使用html-qrcode,直接下载html5-qrcode.min.js压缩包,并引入该文件。
2.html-qrcode的git地址:https://github.com/mebjas/html5-qrcode/blob/master/examples/html5/index.html
3.也可参照以下示例:https://github.com/mebjas/html5-qrcode/blob/master/examples/html5/index.html
<div class="qrcode-cointer" style="height:calc(100% - 80px);overflow: auto">
<button class="search_btn" id="sm_btn" onclick="getCameras()">扫码查询</button>
<button class="search_btn search_btn_two" id="cx_btn" onclick="codeSearch()">查询</button>
<div class="qrcode">
<div id="reader"></div>
</div>
</div>
<script src="./html5-qrcode.min.js"></script>
<script>
let html5QrCode = null;
// 获取摄像头权限
const getCameras = () => {
Html5Qrcode.getCameras()
.then((devices) => {
// 返回的是你的摄像设备列表,手机有两个摄像头,电脑返回一个摄像头
// 初始化扫描程序,在这里需要传递标签的id,第二个参数用来控制识别类型
html5QrCode = new Html5Qrcode("reader");
start();
})
.catch((err) => {
html5QrCode = new Html5Qrcode("reader")
})
}
// 开始扫描相机给的二维码
const start = () => {
html5QrCode.start(
// environment后置摄像头 user前置摄像头 也可以传递获取摄像头时的id
// 也可以是这样的{ deviceId: { exact: cameraId} }
{ facingMode: "environment" },
{
fps: 20, // 可选,每秒帧扫描二维码
qrbox: { width: 250, height: 250 }, // 可选,如果你想要有界框UI
aspectRatio: 1.777778 // 可选,视频馈送需要的纵横比,(4:3--1.333334, 16:9--1.777778, 1:1--1.0)传递错误的纵横比会导致视频不显示
},
(decodedText, decodedResult) => {
// 成功的回调函数
alert(decodedText)
if(decodedText.includes("http")) {
stop();
window.location.href= decodedText;
}else if(!decodedText.includes("http")){
stop();
window.location.href = base_url + "?code=" + decodedText
}
})
// 这里应该还有一个错误回调函数(没有识别到的时候会执行,太频繁了,没写)
}
// 停止摄像头
const stop = () => {
if(html5QrCode){
html5QrCode.stop().then((suc) => {
console.log("关闭摄像头")
})
}
}
</script>