Browse Source

人脸识别

master
xuhuajiao 3 months ago
parent
commit
7730c8a63a
  1. BIN
      src/assets/images/faceH5/img4.png
  2. 2
      src/main.js
  3. 2
      src/router/index.js
  4. 5
      src/router/routers.js
  5. 20
      src/utils/upload.js
  6. 8
      src/views/faceRecognition/faceRecLog.vue
  7. 553
      src/views/faceRecognition/module/camera.vue
  8. 33
      src/views/faceRecognition/module/faceSearch.vue
  9. 1
      src/views/faceRecognition/module/selfRegister.vue
  10. 180
      src/views/faceRecognition/personInfoManage.vue
  11. 44
      src/views/faceRegisterH5/fail.vue
  12. 8
      src/views/faceRegisterH5/selfRegister.vue

BIN
src/assets/images/faceH5/img4.png

After

Width: 1287  |  Height: 1000  |  Size: 112 KiB

2
src/main.js

@ -34,9 +34,7 @@ import 'viewerjs/dist/viewer.css'
import Print from 'vue-print-nb'
// import Vconsole from 'vconsole'
// const vConsole = new Vconsole()
// export default vConsole
// 加载用户主题

2
src/router/index.js

@ -9,7 +9,7 @@ import { filterAsyncRouter } from '@/store/modules/permission'
NProgress.configure({ showSpinner: false })// NProgress Configuration
const whiteList = ['/login', '/faceRegisterH5', '/faceError', '/faceSuccess', '/faceRegistered', '/faceSelfRegister']// no redirect whitelist
const whiteList = ['/login', '/faceRegisterH5', '/faceError', '/faceSuccess', '/faceRegistered', '/faceSelfRegister', '/faceFail']// no redirect whitelist
router.beforeEach((to, from, next) => {
if (to.meta.title) {

5
src/router/routers.js

@ -122,6 +122,11 @@ export const constantRouterMap = [
meta: { title: '人脸信息自助采集', noCache: true },
component: (resolve) => require(['@/views/faceRegisterH5/selfRegister'], resolve),
hidden: true
},
{ path: '/faceFail',
meta: { title: '人脸信息自助采集', noCache: true },
component: (resolve) => require(['@/views/faceRegisterH5/fail'], resolve),
hidden: true
}
]

20
src/utils/upload.js

@ -155,3 +155,23 @@ export function uploadPerson(api, file, params) {
}
return axios.post(api, data, config)
}
export function uploadFaceImgBase64(api, base64String) {
// 注意:如果base64字符串包含前缀(如data:image/jpeg;base64,),
// 可能需要根据后端要求决定是否去除
// const pureBase64 = base64String.replace(/^data:image\/\w+;base64,/, '')
return axios({
url: api + '/api/fileRelevant/uploadFaceImgBase64',
method: 'post',
data: {
base64String: base64String // 这里的键名要和后端接口要求的参数名一致
},
headers: {
'Authorization': getToken(),
'Content-Type': 'application/json' // 以JSON格式发送
// 如果后端要求form-data格式,可以使用multipart/form-data
// 'Content-Type': 'multipart/form-data'
}
})
}

8
src/views/faceRecognition/faceRecLog.vue

@ -8,6 +8,10 @@
<i slot="prefix" class="iconfont icon-zhuangtai" />
<el-option v-for="item in userTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
</el-select>
<el-select v-model="query.snapshotOperation" clearable size="small" placeholder="操作类型" class="filter-item" style="width: 150px;" @change="crud.toQuery">
<i slot="prefix" class="iconfont icon-zhuangtai" />
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
</el-select>
<el-input
v-model="keyWord"
size="small"
@ -27,10 +31,6 @@
/>
</el-select>
</el-input>
<el-select v-model="query.snapshotOperation" clearable size="small" placeholder="操作类型" class="filter-item" style="width: 150px;" @change="crud.toQuery">
<i slot="prefix" class="iconfont icon-zhuangtai" />
<el-option v-for="item in enabledTypeOptions" :key="item.key" :label="item.display_name" :value="item.key" />
</el-select>
<date-range-picker v-model="blurryTime" class="date-item" />
<rrOperation>
<template v-slot:right>

553
src/views/faceRecognition/module/camera.vue

@ -0,0 +1,553 @@
<template>
<div class="camera-container">
<input v-model="imgValue" type="hidden">
<el-button class="subsystembtn" @click="openCamera">打开摄像头</el-button>
<el-select v-if="devices.length > 0" v-model="selectedDeviceId" @change="getStream">
<el-option
v-for="(device,index) in devices"
:key="index"
:value="device.deviceId"
:label="`摄像头 ${device.index + 1}`"
/>
</el-select>
<!-- 错误提示 -->
<div v-if="errorMessage" class="error-message">{{ errorMessage }}</div>
<!-- 加载提示 -->
<div v-if="isLoading" class="loading-message">正在获取摄像头画面请稍候...</div>
<!-- 调试信息 (生产环境可隐藏) -->
<div v-if="showDebugInfo" class="debug-info">
<p>调试信息:</p>
<p>设备数量: {{ devices.length }}</p>
<p>选中设备: {{ selectedDeviceId ? '存在' : '未选择' }}</p>
<p>视频状态: {{ getVideoStatusText() }}</p>
<p>流状态: {{ stream ? '已获取' : '未获取' }}</p>
<p v-if="stream">轨道状态: {{ stream.getVideoTracks()[0]?.readyState || '未知' }}</p>
<p v-if="videoDimensions">视频尺寸: {{ videoDimensions.width }}x{{ videoDimensions.height }}</p>
</div>
<div v-if="showVideo" id="videoContainer">
<!-- 视频容器增加背景和边框明确显示区域 -->
<div style="display: flex; justify-content: center;">
<div class="video-wrapper">
<video
ref="video"
:style="{display: videoVisible ? 'block' : 'none'}"
width="240"
autoplay
playsinline
muted
class="video-feed"
/>
</div>
<canvas
ref="canvasPreview"
:style="{display: canvasVisible ? 'block' : 'none', marginLeft: '15px'}"
width="240"
height="320"
/>
<canvas
ref="canvasUpload"
style="display: none;"
width="240"
height="320"
/>
</div>
<button
class="subsystembtn take-btn"
:style="{display: videoVisible ? 'block' : 'none'}"
:disabled="isLoading || !isVideoPlaying"
@click="takePhoto"
>
拍照
</button>
<button
class="subsystembtn take-btn"
:style="{display: canvasVisible ? 'block' : 'none',}"
@click="retakePhoto"
>
重拍
</button>
<!-- 调试按钮 -->
<!-- <button
class="debug-btn"
@click="toggleDebugInfo"
>
{{ showDebugInfo ? '隐藏调试' : '显示调试' }}
</button> -->
</div>
</div>
</template>
<script>
export default {
name: 'CameraCapture',
data() {
return {
imgValue: '',
devices: [],
selectedDeviceId: '',
showVideo: false,
videoVisible: true,
canvasVisible: false,
stream: null,
errorMessage: '',
isLoading: false,
isVideoPlaying: false,
videoDimensions: null, //
showDebugInfo: false, //
videoInterval: null //
}
},
mounted() {
// this.checkBrowserSupport()
// this.enumerateDevices()
},
beforeDestroy() {
this.stopStream()
if (this.videoInterval) {
clearInterval(this.videoInterval)
}
},
methods: {
//
getVideoStatusText() {
if (!this.$refs.video) return '未初始化'
if (this.isLoading) return '加载中'
if (this.isVideoPlaying) return '播放中'
if (this.stream) return '已获取流但未播放'
return '未获取流'
},
//
toggleDebugInfo() {
this.showDebugInfo = !this.showDebugInfo
},
//
checkBrowserSupport() {
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
this.errorMessage = '您的浏览器不支持摄像头访问,请使用Chrome、Firefox或Edge等现代浏览器'
return false
}
return true
},
//
enumerateDevices() {
return navigator.mediaDevices.enumerateDevices()
.then(devices => {
this.handleDevices(devices)
return devices
})
.catch(error => {
this.handleError(error)
return Promise.reject(error)
})
},
handleDevices(deviceInfos) {
this.devices = deviceInfos
.filter(device => device.kind === 'videoinput' && device.deviceId) // deviceId
.map((device, index) => ({
...device,
index,
deviceId: device.deviceId || `device-${index}` // ID
}))
console.log('检测到的摄像头设备:', this.devices)
if (this.devices.length > 0 && !this.selectedDeviceId) {
this.selectedDeviceId = this.devices[0].deviceId
} else if (this.devices.length === 0) {
this.errorMessage = '未检测到摄像头设备,请检查硬件连接'
}
},
// -
getStream() {
//
if (this.videoInterval) {
clearInterval(this.videoInterval)
}
this.stopStream()
this.errorMessage = ''
this.isLoading = true
this.isVideoPlaying = false
this.videoDimensions = null
//
// getStreamconstraints
const constraints = {
audio: false,
video: {
...(this.selectedDeviceId ? { deviceId: { exact: this.selectedDeviceId }} : {}),
facingMode: 'user',
width: { ideal: 240 },
height: { ideal: 320 },
aspectRatio: 3 / 4 // 640x480
}
}
//
if (this.selectedDeviceId && !this.devices.some(d => d.deviceId === this.selectedDeviceId)) {
this.errorMessage = '所选摄像头设备不存在'
this.isLoading = false
return
}
//
const streamPromise = navigator.mediaDevices.getUserMedia(constraints)
// 10
Promise.race([
streamPromise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('获取摄像头流超时,请检查设备是否被占用')), 10000)
)
])
.then(stream => this.handleStream(stream))
.catch(error => this.handleError(error))
.finally(() => {
this.isLoading = false
})
},
handleStream(stream) {
this.stream = stream
if (!this.$refs.video) {
this.errorMessage = '视频元素未初始化'
return
}
if (this.$refs.video) {
this.$refs.video.style.transform = 'translateZ(0)'
this.$refs.video.style.willChange = 'auto'
}
//
this.$refs.video.oncanplay = null
this.$refs.video.onerror = null
this.$refs.video.onplaying = null
this.$refs.video.onended = null
this.$refs.video.onloadedmetadata = null
//
this.$refs.video.pause()
this.$refs.video.srcObject = null
//
const videoTracks = stream.getVideoTracks()
if (videoTracks.length > 0) {
console.log('视频轨道状态:', videoTracks[0].readyState)
videoTracks[0].onended = () => {
console.log('视频轨道已结束')
this.errorMessage = '视频轨道已中断,请重新连接'
this.isVideoPlaying = false
}
}
//
this.$refs.video.srcObject = stream
//
this.$refs.video.onloadedmetadata = () => {
console.log('视频元数据加载完成')
this.videoDimensions = {
width: this.$refs.video.videoWidth,
height: this.$refs.video.videoHeight
}
console.log('视频尺寸:', this.videoDimensions)
}
//
this.$refs.video.onerror = (e) => {
console.error('视频元素错误:', e)
console.error('错误代码:', e.target.error.code)
const errorCodes = {
1: '用户终止',
2: '网络错误',
3: '解码错误',
4: '无法播放',
5: '加密错误'
}
this.errorMessage = `视频播放失败: ${errorCodes[e.target.error.code] || '未知错误'}`
}
//
this.$refs.video.oncanplay = () => {
console.log('视频流已准备就绪')
this.errorMessage = ''
//
this.$refs.video.play().catch(err => this.handlePlayError(err))
}
//
this.$refs.video.onplaying = () => {
console.log('视频开始播放')
this.isVideoPlaying = true
this.errorMessage = ''
}
//
this.$refs.video.onended = () => {
console.log('视频流已结束')
this.isVideoPlaying = false
this.errorMessage = '视频流已中断,请重新获取'
}
//
this.$refs.video.play().catch(err => this.handlePlayError(err))
//
this.videoInterval = setInterval(() => {
if (this.$refs.video && this.$refs.video.readyState >= 1) {
if (!this.isVideoPlaying) {
console.log('视频未播放,尝试重新播放')
this.$refs.video.play().catch(err => this.handlePlayError(err))
}
}
}, 2000)
},
//
handlePlayError(err) {
console.error('播放失败:', err)
const playErrors = {
'NotAllowedError': '播放被阻止,可能是自动播放策略限制',
'NotSupportedError': '浏览器不支持此视频格式',
'AbortError': '播放被中止',
'InvalidStateError': '视频元素状态无效'
}
this.errorMessage = `播放失败: ${playErrors[err.name] || err.message}`
},
//
stopStream() {
if (this.stream) {
try {
this.stream.getTracks().forEach(track => {
track.stop()
})
} catch (e) {
console.error('停止流时出错:', e)
}
this.stream = null
}
if (this.$refs.video) {
this.$refs.video.pause()
this.$refs.video.srcObject = null
}
this.isVideoPlaying = false
},
//
openCamera() {
if (!this.checkBrowserSupport()) return
this.enumerateDevices().then(() => {
if (this.devices.length > 0) {
this.showVideo = true
this.videoVisible = true
this.canvasVisible = false
this.getStream()
} else {
this.errorMessage = '未检测到摄像头设备'
}
})
},
//
takePhoto() {
if (!this.$refs.video || !this.$refs.canvasPreview || !this.$refs.canvasUpload) {
this.errorMessage = '组件未完全加载'
return
}
if (!this.isVideoPlaying) {
this.errorMessage = '视频未就绪,请稍候再试'
return
}
try {
const previewCtx = this.$refs.canvasPreview.getContext('2d')
previewCtx.drawImage(this.$refs.video, 0, 0, 240, 320)
const uploadCtx = this.$refs.canvasUpload.getContext('2d')
uploadCtx.drawImage(this.$refs.video, 0, 0, 240, 320)
this.videoVisible = false
this.canvasVisible = true
this.imgValue = this.$refs.canvasUpload.toDataURL('image/jpeg')
this.errorMessage = ''
// uploadFaceImgBase64(this.baseApi, this.imgValue).then(res => {
// console.log(res)
// if (res.data.code === 200) {
// // this.form.personPhotoUrl = res.data.data
// this.$emit('cameraData', this.imgValue, res.data.data)
// }
// })
} catch (e) {
console.error('拍照失败:', e)
this.errorMessage = '拍照失败,请重试'
}
},
//
retakePhoto() {
this.videoVisible = true
this.canvasVisible = false
this.imgValue = ''
if (!this.stream || !this.isVideoPlaying) {
this.getStream()
}
},
//
getImgData() {
return this.imgValue
},
//
handleError(error) {
console.error('摄像头错误:', error)
switch (error.name) {
case 'NotAllowedError':
this.errorMessage = '请授予摄像头访问权限(通常在浏览器地址栏附近)'
break
case 'NotFoundError':
this.errorMessage = '未找到指定的摄像头设备'
break
case 'NotReadableError':
this.errorMessage = '摄像头被占用或无法访问,请关闭其他使用摄像头的程序'
break
case 'OverconstrainedError':
this.errorMessage = '摄像头不支持所需的参数,请尝试其他设备'
break
case 'TypeError':
this.errorMessage = '摄像头参数错误,请刷新页面重试'
break
default:
this.errorMessage = `${error.name}: ${error.message}`
}
}
}
}
</script>
<style scoped>
.camera-container {
text-align: center;
}
#videoContainer {
margin-top: 15px;
position: relative;
}
.video-wrapper {
display: inline-block;
border: 1px solid #ddd;
border-radius: 4px;
/* background-color: #000; */
position: relative;
}
/* 视频加载时显示提示 */
.video-wrapper::before {
content: '等待视频流...';
position: absolute;
color: #aaa;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
}
.video-feed {
width: 240px !important;
height: 320px !important;
display: block !important;
opacity: 1 !important;
z-index: 9999 !important;
background-color: transparent !important;
}
.subsystembtn {
width: 120px;
height: 30px;
background-color: #1d5db2;
border: 0;
color: white;
font-size: 14px;
cursor: pointer;
margin: 5px;
border-radius: 4px;
transition: background-color 0.3s;
}
.subsystembtn:disabled {
background-color: #999;
cursor: not-allowed;
}
.subsystembtn:hover:not(:disabled) {
background-color: #164b8c;
}
.take-btn{
margin: 15px auto 0 auto;
}
.el-select {
margin: 0 10px;
width: auto;
}
.error-message {
color: #dc3545;
margin: 10px 0;
padding: 8px;
background-color: #f8d7da;
border-radius: 4px;
font-size: 14px;
}
.loading-message {
color: #666;
margin: 10px 0;
padding: 8px;
background-color: #f8f9fa;
border-radius: 4px;
font-size: 14px;
}
.debug-info {
margin: 10px 0;
padding: 10px;
background-color: #f0f7ff;
border-radius: 4px;
font-size: 12px;
text-align: left;
color: #333;
}
.debug-btn {
margin-top: 10px;
padding: 5px 10px;
font-size: 12px;
background-color: #f0f0f0;
border: 1px solid #ddd;
border-radius: 3px;
cursor: pointer;
}
</style>

33
src/views/faceRecognition/module/faceSearch.vue

@ -92,32 +92,27 @@ export default {
this.isLoading = true
this.searchPerson = {}
try {
const fileBase64 = await this.getBase64(this.file)
this.imageUrl = fileBase64
const fileBase64 = await this.getBase64(this.file)
this.imageUrl = fileBase64
const params = {
'libcode': this.user.fonds.fondsNo
}
const params = {
'libcode': this.user.fonds.fondsNo
}
const res = await uploadPerson(this.baseApi + '/api/person/findPersonFace', this.file, params)
const res = await uploadPerson(this.baseApi + '/api/person/findPersonFace', this.file, params)
console.log(res)
if (res.data.code === 200) {
if (res.data.code === 200) {
if (res.data.data.includes('"personId"')) {
const result = JSON.parse(res.data.data)
if (result.personId) {
this.searchPerson = result
} else {
this.$message({ message: res.data.data, type: 'error', offset: 8 })
}
this.searchPerson = result
} else {
this.$message({ message: '查询失败,请重试', type: 'error', offset: 8 })
this.$message({ message: res.data.data, type: 'error', offset: 8 })
}
} catch (error) {
console.error('人脸查询出错:', error)
this.$message({ message: '查询过程出错,请重试', type: 'error', offset: 8 })
} finally {
this.isLoading = false
} else {
this.$message({ message: res.data.data, type: 'error', offset: 8 })
}
this.isLoading = false
} else {
this.$message({ message: '只可上传图片', type: 'error', offset: 8 })
this.imageUrl = null

1
src/views/faceRecognition/module/selfRegister.vue

@ -11,6 +11,7 @@
<div class="qrcode-wrapper">
<div ref="qrcode" class="qrcode" />
</div>
<div style="font-size: 12px; color: #9098a4; margin-top: 10px;">注意请确保手机接入局域网WIFI能访问后台的网络然后扫码并完成自助人脸注册操作</div>
</div>
</template>

180
src/views/faceRecognition/personInfoManage.vue

@ -80,26 +80,7 @@
<input id="upFile" type="file" name="upFile" accept="image/*" @change="changeFile($event)">
<el-button size="small" type="primary"><i class="iconfont icon-shangchuan" />选择上传照片</el-button>
</div>
<el-button type="primary" @click="showCamera = true">
<i class="iconfont icon-yulan" />摄像头拍摄
</el-button>
</div>
<!-- 摄像头拍摄区域 (默认隐藏) -->
<div v-if="showCamera" class="camera-container">
<video
id="video"
autoplay
style="width: 100%; max-width: 400px; border: 1px solid #ccc;"
/>
<canvas
id="canvas"
style="display: none; width: 100%; max-width: 400px;"
/>
<div class="camera-buttons">
<el-button type="warning" @click="takePhoto">拍照</el-button>
<el-button type="info" @click="resetCamera">重拍</el-button>
<el-button type="danger" @click="closeCamera">关闭</el-button>
</div>
<el-button type="primary" @click="showCamera"><i class="iconfont icon-yulan" />摄像头拍摄</el-button>
</div>
</div>
<div slot="footer" class="dialog-footer">
@ -161,6 +142,14 @@
<SelfRegister ref="selfRegisterRefs" :is-dialog-face-regsiter="true" />
</el-dialog>
<el-dialog :close-on-click-modal="false" :modal-append-to-body="false" append-to-body title="摄像头拍摄" :visible.sync="cameraVisible" :before-close="handleClose">
<Camera ref="cameraRefs" />
<div slot="footer" class="dialog-footer">
<el-button type="text" @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleCamera">确定</el-button>
</div>
</el-dialog>
</div>
</template>
@ -173,15 +162,16 @@ import pagination from '@crud/Pagination'
import FaceSearch from './module/faceSearch'
import SelfRegister from './module/selfRegister'
import BatchImport from './module/batchImport'
import Camera from './module/camera'
// import { exportFile } from '@/utils/index'
// import qs from 'qs'
import { mapGetters } from 'vuex'
import { upload } from '@/utils/upload'
import { upload, uploadFaceImgBase64 } from '@/utils/upload'
const defaultForm = { personId: null, libcode: null, barcode: null, personName: null, personPhotoUrl: null, idCard: null, phone: null, personSex: 1, personType: 0, personPhotoBase64: null }
export default {
name: 'PersonInfoManage',
components: { pagination, crudOperation, rrOperation, FaceSearch, SelfRegister, BatchImport },
components: { pagination, crudOperation, rrOperation, FaceSearch, SelfRegister, BatchImport, Camera },
cruds() {
return CRUD({ title: '人员信息', idField: 'personId', url: 'api/person/initPersonInfo', crudMethod: { ...crudFace }, optShow: {
add: true,
@ -225,12 +215,7 @@ export default {
imageUrl: null,
selfRegisterVisible: false,
faceSearchVisible: false,
//
showCamera: false,
stream: null,
videoElement: null,
canvasElement: null
cameraVisible: false
}
},
computed: {
@ -239,25 +224,30 @@ export default {
'user'
])
},
watch: {
//
showCamera(val) {
if (val) {
this.openCamera()
} else {
this.stopStream()
}
}
},
beforeDestroy() {
//
this.stopStream()
},
created() {
},
mounted() {
},
methods: {
showCamera() {
this.cameraVisible = true
this.$nextTick(() => {
this.$refs.cameraRefs.checkBrowserSupport()
this.$refs.cameraRefs.enumerateDevices()
})
},
handleCamera() {
if (this.$refs.cameraRefs && this.$refs.cameraRefs.imgValue) {
uploadFaceImgBase64(this.baseApi, this.$refs.cameraRefs.imgValue).then(res => {
console.log(res)
if (res.data.code === 200) {
this.form.personPhotoUrl = res.data.data
this.imageUrl = this.$refs.cameraRefs.imgValue
this.handleClose()
}
})
}
},
updatePerson() {
this.crud.refresh()
},
@ -311,10 +301,10 @@ export default {
},
//
[CRUD.HOOK.afterValidateCU](crud) {
// if (crud.form.personPhotoUrl === '' || crud.form.personPhotoUrl === null) {
// this.$message({ message: '', type: 'error', offset: 8 })
// return false
// }
if (crud.form.personPhotoUrl === '' || crud.form.personPhotoUrl === null) {
this.$message({ message: '请选择上传照片', type: 'error', offset: 8 })
return false
}
console.log(crud.form)
return true
},
@ -438,67 +428,12 @@ export default {
this.$refs.faceSearchRefs.searchPerson = {}
this.$refs.faceSearchRefs.btnLoading = false
}
},
//
async openCamera() {
try {
//
this.videoElement = document.getElementById('video')
this.canvasElement = document.getElementById('canvas')
//
this.stream = await navigator.mediaDevices.getUserMedia({
video: { width: 400, height: 300 },
audio: false
})
this.videoElement.srcObject = this.stream
} catch (error) {
this.$message.error('无法访问摄像头:' + error.message)
console.error('摄像头访问错误:', error)
}
},
takePhoto() {
if (!this.videoElement || !this.canvasElement) return
//
this.canvasElement.width = this.videoElement.videoWidth
this.canvasElement.height = this.videoElement.videoHeight
//
const context = this.canvasElement.getContext('2d')
context.drawImage(this.videoElement, 0, 0)
// base64
const imageData = this.canvasElement.toDataURL('image/jpeg')
//
this.imageUrl = imageData
this.form.photo = imageData
//
this.stopStream()
},
resetCamera() {
//
this.stopStream()
this.openCamera()
},
closeCamera() {
//
this.stopStream()
this.showCamera = false
},
stopStream() {
if (this.stream) {
this.stream.getTracks().forEach(track => {
track.stop()
})
this.stream = null
this.cameraVisible = false
if (this.$refs.cameraRefs) {
this.$refs.cameraRefs.stopStream()
this.$refs.cameraRefs.imgValue = ''
this.$refs.cameraRefs.errorMessage = ''
this.$refs.cameraRefs.showVideo = false
}
}
}
@ -551,35 +486,4 @@ export default {
border-color: #909399;
color: #909399;
}
.camera-container {
margin-top: 20px;
padding: 15px;
border: 1px solid #e6e6e6;
border-radius: 4px;
}
.camera-buttons {
margin-top: 15px;
display: flex;
gap: 10px;
justify-content: center;
}
.right-user-img {
margin-left: 30px;
text-align: center;
}
.right-user-img img {
width: 200px;
height: 260px;
object-fit: cover;
border: 1px solid #e6e6e6;
margin-bottom: 15px;
}
.upload-btn input {
display: none;
}
</style>

44
src/views/faceRegisterH5/fail.vue

@ -0,0 +1,44 @@
<template>
<div class="face-mobile">
<!-- 查到用户信息并且已经绑定过人脸 -->
<div class="face-valid">
<img src="@/assets/images/faceH5/img4.png" alt="">
<p>未成功识别人脸人脸注册失败</p>
<div class="reg-btn-group">
<div class="return-btn other-return" @click="returnValid">返回首页</div>
<div class="return-btn" @click="toSelfRegister">重新注册</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Registered',
data() {
return {
}
},
watch: {
},
methods: {
returnValid() {
this.$router.push({
path: '/faceRegisterH5',
query: { 'strLibcode': this.$route.query.strLibcode }
})
},
toSelfRegister() {
this.$router.push({
path: '/faceSelfRegister',
query: { 'strLibcode': this.$route.query.strLibcode }
})
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/assets/styles/faceMobile.scss";
</style>

8
src/views/faceRegisterH5/selfRegister.vue

@ -127,17 +127,18 @@ export default {
console.log('params', params)
FetchEditReaderFace(params).then(res => {
console.log('res', res)
if (res) {
if (res.code === 200) {
this.$router.push({
path: '/faceSuccess',
query: { 'strLibcode': this.personInfo.libcode }
})
} else {
this.$router.push({
path: '/faceError',
path: '/faceFail',
query: { 'strLibcode': this.personInfo.libcode }
})
}
// faceFail
this.submitLoading = false
}).catch(err => {
console.log(err)
@ -152,10 +153,11 @@ export default {
input.onchange = (e) => {
const file = e.target.files[0]
if (file) {
this.imageUrl = URL.createObjectURL(file)
// this.imageUrl = URL.createObjectURL(file)
upload(this.baseApi + '/api/fileRelevant/uploadFaceImg', file).then(res => {
console.log(res)
if (res.data.code === 200) {
this.imageUrl = this.baseApi + '/api/fileRelevant/getImg?imgType=3&imgId=' + res.data.data
this.form.personPhotoUrl = res.data.data
}
})

Loading…
Cancel
Save