|
@@ -0,0 +1,427 @@
|
|
|
+const errorMessage = {
|
|
|
+ ms1: '请使用微信、Chrome、Firefox或Safari浏览器,如果浏览器没有问题,请联系管理员',
|
|
|
+ ms2: '请使用微信、Chrome、Firefox或Safari',
|
|
|
+ ms3: 'navigator对象无媒体属性', // 错误提示同ms2
|
|
|
+ ms4: '未找到摄像头,请确认摄像头是否正常以及当前摄像头是否被禁用',
|
|
|
+ ms5: '检测到当前摄像头已被占用,请关闭摄像头后重新尝试',
|
|
|
+ ms6: '摄像头硬件无法满足使用要求,请更换摄像头后重新尝试',
|
|
|
+ ms7: '请开启浏览器摄像头权限',
|
|
|
+ ms8: '未获取摄像头数据,请检测摄像头是否正常',
|
|
|
+ ms9: '当前浏览器不支持,请更换浏览器后尝试',
|
|
|
+ ms10: '当前Android系统版本低于9!请更新操作系统',
|
|
|
+ ms11: '当前IOS系统版本低于14.3!请更新操作系统',
|
|
|
+ ms12: '推荐使用safari浏览器或微信,使用其他浏览器可能会在考试过程中出现摄像头问题,影响考试结果,导致重考,不建议使用其他浏览器。',
|
|
|
+ ms13: '推荐使用火狐浏览器、谷歌浏览器或微信,使用其他浏览器可能会在考试过程中出现摄像头问题,影响考试结果,导致重考,不建议使用其他浏览器',
|
|
|
+};
|
|
|
+
|
|
|
+export {
|
|
|
+ errorMessage
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+function checkPlatform2() {
|
|
|
+ const ua = navigator.userAgent.toLowerCase();
|
|
|
+ // 安卓系统
|
|
|
+ if (/android/i.test(navigator.userAgent)) {
|
|
|
+ let test = /android\s([\w.]+)/;
|
|
|
+ let match = test.exec(ua);
|
|
|
+ let version = match[1].split('.')[0];
|
|
|
+ if (version < 9) {
|
|
|
+ Toast.fail(errorMessage.ms10);
|
|
|
+ return {
|
|
|
+ data: false,
|
|
|
+ waitCode: false
|
|
|
+ };
|
|
|
+ }
|
|
|
+ // 判断 浏览器 Android
|
|
|
+ if (!checkAndroidForBrowser()) {
|
|
|
+ // 提示信息
|
|
|
+ Toast.fail({
|
|
|
+ message: errorMessage.ms13,
|
|
|
+ duration: 5000
|
|
|
+ });
|
|
|
+ return {
|
|
|
+ data: true,
|
|
|
+ waitCode: true
|
|
|
+ };
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ data: true,
|
|
|
+ waitCode: false
|
|
|
+ };
|
|
|
+ }
|
|
|
+ // ios 系统
|
|
|
+ if (/(iphone | ipad | ipod | iOS)/i.test(navigator.userAgent)) {
|
|
|
+ let test = /os\s([\w]+)/;
|
|
|
+ let match = test.exec(ua);
|
|
|
+ let vs = match[1].split('_');
|
|
|
+ let version = '';
|
|
|
+
|
|
|
+ if (vs.length > 2) {
|
|
|
+ version = `${vs[0]}.${vs[1]}`;
|
|
|
+ } else if (vs.length == 2) {
|
|
|
+ version = `${vs[0]}.${vs[1]}`;
|
|
|
+ } else {
|
|
|
+ version = `${vs[0]}.0`;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (version < 14.3) {
|
|
|
+ Toast.fail(errorMessage.ms11);
|
|
|
+ return {
|
|
|
+ data: false,
|
|
|
+ waitCode: false
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断浏览器兼容 判断 ios 浏览器提示信息
|
|
|
+ // 判断 浏览器 Android
|
|
|
+ if (!checkIosForBrowser()) {
|
|
|
+ // 提示信息
|
|
|
+ Toast.fail({
|
|
|
+ message: errorMessage.ms12,
|
|
|
+ duration: 5000
|
|
|
+ });
|
|
|
+ return {
|
|
|
+ data: true,
|
|
|
+ waitCode: true
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ data: true,
|
|
|
+ waitCode: false
|
|
|
+ };
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function checkIosForBrowser() {
|
|
|
+ let u = navigator.userAgent;
|
|
|
+ let result = false;
|
|
|
+ let curname = getBrowser();
|
|
|
+ // 如果是 苹果
|
|
|
+ if (curname === 'safari') {
|
|
|
+ result = true;
|
|
|
+ }
|
|
|
+ // 如果是 微信
|
|
|
+ if (u.indexOf('MicroMessenger') > -1) {
|
|
|
+ result = true;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+function checkAndroidForBrowser() {
|
|
|
+ let curname = getBrowser();
|
|
|
+ let result = false;
|
|
|
+ // 如果是 谷歌
|
|
|
+ if (curname === 'chrome') {
|
|
|
+ result = true;
|
|
|
+ }
|
|
|
+ if (curname === 'firefox') {
|
|
|
+ result = true;
|
|
|
+ }
|
|
|
+ // 如果是 微信
|
|
|
+ if (curname === 'wechat') {
|
|
|
+ result = true;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+function getBrowser() {
|
|
|
+ var u = navigator.userAgent;
|
|
|
+
|
|
|
+ var bws = [{
|
|
|
+ name: 'sgssapp',
|
|
|
+ it: /sogousearch/i.test(u),
|
|
|
+ }, {
|
|
|
+ name: 'wechat',
|
|
|
+ it: /MicroMessenger/i.test(u),
|
|
|
+ }, {
|
|
|
+ name: 'weibo',
|
|
|
+ it: !!u.match(/Weibo/i),
|
|
|
+ }, {
|
|
|
+ name: 'uc',
|
|
|
+ it: !!u.match(/UCBrowser/i) || u.indexOf(' UBrowser') > -1,
|
|
|
+ }, {
|
|
|
+ name: 'sogou',
|
|
|
+ it: u.indexOf('MetaSr') > -1 || u.indexOf('Sogou') > -1,
|
|
|
+ }, {
|
|
|
+ name: 'xiaomi',
|
|
|
+ it: u.indexOf('MiuiBrowser') > -1,
|
|
|
+ }, {
|
|
|
+ name: 'baidu',
|
|
|
+ it: u.indexOf('Baidu') > -1 || u.indexOf('BIDUBrowser') > -1,
|
|
|
+ }, {
|
|
|
+ name: '360',
|
|
|
+ it: u.indexOf('360EE') > -1 || u.indexOf('360SE') > -1,
|
|
|
+ }, {
|
|
|
+ name: '2345',
|
|
|
+ it: u.indexOf('2345Explorer') > -1,
|
|
|
+ }, {
|
|
|
+ name: 'edge',
|
|
|
+ it: u.indexOf('Edge') > -1,
|
|
|
+ }, {
|
|
|
+ name: 'ie11',
|
|
|
+ it: u.indexOf('Trident') > -1 && u.indexOf('rv:11.0') > -1,
|
|
|
+ }, {
|
|
|
+ name: 'ie',
|
|
|
+ it: u.indexOf('compatible') > -1 && u.indexOf('MSIE') > -1,
|
|
|
+ }, {
|
|
|
+ name: 'firefox',
|
|
|
+ it: u.indexOf('Firefox') > -1,
|
|
|
+ }, {
|
|
|
+ name: 'safari',
|
|
|
+ it: u.indexOf('Safari') > -1 && u.indexOf('Chrome') === -1 && u.indexOf('(KHTML, like Gecko) Version') >
|
|
|
+ -1 && u.indexOf('MQQBrowser') === -1 && u.indexOf('FingerBrowser') === -1,
|
|
|
+ }, {
|
|
|
+ name: 'qqbrowser',
|
|
|
+ it: u.indexOf('MQQBrowser') > -1 && u.indexOf(' QQ') === -1,
|
|
|
+ }, {
|
|
|
+ name: 'qq',
|
|
|
+ it: u.indexOf('QQ') > -1,
|
|
|
+ }, {
|
|
|
+ name: 'chrome',
|
|
|
+ it: u.indexOf('(KHTML, like Gecko) Chrome') > -1 && u.indexOf('MiuiBrowser') === -1 && u.indexOf(
|
|
|
+ 'UCBrowser') === -1 && u.indexOf('HarmonyOS') === -1 && u.indexOf('HuaweiBrowser') === -1,
|
|
|
+ }, {
|
|
|
+ name: 'opera',
|
|
|
+ it: u.indexOf('Opera') > -1 || u.indexOf('OPR') > -1,
|
|
|
+ }, {
|
|
|
+ name: 'wechat',
|
|
|
+ it: /MicroMessenger/i.test(u)
|
|
|
+ }, ];
|
|
|
+
|
|
|
+ for (var i = 0; i < bws.length; i++) {
|
|
|
+ if (bws[i].it) {
|
|
|
+ return bws[i].name;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 'other';
|
|
|
+}
|
|
|
+
|
|
|
+export function errorFunComplete(e) {
|
|
|
+ const name = e.name;
|
|
|
+ if (name === 'NotFoundError' || name === 'DevicesNotFoundError') {
|
|
|
+ Toast.fail(errorMessage.ms4);
|
|
|
+ }
|
|
|
+ if (name === 'NotReadableError' || name === 'TrackStartError') {
|
|
|
+ Toast.fail(errorMessage.ms5);
|
|
|
+ } else if (name === 'OverconstrainedError' || name === 'ConstraintNotSatisfiedError') {
|
|
|
+ Toast.fail(errorMessage.ms6);
|
|
|
+ } else if (name === 'NotAllowedError' || name === 'PermissionDeniedError') {
|
|
|
+ Toast.fail(errorMessage.ms7);
|
|
|
+ } else if (name === 'TypeError' || name === 'TypeError') {
|
|
|
+ Toast.fail(errorMessage.ms8);
|
|
|
+ } else {
|
|
|
+ Toast.fail(errorMessage.ms9);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+let constraints = {
|
|
|
+ audio: false,
|
|
|
+ video: {
|
|
|
+ width: 480,
|
|
|
+ height: 320,
|
|
|
+ sourceId: 'default',
|
|
|
+ deviceId: 'default',
|
|
|
+ transform: 'rotate(180deg)',
|
|
|
+ facingMode: {
|
|
|
+ // exact: 'user'
|
|
|
+ },
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+// 校验权限
|
|
|
+export function check(success, error, backFun) {
|
|
|
+
|
|
|
+ if (!navigator) {
|
|
|
+ Toast.fail({
|
|
|
+ message: errorMessage.ms1,
|
|
|
+ duration: 5000
|
|
|
+ });
|
|
|
+ // 当前浏览器版本 无媒体对象
|
|
|
+ backFun && backFun();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ const resoutData = checkPlatform2();
|
|
|
+ const timeD = resoutData.waitCode ? 5000 : 0;
|
|
|
+ if (!resoutData.data) {
|
|
|
+ backFun && backFun();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
|
|
+ //最新的标准API 返回promise
|
|
|
+ navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
|
|
|
+ } else if (navigator.webkitGetUserMedia) {
|
|
|
+ //webkit核心浏览器
|
|
|
+ navigator.webkitGetUserMedia(constraints, success, error);
|
|
|
+
|
|
|
+ } else if (navigator.mozGetUserMedia) {
|
|
|
+ //firfox浏览器
|
|
|
+ navigator.mozGetUserMedia(constraints, success, error);
|
|
|
+
|
|
|
+ } else if (navigator.getUserMedia) {
|
|
|
+ //旧版API
|
|
|
+ navigator.getUserMedia(constraints, success, error);
|
|
|
+ } else {
|
|
|
+ Toast.fail(errorMessage.ms2);
|
|
|
+ backFun && backFun();
|
|
|
+ }
|
|
|
+ }, timeD);
|
|
|
+ // 没有媒体对象
|
|
|
+ // Toast.fail(errorMessage.ms2);
|
|
|
+ // 1. ios 14.3版本一下
|
|
|
+ // 2. 当前浏览器非完整版
|
|
|
+}
|
|
|
+
|
|
|
+// 抓拍确认
|
|
|
+export function check2(success, error) {
|
|
|
+
|
|
|
+
|
|
|
+ if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
|
|
+ //最新的标准API 返回promise
|
|
|
+ navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
|
|
|
+ } else if (navigator.webkitGetUserMedia) {
|
|
|
+ //webkit核心浏览器
|
|
|
+ navigator.webkitGetUserMedia(constraints, success, error);
|
|
|
+
|
|
|
+ } else if (navigator.mozGetUserMedia) {
|
|
|
+ //firfox浏览器
|
|
|
+ navigator.mozGetUserMedia(constraints, success, error);
|
|
|
+ } else if (navigator.getUserMedia) {
|
|
|
+ //旧版API
|
|
|
+ navigator.getUserMedia(constraints, success, error);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+import {
|
|
|
+ ref,
|
|
|
+ nextTick
|
|
|
+} from "vue"
|
|
|
+// H5 播放抓拍功能
|
|
|
+export function useH5Camera({
|
|
|
+ elVideoId,
|
|
|
+ elCanvasId,
|
|
|
+ onVideoSuccess, // 成功播放回调
|
|
|
+ onVideoError, // 失败回调
|
|
|
+ zhuapaiHttp, // 抓拍接口将base64 上传
|
|
|
+}) {
|
|
|
+ const videoRef = ref('');
|
|
|
+ videoRef.value = document.querySelector(`${elVideoId} .uni-video-video`);
|
|
|
+
|
|
|
+ function videoSuccessFun(MediaStream) {
|
|
|
+ // 赋值流
|
|
|
+ videoRef.value.srcObject = MediaStream;
|
|
|
+ // 设置video监听 为了确实获取视频播放判断有视频数据流
|
|
|
+ addVideoListener();
|
|
|
+ // 播放video 执行播放操作
|
|
|
+ playVideo();
|
|
|
+ }
|
|
|
+
|
|
|
+ function addVideoListener() {
|
|
|
+ videoRef && videoRef.value.addEventListener('play', onVideoPlay);
|
|
|
+ }
|
|
|
+
|
|
|
+ function removeVideoListener() {
|
|
|
+ videoRef && videoRef.value.removeEventListener('play', onVideoPlay);
|
|
|
+ }
|
|
|
+
|
|
|
+ function playVideo() {
|
|
|
+ if (videoRef.value) {
|
|
|
+ videoRef.value.play();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function onVideoPlay() {
|
|
|
+ onVideoSuccess && onVideoSuccess();
|
|
|
+ }
|
|
|
+
|
|
|
+ function videoErrorFun(e) {
|
|
|
+ console.log('错误', e)
|
|
|
+ removeVideoListener();
|
|
|
+ onVideoError && onVideoError(e)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 主动开启
|
|
|
+ function startH5Camera() {
|
|
|
+ check2(videoSuccessFun, videoErrorFun);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 主动关闭
|
|
|
+ function stopH5Camera() {
|
|
|
+ // 重置虚拟流
|
|
|
+ const stream = videoRef && videoRef.value.srcObject;
|
|
|
+ if (!stream) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const tracks = stream.getTracks();
|
|
|
+ tracks.forEach(function(track) {
|
|
|
+ track.stop();
|
|
|
+ });
|
|
|
+ // 销毁视频资源
|
|
|
+ videoRef.value.srcObject = null;
|
|
|
+ // 移除监听
|
|
|
+ removeVideoListener && removeVideoListener();
|
|
|
+ }
|
|
|
+
|
|
|
+ function handlePaiZhao() {
|
|
|
+ try {
|
|
|
+ const streamActive = videoRef.value.srcObject.active;
|
|
|
+ // 判断视频流 是否运行
|
|
|
+ if (!streamActive) {
|
|
|
+ onVideoError && onVideoError(new Error('摄像头抓拍异常'))
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ onVideoError && onVideoError(new Error('摄像头抓拍异常'))
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ let canvas = document.querySelector(`${elCanvasId} .uni-canvas-canvas`);
|
|
|
+ let context = canvas.getContext('2d');
|
|
|
+ context.drawImage(videoRef.value, 0, 0, videoRef.value.clientWidth, videoRef.value.clientHeight);
|
|
|
+ const ImageFile = context.canvas.toDataURL('image/png');
|
|
|
+ getSnapShotImage(ImageFile);
|
|
|
+ } catch (err) {
|
|
|
+ console.error('源 :绘图失败', err);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function getSnapShotImage(data) {
|
|
|
+ console.log('base64',data)
|
|
|
+ const imgData = data.split(';base64,');
|
|
|
+ if (!imgData.length) {
|
|
|
+ console.error('【源 :拍照数据异常,未找到图片二进制数据分割节点: `;base64,`】');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const opt = {
|
|
|
+ data: imgData[1],
|
|
|
+ prefix: 'kaoshi/zhuapai',
|
|
|
+ suffix: 'png',
|
|
|
+ };
|
|
|
+
|
|
|
+ console.log('optoptopt',opt)
|
|
|
+ zhuapaiHttp && zhuapaiHttp(opt)
|
|
|
+ .then(res => {
|
|
|
+ console.log('【源 : 获取抓拍数据】');
|
|
|
+ this.$emit('getImage', res.data);
|
|
|
+ })
|
|
|
+ .catch(err => {
|
|
|
+ console.error('源 :抓拍接口异常', err);
|
|
|
+ this.$router.push({
|
|
|
+ name: 'examList'
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ startH5Camera,
|
|
|
+ stopH5Camera,
|
|
|
+ handlePaiZhao
|
|
|
+ }
|
|
|
+}
|