Commit d2c4018b authored by kang.nie@inzymeits.com's avatar kang.nie@inzymeits.com
Browse files

初始化代码

parent de8a8ae5
Pipeline #3105 failed with stages
in 0 seconds
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>ico_清除条件@2x</title>
<g id="车企实名制系统" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="入网协议-签字" transform="translate(-945.000000, -242.000000)" fill-rule="nonzero">
<g id="编组-7" transform="translate(407.000000, 155.000000)">
<g id="Group-52" transform="translate(522.000000, 78.000000)">
<g id="清除" transform="translate(16.000000, 9.000000)">
<rect id="矩形" fill="#000000" opacity="0" x="0" y="0" width="16" height="16"></rect>
<path d="M14.0484375,13.5875 L13.2203125,8.8125 L13.5,8.8125 C13.725,8.8125 13.90625,8.63125 13.90625,8.40625 L13.90625,5.40625 C13.90625,5.18125 13.725,5 13.5,5 L9.65625,5 L9.65625,2.15625 C9.65625,1.93125 9.475,1.75 9.25,1.75 L6.75,1.75 C6.525,1.75 6.34375,1.93125 6.34375,2.15625 L6.34375,5 L2.5,5 C2.275,5 2.09375,5.18125 2.09375,5.40625 L2.09375,8.40625 C2.09375,8.63125 2.275,8.8125 2.5,8.8125 L2.7796875,8.8125 L1.9515625,13.5875 C1.946875,13.6109375 1.9453125,13.634375 1.9453125,13.65625 C1.9453125,13.88125 2.1265625,14.0625 2.3515625,14.0625 L13.6484375,14.0625 C13.671875,14.0625 13.6953125,14.0609375 13.7171875,14.05625 C13.9390625,14.01875 14.0875,13.8078125 14.0484375,13.5875 Z M3.1875,6.09375 L7.4375,6.09375 L7.4375,2.84375 L8.5625,2.84375 L8.5625,6.09375 L12.8125,6.09375 L12.8125,7.71875 L3.1875,7.71875 L3.1875,6.09375 Z M10.5,12.96875 L10.5,10.53125 C10.5,10.4625 10.44375,10.40625 10.375,10.40625 L9.625,10.40625 C9.55625,10.40625 9.5,10.4625 9.5,10.53125 L9.5,12.96875 L6.5,12.96875 L6.5,10.53125 C6.5,10.4625 6.44375,10.40625 6.375,10.40625 L5.625,10.40625 C5.55625,10.40625 5.5,10.4625 5.5,10.53125 L5.5,12.96875 L3.16875,12.96875 L3.8734375,8.90625 L12.125,8.90625 L12.8296875,12.96875 L10.5,12.96875 Z" id="形状" fill="#2A68FF"></path>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<template>
<div class="photo-steps">
<div :style="{margin}" class="step-list">
<div v-for="(name, i) of data" :key="i" :class="{finished: index >= i}" class="step-item">{{ name }}</div>
</div>
</div>
</template>
<script>
export default {
name: 'Steps',
props: {
data: {
type: Array,
default() {
return []
}
},
index: {
type: Number,
default() {
return 0
}
},
// 针对只有2个步骤两边需要填充更多白边时,如卡解绑UI稿
margin: {
type: String,
default() {
return '0 0 0 0'
}
}
}
}
</script>
<style lang="scss">
.photo-steps {
width: 260px;
.step-list {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
height: 34px;
&:after {
content: '';
height: 4px;
background: radial-gradient(circle at 1px 1px,#edeef0 2px, transparent 0);
background-size: 10px 10px;
position: absolute;
top: 50%;
left: 0;
right: 0;
margin-top: -2px;
z-index: 1;
}
}
.step-item {
padding: 0 12px 0 34px;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
background: #fff url(./icon_arrow.svg) no-repeat 12px center / 14px 14px;
color: rgba(132, 133, 138, 0.6);
position: relative;
z-index: 9;
&.finished {
color: #212026;
background: #fff url(./icon_arrow_finished.svg) no-repeat 12px center / 14px 14px;
}
&:first-child {
padding-left: 22px;
background-position: 0 center;
}
}
}
</style>
<template>
<el-dialog v-loading.fullscreen.lock="fullscreenLoading" :visible.sync="showDialog" :title="title" destroy-on-close width="750px" top="10vh" @close="closeDialog">
<div v-if="sign" class="device-top">
<el-button class="btn-clear" size="small" @click="openSign">{{ signList.length === 0 ? '签名' : '重签' }}</el-button>
</div>
<div class="preview-list">
<div v-for="(item, i) of photoList" :key="i" class="preview-item" >
<img :src="item.accessUrl" @click="previewPhoto(i)" >
<i class="el-icon-delete" @click="removePhoto(i)"/>
</div>
</div>
<div v-loading="loading" class="take-photo">
<canvas v-show="stepIndex === 0 && !sign" ref="photoCanvas" :width="videoParams.video_frame_w" :height="videoParams.video_frame_h" class="canvas-phone"/>
<div v-if="sign" :width="iWidth" :height="iHeight" class="canvas-sign">
<div v-for="sign in signList" :key="sign.accessUrl" class="compose-area">
<img :src="sign.agreement" crossorigin="anonymous" class="agreement-image">
<img :src="sign.accessUrl" class="sign-image">
<img :src="attachmentUrl" class="agreement-image">
</div>
<div v-if="signList.length === 0" class="tip">点击“签名”按钮后,请在高拍仪上签名</div>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button size="small" @click="closeDialog">取消</el-button>
<template v-if="sign">
<el-button :disabled="signList.length === 0" :loading="showLoading" size="small" type="primary" @click="handleComposeSign">完成</el-button>
</template>
<template v-else>
<el-button :disabled="photoList.length >= limit" size="small" class="btn-camera" @click="takePhoto()">拍摄</el-button>
<el-button :disabled="photoList.length === 0" size="small" type="primary" @click="uploadList">完成</el-button>
</template>
</div>
<image-viewer v-if="showViewer" :z-index="zIndex" :initial-index="imageIndex" :on-close="closeViewer" :url-list="urlList" />
</el-dialog>
</template>
<script>
import deviceService from '@/components/Device/service'
import ImageViewer from '@/components/Uploader/image-viewer'
import { postVinUpload, cuscImageUpload } from '@/api/realname-enterprise'
import { getToken } from '@/utils/auth'
import request from '@/utils/request'
import photoStep from './components/photoStep'
import wzh from './wzh'
import { imageViewer } from '@/utils/upload'
import { jsPDF as JsPdf } from 'jspdf'
import html2canvas from 'html2canvas'
import { queryPotocolManage } from '@/api/management'
export default {
name: 'DeviceHandle',
components: {
ImageViewer,
photoStep
},
props: {
// 展示弹窗
show: {
type: Boolean,
default: false
},
// 是否签字
sign: {
type: Boolean,
default: false
},
// 弹窗类型, photo: 图片, video: 视频
type: {
type: String,
default: 'photo'
},
// 图片个数
limit: {
type: Number,
default: 3
},
// 弹窗标题
title: {
type: String,
default: '拍照'
},
// 图片列表
data: {
type: Array,
default: () => { [] }
},
// 是否加密上传
security: {
type: Boolean,
default: false
},
// 需要签名的字段
signKey: {
type: String,
default: ''
},
// 附件
attachmentUrl: {
type: String,
default: ''
}
},
data() {
return {
fullscreenLoading: false,
loading: false,
showDialog: false,
stepIndex: 0,
stepData: ['拍摄', '签字'],
openStatus: false, // 视频打开状态
cutStatus: 0, // 0代表裁切,1代表不裁切
changeStatus: false,
// 设备宽高
deviceWidth: 0,
deviceHeight: 0,
// 画板宽高
iWidth: 800,
iHeight: 490,
// 签字状态
startSign: false,
isFirst: false,
points: [],
beginPoint: null,
videoParams: {
camera_type: 0, // 0代表主摄像头, 1代表副摄像头
video_frame_w: 0,
video_frame_h: 0
},
// 上传参数
uploadConf: {
url: this.security ? cuscImageUpload : postVinUpload,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
'Authorization': `bearer ${getToken()}`
},
data: {
rootPath: '',
path: ''
}
},
// 预览参数
photoList: [],
signList: [],
showViewer: false,
zIndex: 2000,
imageIndex: 0,
prevOverflow: '',
showLoading: false
}
},
computed: {
urlList() {
if (this.stepIndex === 0) {
return this.photoList.map(item => {
return item.accessUrl
})
} else {
return this.signList.map(item => {
return item.accessUrl
})
}
}
},
watch: {
show(val) {
this.showDialog = val
if (val) {
// 如果当前需要签名,则直接打开签名页
if (!this.sign) {
this.initList(this.data)
this.$nextTick(() => {
this.openPhoto()
})
}
} else {
this.closePhoto()
if (this.stepIndex === 1) {
this.closeSign()
}
this.stepIndex = 0
this.photoList = []
this.signList = []
this.startSign = false
}
}
},
methods: {
/**
* 初始化图片列表及签名列表数据
* @time 2022-05-12 21:03:29
*/
initList(list) {
list.forEach(item => {
if (item.type === 'photo' || !item.type) {
this.photoList.push(item)
} else if (item.type === 'sign') {
this.signList.push(item)
}
})
},
/**
* 打开拍照
* @time 2022-05-10 15:51:55
*/
openPhoto() {
this.openStatus = false
this.videoParams = {
camera_type: 0,
video_frame_w: 0,
video_frame_h: 0
}
this.loading = true
// 获取设备信息
deviceService.getVideoInfo().then(data => {
if (data.length === 2) {
// 主摄像头
const masterCamera = data.find(item => {
return item.camera_type === this.videoParams.camera_type
})
const resolutionInfo = masterCamera.resolution_info || []
const pixelIndex = parseInt(resolutionInfo.length / 2)
if (this.videoParams.video_frame_w === 0 || this.videoParams.video_frame_w === resolutionInfo[pixelIndex].width) {
this.videoParams.video_frame_w = resolutionInfo[pixelIndex].width
this.videoParams.video_frame_h = resolutionInfo[pixelIndex].height
}
const ctx = this.$refs.photoCanvas.getContext('2d')
const image = new Image()
ctx.clearRect(0, 0, this.videoParams.video_frame_w, this.videoParams.video_frame_h)
// 打开主视频
deviceService.masterOpenVideo(this.videoParams, this.cutStatus, json => {
if (!this.changeStatus) {
this.openStatus = true
}
if (json.err_code === 0) {
this.loading = false
image.src = 'data:image/jpeg;base64,' + json.data
image.onload = function() {
ctx.drawImage(image, 0, 0, image.width, image.height)
}
} else {
this.$message.error(json.err_msg)
this.loading = false
}
})
}
}).catch(err => {
this.$message.error(err)
})
},
/**
* 关闭拍照
* @time 2022-05-10 16:21:38
*/
closePhoto() {
deviceService.closeVideo().then(() => {
console.log('拍照关闭成功')
}).catch(err => {
console.log(err)
})
},
/**
* 主摄像头拍照
* @time 2022-05-10 15:54:58
*/
takePhoto() {
if (!this.openStatus) {
this.$message.warning('摄像头正在加载中,请稍候')
return
}
deviceService.masterTakePhoto().then(data => {
if (data) {
const content = 'data:image/png;base64,' + data
const blob = this.base64ToBlob(content)
const blobUrl = URL.createObjectURL(blob)
this.photoList.push({
type: 'photo',
blob,
accessUrl: blobUrl
})
} else {
this.closeDialog()
}
}).catch(err => {
this.$message.error(err)
})
},
/**
* 关闭弹窗
* @time 2022-05-10 16:21:17
*/
closeDialog() {
this.$emit('update:show', false)
},
/**
* 下一步
* @time 2022-05-10 16:09:45
*/
onNextStep() {
this.loading = true
this.stepIndex = 1
this.closePhoto()
this.initSignCanvas()
if (this.signList.length === 0) {
this.openSign()
}
},
/**
* 上一步
* @time 2022-05-10 16:09:36
*/
onPrevStep() {
this.stepIndex = 0
this.startSign = false
this.signList = []
this.initSignCanvas()
this.closeSign()
this.$nextTick(() => {
this.openPhoto()
})
},
/**
* 初始化签名画布
* @time 2022-05-10 16:00:49
*/
initSignCanvas() {
this.loading = false
this.ctxSign = this.$refs.signCanvas.getContext('2d')
this.ctxSign.fillStyle = '#f9f9f9'
this.ctxSign.fillRect(0, 0, this.iWidth, this.iHeight)
this.ctxSign.strokeStyle = '#000' // 线条颜色
this.ctxSign.lineWidth = 3
this.ctxSign.lineCap = 'round' // 线条末端添加圆形线帽,减少线条的生硬感
this.ctxSign.lineJoin = 'round' // 线条交汇时为原型边角
// 利用阴影,消除锯齿
this.ctxSign.shadowBlur = 1 // 线条阴影大小
this.ctxSign.shadowColor = '#000' // 线条阴影颜色
},
/**
* 页面点击重签
* @time 2022-05-10 16:49:47
*/
againSign() {
if (this.signList.length > 0) {
this.signList = []
this.startSign = true
this.initSignCanvas()
this.openSign()
return
}
if (!this.startSign) {
return
}
this.againSignCallback()
},
/**
* 外部设备重签
* @time 2022-05-10 16:09:57
*/
againSignCallback() {
this.startSign = false
this.initSignCanvas()
},
/**
* 关闭签字
* @time 2022-05-10 16:10:07
*/
closeSign() {
deviceService.closeSign().then(data => {
console.log('关闭签字成功')
}).catch(err => {
console.log(err)
})
},
/**
* 开始签名
* @time 2022-05-10 16:10:39
*/
async openSign() {
// 请求接口
const agreements = await queryPotocolManage({ orgId: this.$store.getters.organId })
// 源url
const originUrlLink = agreements[this.signKey + 'Vertical']
// 源文件路径
const originUrl = originUrlLink ? originUrlLink.fileUrl : ''
// 协议图片
const agreementImage = new Image()
// 协议图片加载完成
agreementImage.onload = async() => {
// 创建canvas
const cvs = document.createElement('canvas')
// 获取绘图上下文
const ctx = cvs.getContext('2d')
// 创建canvas的大小
cvs.width = agreementImage.width
cvs.height = agreementImage.height
// 开始绘制图片
ctx.drawImage(agreementImage, 0, 0, agreementImage.width, agreementImage.height)
// 获取base64地址
const base64 = cvs.toDataURL('image/jpeg', 0.8)
// 创建pdf对象
const pdf = new JsPdf('', 'pt', 'a4')
// 开始添加图片
pdf.addImage(base64, 'JPEG', 0, 0, 595.28, (595.28 / agreementImage.width) * agreementImage.height)
// pdf的base64编码
const pdfBase64 = pdf.output('dataurlstring')
// 开始预览签名
const respData = await wzh.previewSignT(pdfBase64.slice(pdfBase64.indexOf('base64,') + 7))
// 将签名图片合
this.signList = [
{ accessUrl: `data:image/png;base64,${respData.signImage}`, agreement: originUrl }
]
}
// 添加跨域
agreementImage.setAttribute('crossOrigin', 'anonymous')
// 开始加载图片
agreementImage.src = originUrl
},
/**
* 将签名照片和协议照片组合在一起
*/
async handleComposeSign() {
// 确定按钮展示loading
this.showLoading = true
try {
// 将html抓换成canvas
const canvas = await html2canvas(document.querySelector('.compose-area'), { useCORS: true })
// 导出图片文件
canvas.toBlob(async blob => {
try {
// 将生成的图片传递给父组件
const file = await this.uploadPhoto(blob)
// 关闭弹框
this.closeDialog()
// 开始通知父组件
this.$emit('getPhoto', [file])
} catch (error) {
console.error(error)
}
// 关闭loading状态
this.showLoading = false
})
} catch (error) {
console.error(error)
this.showLoading = false
}
},
/**
* 签名获取坐标
* @time 2022-05-10 16:10:47
*/
getCoordinateCallback(res) {
if (res.err_code === 0) {
const signStatus = res.data.status
const xInit = res.data.x
const yInit = res.data.y
const xSign = this.iWidth * xInit / this.deviceWidth
const ySign = this.iHeight * yInit / this.deviceHeight
switch (signStatus) {
case 161:
this.startSign = true
if (this.isFirst) {
this.points.push({ xSign: xSign, ySign: ySign })
if (this.points.length > 3) {
const lastTwoPoints = this.points.slice(-2)
const controlPoint = lastTwoPoints[0]
const endPoint = {
xSign: (lastTwoPoints[0].xSign + lastTwoPoints[1].xSign) / 2,
ySign: (lastTwoPoints[0].ySign + lastTwoPoints[1].ySign) / 2
}
this.drawSign(this.beginPoint, controlPoint, endPoint)
this.beginPoint = endPoint
}
} else {
// 开始绘制
this.isFirst = true
this.points.push({ xSign: xSign, ySign: ySign })
this.beginPoint = { xSign, ySign }
}
break
case 128:
this.points.push({ xSign: xSign, ySign: ySign })
if (this.points.length > 3) {
var lastTwoPoints = this.points.slice(-2)
var controlPoint = lastTwoPoints[0]
var endPoint = lastTwoPoints[1]
this.drawSign(this.beginPoint, controlPoint, endPoint)
}
this.isFirst = false
this.beginPoint = null
this.points = []
// 结束绘制
break
}
} else {
this.closeDialog()
}
},
/**
* 签名方法
* @time 2022-05-10 16:11:01
*/
drawSign(beginPoint, controlPoint, endPoint) {
this.ctxSign.beginPath()
this.ctxSign.moveTo(beginPoint.xSign, beginPoint.ySign)
this.ctxSign.quadraticCurveTo(controlPoint.xSign, controlPoint.ySign, endPoint.xSign, endPoint.ySign)
this.ctxSign.stroke()
this.ctxSign.closePath()
},
/**
* 下载文件
* @time 2022-05-10 15:56:04
*/
downloadFile(content) {
const fileName = 'photo_' + new Date().getTime()
const link = document.createElement('a')
const blob = this.base64ToBlob(content)
link.download = fileName
link.href = URL.createObjectURL(blob)
link.click()
},
/**
* base64转blob
* @param {str} base64code base64编码
* @time 2022-05-10 15:56:11
*/
base64ToBlob(base64code) {
const parts = base64code.split(';base64,')
const contentType = parts[0].split(':')[1]
const raw = window.atob(parts[1])
const rawLength = raw.length
const uInt8Array = new Uint8Array(rawLength)
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i)
}
return new Blob([uInt8Array], {
type: contentType
})
},
/**
* 关闭图片预览
* @time 2022-05-10 15:57:48
*/
closeViewer() {
document.body.style.overflow = this.prevOverflow
this.showViewer = false
},
/**
* 图片预览
* @param {num} index 图片索引
* @time 2022-05-10 15:57:17
*/
previewPhoto(index) {
this.imageIndex = index
this.prevOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
this.showViewer = true
},
/**
* 移除图片
* @param {num} index 图片索引
* @time 2022-05-10 15:58:17
*/
removePhoto(index) {
if (this.stepIndex === 0) {
this.photoList.splice(index, 1)
} else {
this.signList.splice(index, 1)
}
},
/**
* 上传列表
* @time 2022-05-10 18:34:04
*/
uploadList() {
this.fullscreenLoading = true
const blobList = []
const httpList = []
this.photoList.forEach(item => {
if (item.accessUrl.indexOf('blob:') === 0) {
blobList.push(item)
} else {
httpList.push(item)
}
})
this.signList.forEach(item => {
if (item.accessUrl.indexOf('blob:') === 0) {
blobList.push(item)
} else {
httpList.push(item)
}
})
if (blobList.length === 0) {
this.closeDialog()
this.$emit('getPhoto', httpList)
this.fullscreenLoading = false
return
}
const photoList = []
this.runQueue(blobList, async(item, next) => {
try {
const data = await this.uploadPhoto(item.blob)
photoList.push({
type: item.type,
uuid: data.uuid,
accessUrl: data.accessUrl
})
next()
} catch (e) {
this.closeDialog()
this.$emit('getPhoto', [...httpList])
this.fullscreenLoading = false
console.log(e)
}
}, () => {
this.closeDialog()
this.$emit('getPhoto', [...httpList, ...photoList])
this.fullscreenLoading = false
})
},
/**
* promise阵列
* @time 2022-05-11 23:03:00
*/
runQueue(queue, fn, cb) {
const step = function(index) {
if (index >= queue.length) {
cb()
} else {
if (queue[index]) {
fn(queue[index], function() {
step(index + 1)
})
} else {
step(index + 1)
}
}
}
step(0)
},
/**
* 上传图片
* @param {str} blob 图片blob地址
* @time 2022-05-10 15:58:45
*/
uploadPhoto(blob) {
return new Promise((resolve, reject) => {
const data = new FormData()
const file = new File([blob], `${new Date().getTime()}.png`, { type: 'image/png' })
const opts = { ...this.uploadConf }
data.append('file', file)
data.append('path', opts.data.path)
data.append('rootPath', opts.data.rootPath)
opts.data = data
return request(opts).then(async data => {
// 如果当前需要加密
if (this.security) {
data.accessUrl = await imageViewer(data.uuid)
}
resolve(data)
}).catch(err => {
reject(err)
})
})
}
}
}
</script>
<style lang="scss" scoped>
.device-top {
margin-bottom: 10px;
display: flex;
justify-content: space-between;
}
.btn-camera,
.btn-camera:hover {
color: #2A68FF;
border: 1px solid #B9CFFF !important;
padding-left: 30px;
background:#F5F7FF url(./components/photoStep/icon_camera.svg) no-repeat 10px center / 16px 17px;
}
.btn-clear,
.btn-clear:hover {
color: #2A68FF;
border: 1px solid #B9CFFF !important;
padding-left: 30px;
background:#F5F7FF url(./components/photoStep/icon_clear.svg) no-repeat 10px center / 16px 16px;
}
.take-photo {
height: 400px;
.canvas-phone,
.canvas-sign {
width: 100%;
height: 100%;
display: block;
border: 1px dashed #ddd;
border-radius: 4px;
overflow: auto;
.compose-area {
width: 600px;
position: relative;
margin: 0 auto;
.agreement-image {
width: 100%;
height: auto;
}
.sign-image {
display: block;
width: auto;
height: 80px;
margin: -80px 0 0 52%;
}
}
.tip {
color: #666666;
font-size: 24px;
text-align: center;
line-height: 380px;
}
}
}
.preview-list {
display: flex;
.preview-item {
position: relative;
margin: 0 8px 10px 0;
height: 68px;
width: 68px;
border-radius: 3px;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
flex: none;
background: #f2f2f2;
&:hover {
.el-icon-delete {
opacity: 1;
}
}
.el-icon-delete {
width: 68px;
height: 20px;
line-height: 20px;
background: rgba(0, 0, 0, 0.5);
color: #ccc;
text-align: center;
cursor: pointer;
position: absolute;
bottom: 0;
left: 0;
font-size: 12px;
opacity: 0;
transition: opacity .2s ease;
}
}
img {
width: 100%;
position: relative;
cursor: pointer;
&:last-child {
margin-right: 0;
}
}
}
.dialog-footer {
text-align: right;
}
</style>
const baseUrl = 'http://127.0.0.1:6045'
/**
* 列出所有读卡设备
*/
export function listCard() {
return new Promise((resolve, reject) => {
window.$.ajax({
contentType: 'application/json;charset=UTF-8',
dataType: 'jsonp',
timeout: 10000,
jsonp: 'callback',
url: `${baseUrl}/getCardReaderList`,
success: function(resultInfo, status) {
// 取出错误信息
const error = resultInfo.filter(r => r.error)
// 如果设备报错,且全都是错误信息,则取出第一个报错信息
if (error.length > 0 && error.length === resultInfo.length) {
return reject(error.map(e => e.error).join(','))
}
resolve(resultInfo)
},
error: function(jqXHR, textStatus, errorThrown) {
reject(errorThrown)
}
})
})
}
export function readCard(readerIndex = 0) {
const param = '{"portType": 5}'
return new Promise((resolve, reject) => {
window.$.ajax({
contentType: 'application/json;charset=UTF-8',
dataType: 'jsonp',
timeout: 10000,
jsonp: 'callback',
url: baseUrl + '/readCard?param=' + param + '&index=' + readerIndex,
success(resultInfo, status) {
// 如果接口返回正确
if (resultInfo && resultInfo.resultFlag === 0) {
// 如果性别是中文,则转换成数值类型
resultInfo.resultContent.gender = resultInfo.resultContent.gender === '' ? '1' : '2'
resolve(resultInfo.resultContent)
} else {
reject(resultInfo ? resultInfo.errMsg : '设备驱动失败,请重试')
}
},
error(jqXHR, textStatus, errorThrown) {
reject(errorThrown)
}
})
})
}
export function readCardNumber(readerIndex = 1) {
return new Promise((resolve, reject) => {
window.$.ajax({
contentType: 'application/json;charset=UTF-8',
dataType: 'jsonp',
timeout: 10000,
jsonp: 'callback',
url: baseUrl + '/readCardNo?index=' + readerIndex,
success(resultInfo, status) {
resolve(resultInfo)
},
error(jqXHR, textStatus, errorThrown) {
reject(errorThrown)
}
})
})
}
/* eslint-disable */
import cardutil from './cardWebsocket'
var readCert = {
// 一体式读证
rcOpenCertDevice: function(callback) {
if (cardutil.certWsStatus) {
cardutil.wsReadIntegratedCard(function(openCallback) {
console.log('openCallback', openCallback)
callback(openCallback)
})
} else {
readCert.rcReconnect('rcOpenCertDevice', function(resp) {
callback(resp)
})
}
},
// 分离式获取秘钥
rcSetAppParamEx: function(appKey, appSecret, password, rcSetAppParamExCallback) {
if (cardutil.certWsStatus) {
console.log(appKey, appSecret, password)
cardutil.setAppParamEx(appKey, appSecret, password, function(openCallback) {
console.log('openCallback', openCallback)
rcSetAppParamExCallback(openCallback)
})
} else {
cardutil.startWebSocket(function(res) {
console.log('读证读卡ws链接状态1111', res)
if (res) {
cardutil.certWsStatus = true
readCert.rcSetAppParamEx(appKey, appSecret, password, rcSetAppParamExCallback)
} else {
cardutil.certWsStatus = false
var retcode = {
err_code: -1,
err_msg: 'ws连接失败,查看服务是否启动!!!',
data: ''
}
rcSetAppParamExCallback(retcode)
}
})
}
},
// 分离式读证
rcReadCardEx: function(rcReadCardExCallback) {
if (cardutil.certWsStatus) {
cardutil.readCardEx(function(openCallback) {
console.log('openCallback', openCallback)
rcReadCardExCallback(openCallback)
})
} else {
readCert.rcReconnect('rcReadCardEx', function(resp) {
callback(resp)
})
}
},
rcReconnect: function(params, rcReconnectCallback) {
cardutil.startWebSocket(function(res) {
console.log('读证读卡ws链接状态1111', res)
if (res) {
cardutil.certWsStatus = true
if (params == 'rcOpenCertDevice') {
readCert.rcOpenCertDevice(rcReconnectCallback)
}
if (params == 'rcReadCardEx') {
readCert.rcReadCardEx(rcReconnectCallback)
}
} else {
cardutil.certWsStatus = false
var retcode = {
err_code: -1,
err_msg: 'ws连接失败,查看服务是否启动!!!',
data: ''
}
rcReconnectCallback(retcode)
}
})
}
}
export default readCert
import readCert from './readCert'
import readCard from './card'
import takePhoto from './takePhoto'
import sign from './sign'
import * as ktService from './ktService'
const service = {
// 一体式读证
readIntegratedCard() {
return new Promise((resolve, reject) => {
readCert.rcOpenCertDevice((json) => {
if (json.err_code === 0) {
const data = JSON.parse(json.data) || {}
resolve({
name: data.name,
address: data.address,
birthday: `${data.birthday.substr(0, 4)}-${data.birthday.substr(4, 2)}-${data.birthday.substr(6)}`,
effectDate: `${data.effectDate.substr(0, 4)}-${data.effectDate.substr(4, 2)}-${data.effectDate.substr(6)}`,
expireDate: data.expireDate.length === 8 ? `${data.expireDate.substr(0, 4)}-${data.expireDate.substr(4, 2)}-${data.expireDate.substr(6)}` : data.expireDate,
gender: data.gender === '' ? '1' : '2',
idNum: data.idNum,
issueOrg: data.issueOrg,
nation: data.nation
})
} else if (json.err_code === -12104) {
reject('请检查读证设备上是否存在居民身份证', json.err_code)
} else {
reject(json.err_msg, json.err_code)
}
})
})
},
// 分离式读证秘钥
setAppParamEx(appKey, appSecret, password) {
return new Promise((resolve, reject) => {
readCert.rcSetAppParamEx(appKey, appSecret, password, (json) => {
if (json.err_code === 0 || json.resultFlag === 0) {
resolve(json.data)
} else {
reject(json.err_msg, json.err_code)
}
})
})
},
// 分离式读证
readCardEx() {
return new Promise((resolve, reject) => {
readCert.rcReadCardEx(function(json) {
if (json.err_code === 0) {
resolve(json.data)
} else {
reject(json.err_msg, json.err_code)
}
})
})
},
// 获取设备
listCard() {
return new Promise((resolve, reject) => {
readCard.rdListCard(function(json) {
if (json.err_code === 0) {
resolve(json.data)
} else {
reject(json.err_msg, json.err_code)
}
})
})
},
/**
* 获取卡尔的读卡设备
*/
async listKtCard() {
try {
return await ktService.listCard()
} catch (error) {
console.error(error)
return []
}
},
/**
* 读取卡尔的身份证
*/
async readKtCardEx() {
const info = await ktService.readCard()
return info
},
// 连接卡
connectCard() {
return new Promise((resolve, reject) => {
let isSuccess = false
readCard.rdConnectCard((cert) => {
if (cert.err_code !== -290 && isSuccess !== true) {
isSuccess = true
resolve(cert)
} else {
readCard.rdListCard((cert) => {
resolve(cert)
readCard.rdConnectCard((cert) => {
resolve(cert)
})
})
}
})
})
},
// 发送APDU
transmitCard(Apdu, CardName) {
return new Promise((resolve, reject) => {
readCard.rdTransmitCard(Apdu, CardName, (json) => {
if (json.err_code === 0) {
resolve(json.data)
} else {
reject(json.err_msg, json.err_code)
}
})
})
},
// 断开卡
disconnectCard() {
return new Promise((resolve, reject) => {
readCard.rdDisconnectCard((json) => {
if (json.err_code === 0) {
resolve(json.data)
} else {
reject(json.err_msg, json.err_code)
}
})
})
},
// 获取视频设备信息
getVideoInfo() {
return new Promise((resolve, reject) => {
takePhoto.tpGetVideoInfo((json) => {
if (json.err_code === 0) {
resolve(json.data)
} else {
resolve([])
}
})
})
},
// 打开视频
masterOpenVideo(videoParams, cutStatus, callback) {
takePhoto.tpMasterOpenVideo(videoParams, cutStatus, function(json) {
callback(json)
})
},
// 关闭视频
closeVideo() {
return new Promise((resolve, reject) => {
takePhoto.tpCloseVideo((json) => {
if (json.err_code === 0) {
resolve(json.err_msg)
} else {
reject(json.err_msg, json.err_code)
}
})
})
},
// 主头拍照
masterTakePhoto() {
return new Promise((resolve, reject) => {
takePhoto.tpMasterTakePhoto((json) => {
if (json.err_code === 0) {
resolve(json.data)
} else {
reject(json.err_msg, json.err_code)
}
})
})
},
// 副头拍照
slaveTakePhoto() {
return new Promise((resolve, reject) => {
takePhoto.tpslaveTakePhoto((json) => {
if (json.err_code === 0) {
resolve(json.data)
} else {
reject(json.err_msg, json.err_code)
}
})
})
},
// 照片旋转角度
roteAngle(angleValue) {
takePhoto.tpRoteAngle(angleValue)
},
// 签字获取设备在线状态
getSignStatus() {
return new Promise((resolve, reject) => {
sign.sGetSignStatus((json) => {
if (json.err_code === 0) {
resolve(json.data)
} else {
reject(json.err_msg, json.err_code)
}
})
})
},
// 打开签字
signOpen(callback) {
sign.sSignOpen(function(cert) {
callback(cert)
})
},
// 获取坐标
getCoordinate(callback) {
sign.sGetCoordinate(function(cert) {
callback(cert)
})
},
// 签字
signFile() {
return new Promise((resolve, reject) => {
sign.sSignFile((json) => {
if (json.err_code === 0) {
resolve(json.data)
} else {
reject(json.err_msg, json.err_code)
}
})
})
},
// 重签
resetSign() {
return new Promise((resolve, reject) => {
sign.sResetSign((json) => {
if (json.err_code === 0) {
resolve(json.data)
} else {
reject(json.err_msg, json.err_code)
}
})
})
},
// 取消签名
closeSign() {
return new Promise((resolve, reject) => {
sign.sCloseSign((json) => {
if (json.err_code === 0) {
resolve(json.data)
} else {
reject(json.err_msg, json.err_code)
}
})
})
}
}
export default service
import signutil from './signWebsocket'
var sign = {
// 获取设备在线状态
sGetSignStatus: function(sGetSignStatuscallback) {
if (signutil.signWsStatus) {
signutil.getDeviceStatus(function(res) {
console.log('获取设备在线状态回调', res)
var retcode = {
err_code: res.result,
err_msg: res.message,
data: ''
}
sGetSignStatuscallback(retcode)
})
} else {
sign.sReconnect('sGetSignStatus', function(resp) {
sGetSignStatuscallback(resp)
})
}
},
// 打开设备
sSignOpen: function(ssignOpencallback) {
if (signutil.signWsStatus) {
signutil.openDevice(function(res) {
console.log('打开签名回调', res)
res.err_code = res.result
if (res.cmd === 'open_device') {
// 获取设备信息
signutil.getDeviceInfo(function(res) {
console.log('签名获取设备信息的返回', res)
var retcode = {
cmd: res.cmd,
err_code: res.result,
err_msg: res.message,
resolution: res.resolution
}
ssignOpencallback(retcode)
})
}
ssignOpencallback(res)
})
} else {
sign.sReconnect('sSignOpen', function(resp) {
ssignOpencallback(resp)
})
}
},
sGetCoordinate: function(sGoSignEventCallback) {
if (signutil.signWsStatus) {
// 连接签字通道
signutil.startWebSocketSc(function(connectSignCallback) {
console.log('连接签字通道连接状态', connectSignCallback)
},
function(res) {
var data = JSON.parse(res.data)
// console.log('签字坐标返回', data)
var recote = {
err_code: data.result,
err_msg: data.message,
data: data.resultContent
}
sGoSignEventCallback(recote)
})
} else {
sign.sReconnect('sGetCoordinate', function(resp) {
sGoSignEventCallback(resp)
})
}
},
// 确认签名获取图片
sSignFile: function(sSignFilecallback) {
if (signutil.signWsStatus) {
signutil.signConfirm(function(res) {
console.log('获取签名图片', res)
var retcode = {
err_code: res.result,
err_msg: res.message,
data: res.sign_pic_data
}
sSignFilecallback(retcode)
})
} else {
sign.sReconnect('sSignFile', function(resp) {
sSignFilecallback(resp)
})
}
},
// 重签清空画板
sResetSign: function(sResetSigncallback) {
if (signutil.signWsStatus) {
signutil.revivedSign(function(res) {
console.log('重签清空画板的回调', res)
var retcode = {
err_code: res.result,
err_msg: res.message,
data: ''
}
sResetSigncallback(retcode)
})
} else {
sign.sReconnect('sResetSign', function(resp) {
sResetSigncallback(resp)
})
}
},
// 取消签名
sCloseSign: function(sCloseSigncallback) {
if (signutil.signWsStatus) {
signutil.signCancel(function(res) {
console.log('取消签名的回调', res)
var retcode = {
err_code: res.result,
err_msg: res.message,
data: ''
}
sCloseSigncallback(retcode)
})
} else {
sign.sReconnect('sCloseSign', function(resp) {
sCloseSigncallback(resp)
})
}
},
sReconnect: function(params, sReconnectCallback) {
// 连接ws
signutil.startWebSocket(function(res) {
console.log('签名ws链接状态1111', res)
if (res) {
signutil.signWsStatus = true
if (params === 'sGetSignStatus') {
sign.sGetSignStatus(sReconnectCallback)
}
if (params === 'sSignOpen') {
sign.sSignOpen(sReconnectCallback)
}
if (params === 'sGetCoordinate') {
sign.sGetCoordinate(sReconnectCallback)
}
if (params === 'sSignFile') {
sign.sSignFile(sReconnectCallback)
}
if (params === 'sResetSign') {
sign.sResetSign(sReconnectCallback)
}
if (params === 'sCloseSign') {
sign.sCloseSign(sReconnectCallback)
}
} else {
signutil.signWsStatus = false
var retcode = {
err_code: -1,
err_msg: 'ws连接失败,查看服务是否启动!!!',
data: ''
}
sReconnectCallback(retcode)
}
})
}
}
export default sign
var clientId = '1234567890'
var deviceName = 'SR236'
var deviceType = 0
var signutil = {
signWsStatus: false,
// 主通道,用于打开设备、关闭设备等
websocket: null,
wsUrl: 'ws://127.0.0.1:32561/',
// wsUrl: 'ws://192.168.200.56:32561/',
// 用于签字坐标返回数据的通道
websocketSc: null,
wsUrlSc: 'ws://127.0.0.1:42561/',
// wsUrlSc: 'ws://192.168.200.56:42561/',
// 获取设备状态
deviceStatusParams: {
cmd: 'get_device_status',
client_id: clientId,
device_name: deviceName,
device_type: 1
},
// 打开设备
openParams: {
cmd: 'open_device',
client_id: clientId,
device_name: deviceName,
device_type: 1,
recv_msg_port: 42561,
'ui_type': 2,
'x': 0,
'y': 0,
'show_text': 0,
'time': 0
},
// 获取设备信息
deviceInfoParams: {
cmd: 'get_device_info',
client_id: clientId,
device_name: deviceName,
device_type: 1
},
// 确认签字
signConfirmParams: {
cmd: 'sign_confirm',
client_id: clientId,
device_name: deviceName,
device_type: deviceType
},
// 重签
againSignParams: {
cmd: 'revived_sign',
client_id: clientId,
device_name: deviceName,
device_type: 1
},
// 取消签字
signCancelParams: {
cmd: 'sign_cancel',
client_id: clientId,
device_name: deviceName,
device_type: 1
},
openDeviceCallback: null, // 打开设备
connectCallback: null, // 链接回调
getCoordinateCallback: null,
deviceInfoCallback: null, // 设备信息回调
signConfirmCallback: null,
revivedSignCallback: null, // 重签
getDeviceStatusCallback: null, // 获取设备状态
deviceTriggerCallback: null, //
logMessage: function(message) {
if (typeof window.onHandleMessage !== 'undefined') { window.onHandleMessage(message) } else { console.log(message) }
},
// 连接主通道的websocket
startWebSocket: function(callback) {
signutil.connectCallback = callback
if ('WebSocket' in window) {
signutil.websocket = new WebSocket(signutil.wsUrl)
} else if ('MozWebSocket' in window) {
signutil.websocket = new WebSocket(signutil.wsUrl)
} else {
window.alert('浏览器不支持WebSocket')
return
}
signutil.websocket.binaryType = 'arraybuffer'
signutil.websocket.onopen = function() {
console.log('Connected 主通道的URL: ', signutil.wsUrl)
if (signutil.websocket.readyState === 1) {
console.log('链接成功')
signutil.connectCallback(true)
}
}
signutil.websocket.onmessage = function(evt) {
signutil.wsMessage(evt)
}
signutil.websocket.onclose = function(evt) {
if (signutil.websocket.readyState === 3) {
console.log('链接关闭', evt)
signutil.connectCallback(false)
}
}
signutil.websocket.onerror = function(evt) {
if (signutil.websocket.readyState === 3) {
console.log('链接报错', evt)
signutil.connectCallback(false)
}
}
},
// 连接签字坐标通道的websocket
startWebSocketSc: function(callback1, callback2) {
const WebSocketScCallback = callback1
signutil.getCoordinateCallback = callback2
if ('WebSocket' in window) {
signutil.websocketSc = new WebSocket(signutil.wsUrlSc)
} else if ('MozWebSocket' in window) {
signutil.websocketSc = new WebSocket(signutil.wsUrlSc)
} else {
window.alert('浏览器不支持WebSocket')
return
}
signutil.websocketSc.binaryType = 'arraybuffer'
signutil.websocketSc.onopen = function() {
console.log('签字坐标URL链接成功ready')
console.log('签字坐标URL链接成功', signutil.websocketSc)
console.log('Connected 签字坐标URL: ', signutil.wsUrlSc)
if (signutil.websocketSc.readyState === 1) {
console.log('签字坐标URL链接成功')
WebSocketScCallback(true)
}
}
signutil.websocketSc.onmessage = function(evt) {
signutil.wsMessageSc(evt)
}
signutil.websocketSc.onclose = signutil.onSocketClose
signutil.websocketSc.onerror = signutil.onSocketError
},
// 发送信息
sendMsg: function(param) {
// console.log('发送信息', signutil.websocket, param)
if (signutil.websocket && param) {
signutil.websocket.send(JSON.stringify(param))
}
},
// websocket主通道的数据返回
wsMessage: function(res) {
res = JSON.parse(res.data)
// console.log('主通道的返回', res)
var cmd = res.cmd
switch (cmd) {
case 'get_device_status':
signutil.getDeviceStatusCallback(res)
break
case 'open_device':
signutil.openDeviceCallback(res)
break
case 'get_device_info':
signutil.deviceInfoCallback(res)
break
case 'sign_cancel':
if (signutil.signCancelCallback != null) {
signutil.signCancelCallback(res)
} else {
signutil.openDeviceCallback(res)
}
break
case 'revived_sign':
if (signutil.revivedSignCallback != null) {
signutil.revivedSignCallback(res)
} else {
signutil.openDeviceCallback(res)
}
break
case 'sign_pic':
if (signutil.signConfirmCallback != null) {
signutil.signConfirmCallback(res)
} else {
signutil.openDeviceCallback(res)
}
break
}
},
// 签字坐标通道的数据返回
wsMessageSc: function(res) {
// console.log('签字坐标通道的数据返回', res)
signutil.getCoordinateCallback(res)
},
// 断开检测服务器连接
cwStopWebSocket: function() {
if (signutil.websocket || signutil.websocketSc) {
if (signutil.websocket.readyState === 1 || signutil.websocketSc.readyState === 1) { signutil.websocket.close() }
signutil.websocketSc.close()
signutil.websocket = null
signutil.websocketSc = null
return true
} else {
return false
}
},
// 服务连接出错
onSocketError: function(evt) {
signutil.logMessage('连接检测服务有问题...')
},
// 服务连接关闭onSocketClose
onSocketClose: function(evt) {
// websocket = null;
signutil.websocketSc = null
signutil.logMessage('服务已断开...')
},
// 获取设备状态
getDeviceStatus: function(callback) {
signutil.getDeviceStatusCallback = callback
signutil.sendMsg(signutil.deviceStatusParams)
},
// 打开设备
openDevice: function(callback) {
signutil.openDeviceCallback = callback
signutil.sendMsg(signutil.openParams)
},
// 获取设备信息
getDeviceInfo: function(callback) {
signutil.deviceInfoCallback = callback
signutil.sendMsg(signutil.deviceInfoParams)
},
// 取消签名
signCancel: function(callback) {
signutil.signCancelCallback = callback
signutil.sendMsg(signutil.signCancelParams)
},
// 重签
revivedSign: function(callback) {
signutil.revivedSignCallback = callback
signutil.sendMsg(signutil.againSignParams)
},
// 确认签名
signConfirm: function(callback) {
signutil.signConfirmCallback = callback
signutil.sendMsg(signutil.signConfirmParams)
}
}
export default signutil
/* eslint-disable */
import videoutil from './videoWebsocket'
var takePhoto = {
videoParams: {
camera_type: 0,
camera_index: 0,
camera_name: '',
camera_path: '',
video_frame_w: 0,
video_frame_h: 0,
rotate_angle: 0
},
cameraInfo: [],
cutStatus: 0, // //0代表裁切,1代表不裁切
// 获取设备信息
tpGetVideoInfo: function(tpGetVideoInfocallback) {
if (videoutil.videoWsStatus) {
videoutil.getDeviceInfo(function(res) {
// console.log('获取设备信息返回', res)
if (res.statuCode == 0) {
takePhoto.cameraInfo = res.camera_info
// console.log('sfsdfe sd s ', takePhoto.cameraInfo)
}
var recote = {
err_code: res.statuCode,
err_msg: res.message,
data: res.camera_info
}
tpGetVideoInfocallback(recote)
})
} else {
takePhoto.tpReconnect('tpGetVideoInfo', function(resp) {
tpGetVideoInfocallback(resp)
})
}
},
tpMasterOpenVideo: function(videoParams, cutStatus, tpMasterOpenVideocallback) { // 打开视频
takePhoto.cutStatus = cutStatus
takePhoto.videoParams.camera_type = videoParams.camera_type
takePhoto.videoParams.video_frame_w = videoParams.video_frame_w
takePhoto.videoParams.video_frame_h = videoParams.video_frame_h
var cameraInfo = takePhoto.cameraInfo
for (var i = 0; i < cameraInfo.length; i++) {
if (cameraInfo[i].camera_type == videoParams.camera_type) {
var resIndex = i
takePhoto.videoParams.camera_index = cameraInfo[resIndex].device_index
takePhoto.videoParams.camera_name = cameraInfo[resIndex].camera_name
takePhoto.videoParams.camera_path = cameraInfo[resIndex].camera_path
break
}
}
console.log('打开视频videoParams', takePhoto.videoParams)
if (videoutil.videoWsStatus) {
// console.log('1111', takePhoto.videoParams)
videoutil.singleVideoCheck(takePhoto.videoParams, function(singleVideoCheckCallback) {
console.log('打开视频的回调', singleVideoCheckCallback)
if (singleVideoCheckCallback && singleVideoCheckCallback.statuCode == 0) {
videoutil.startWebSocketSc(function(startWebSocketCallback) {
console.log('34257ws连接回调', startWebSocketCallback)
if (!startWebSocketCallback) {
var recote = {
err_code: -1,
err_msg: '34257ws连接关闭',
data: ''
}
} else {
if (takePhoto.cutStatus == 0) {
// 主头开启静态边缘检测
console.log('摄像头', takePhoto.videoParams.camera_type)
if (takePhoto.videoParams.camera_type == 0) {
videoutil.startEdgeCheck(takePhoto.videoParams, function(resp) {
console.log('打开静态边缘的回调', resp)
})
}
}
}
},
function(playVideoCallback) {
// console.log('视频返回', playVideoCallback)
var recote = {
err_code: 0,
err_msg: '打开视频成功',
data: playVideoCallback.data
}
tpMasterOpenVideocallback(recote)
})
} else {
var recote = {
err_code: singleVideoCheckCallback.statuCode,
err_msg: singleVideoCheckCallback.message,
data: ''
}
tpMasterOpenVideocallback(recote)
}
})
} else {
// 连接ws
videoutil.startWebSocket(function(res) {
console.log('打开视频ws链接状态1111', res)
if (res) {
videoutil.videoWsStatus = true
takePhoto.tpMasterOpenVideo(takePhoto.videoParams, tpMasterOpenVideocallback)
} else {
videoutil.videoWsStatus = false
var retcode = {
err_code: -1,
err_msg: 'ws连接失败,查看服务是否启动!!!',
data: ''
}
tpMasterOpenVideocallback(retcode)
}
})
}
},
// 主头拍照
tpMasterTakePhoto: function(tpMasterTakePhotocallback) {
if (videoutil.videoWsStatus) {
videoutil.takeCutPic(takePhoto.videoParams, function(openCallback) {
console.log('拍照', openCallback)
var recote = {
err_code: openCallback.statuCode,
err_msg: openCallback.message,
data: ''
}
if (openCallback.statuCode == 0) {
recote.data = openCallback.pic_data_arr[0].pic_data
}
tpMasterTakePhotocallback(recote)
})
} else {
takePhoto.tpReconnect('tpMasterTakePhoto', function(resp) {
tpMasterTakePhotocallback(resp)
})
}
},
// 副头拍照
tpslaveTakePhoto: function(tpslaveTakePhotocallback) {
if (videoutil.videoWsStatus) {
videoutil.takeCutPic(takePhoto.videoParams, function(openCallback) {
console.log('拍照', openCallback)
var recote = {
err_code: openCallback.statuCode,
err_msg: openCallback.message,
data: ''
}
if (openCallback.statuCode == 0) {
recote.data = openCallback.pic_data_arr[0].pic_data
}
tpslaveTakePhotocallback(recote)
})
} else {
takePhoto.tpReconnect('tpslaveTakePhoto', function(resp) {
tpslaveTakePhotocallback(resp)
})
}
},
// 旋转角度
tpRoteAngle: function(rotate_angle) {
console.log('角度', rotate_angle)
if (rotate_angle == 90) {
rotate_angle = 1
}
if (rotate_angle == 180) {
rotate_angle = 2
}
if (rotate_angle == 270) {
rotate_angle = 3
}
takePhoto.videoParams.rotate_angle = rotate_angle
},
tpCloseVideo: function(tpCloseVideoCallback) {
if (videoutil.videoWsStatus) {
if (takePhoto.cutStatus == 0) {
videoutil.stopEdgeCheck(takePhoto.videoParams)
}
videoutil.stopVideo(takePhoto.videoParams, function(res) {
console.log('关闭视频回调', res)
if (res.statuCode == 0) {
if (videoutil.websocketSc) {
videoutil.websocketSc.close()
}
}
var recote = {
err_code: res.statuCode,
err_msg: res.message
}
tpCloseVideoCallback(recote)
})
} else {
takePhoto.tpReconnect('tpCloseVideo', function(resp) {
tpCloseVideoCallback(resp)
})
}
},
tpReconnect: function(params, tpReconnectCallback) {
// 连接ws
videoutil.startWebSocket(function(res) {
console.log('读证读卡ws链接状态1111', res)
if (res) {
videoutil.videoWsStatus = true
if (params == 'tpGetVideoInfo') {
takePhoto.tpGetVideoInfo(tpReconnectCallback)
}
// if (params == 'tpMasterOpenVideo') {
// takePhoto.tpMasterOpenVideo(tpReconnectCallback);
// }
if (params == 'tpMasterTakePhoto') {
takePhoto.tpMasterTakePhoto(tpReconnectCallback)
}
if (params == 'tpslaveTakePhoto') {
takePhoto.tpslaveTakePhoto(tpReconnectCallback)
}
if (params == 'tpCloseVideo') {
takePhoto.tpCloseVideo(tpReconnectCallback)
}
} else {
videoutil.videoWsStatus = false
var retcode = {
err_code: -1,
err_msg: 'ws连接失败,查看服务是否启动!!!',
data: ''
}
tpReconnectCallback(retcode)
}
})
}
}
export default takePhoto
<template>
<el-dialog
:visible="showDialog"
:title="title"
:close-on-press-escape="false"
:close-on-click-modal="false"
class="rnr-dialog"
width="480px"
top="10vh"
@close="closeDialog"
>
<div v-loading="loading" class="take-camera">
<!-- 提示读取的数字,或者动作 -->
<div class="random-num-wrapper">
<p class="important-tips">
录制时请{{ livenessType === LIVENESS_TYPE.ACTION ? '做出如下动作' : '用普通话大声读如下数字' }}
</p>
<p class="random-num">
<i v-if="status === VIDEO_STATUS.SUCCESS" class="el-icon-success" />
{{ livenessType === LIVENESS_TYPE.ACTION ? headAction : randomNum }}
<span v-if="status === VIDEO_STATUS.TAKING" class="countdown">{{ countdown }}</span>
</p>
</div>
<!-- 如果当前是录制前,或者录制中 -->
<video v-show="status != VIDEO_STATUS.SUCCESS" ref="video1" name="video1" autoplay="autoplay" muted />
<!-- 如果当前是录制成功,则可以预览视频 -->
<div v-show="status == VIDEO_STATUS.SUCCESS" class="record-video-wrapper">
<video ref="video2" :muted="false" name="video2" />
<i class="btn-video" :class="isPlaying ? 'el-icon-video-pause' : 'el-icon-video-play'" @click="handlePlayVideo" />
</div>
<div class="take-camera-operation">
<el-button v-if="status == VIDEO_STATUS.READY" class="btn-record" type="primary" @click.stop="handleStartRecording"><i class="imageicon-video-pause" />开始录制</el-button>
<el-button v-if="status == VIDEO_STATUS.TAKING" class="btn-record" type="primary" @click="handleStopRecording"><i class="imageicon-video-pause" />停止录制</el-button>
<el-button v-if="status == VIDEO_STATUS.SUCCESS" class="btn-record" type="primary" @click="handleStartRecording"><i class="imageicon-video-pause" />重新录制</el-button>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button size="small" @click="closeDialog">取消</el-button>
<el-button :loading="isUploading" size="small" type="primary" @click="uploadPhoto">确定</el-button>
</div>
</el-dialog>
</template>
<script>
import { postVinUpload } from '@/api/realname-enterprise'
import { getToken } from '@/utils/auth'
import { getConnectDeviceId } from '@/utils/device'
import request from '@/utils/request'
import '@/styles/dialog.scss'
const LIVENESS_TYPE = {
NUMBER: 'LIP',
ACTION: 'ACTION'
}
// 视频状态
const VIDEO_STATUS = {
// 准备状态
READY: 0,
// 录制中
TAKING: 1,
// 录制成功
SUCCESS: 2
}
export default {
name: 'DeviceVideo',
props: {
// 展示弹窗
show: {
type: Boolean,
default: false
},
// 弹窗标题
title: {
type: String,
default: '拍摄视频'
},
randomNum: {
type: String,
default: ''
},
// 验证方式
livenessType: {
type: String,
default: ''
}
},
data() {
return {
VIDEO_STATUS,
LIVENESS_TYPE,
loading: false,
showDialog: false,
recordedBlobs: [],
mediaRecorder: null,
// 初始化视频是等待录制
status: VIDEO_STATUS.READY,
// 当前文件正在上传
isUploading: false,
// 动作提示
actionTip: '',
// 是否展示动画效果
showAnimation: false,
// 是否初始化摄像头
initCameraSuccess: false,
// 动作索引
actionIndex: 0,
// 倒计时
countdown: 2,
// 是否正在播放中
isPlaying: false
}
},
computed: {
/**
* 头部动作
*/
headAction() {
// 取出所有动作
const actions = this.randomNum.split(',')
// 活体序列
const { livenessActionSeq } = this.$store.getters.dict
// 如果未开始录制
if (this.status === VIDEO_STATUS.READY) {
return `请先${actions.map(action => livenessActionSeq.find(seq => `${seq.value}` === `${action}`).label).join(',再')}`
}
// 如果当前正在录制中,则展示需要展示的动作
if (this.status === VIDEO_STATUS.TAKING) {
// 当前需要做的动作
const action = actions[this.actionIndex]
return action ? `请${livenessActionSeq.find(seq => `${seq.value}` === `${action}`).label}` : '请停止录制'
}
// 如果当前录制完成
if (this.status === VIDEO_STATUS.SUCCESS) {
return '已完成录制'
}
return ''
}
},
watch: {
show(val) {
// 将内外状态同步
this.showDialog = val
// 如果当前是展示状态,且没有初始化过摄像头,则开始初始化摄像头
if (val && !this.initCameraSuccess) {
this.initCamera()
}
}
},
destroyed() {
// 如果当前有流,则关闭流通道
if (window.stream) {
const tracks = window.stream.getTracks()
tracks.forEach(t => t.stop())
}
},
methods: {
/**
* 初始化摄像头
*/
async initCamera() {
this.loading = true
try {
// 获取设备id
const deviceId = await getConnectDeviceId()
// 将视频流保存到window下
window.stream = await navigator.mediaDevices.getUserMedia({
audio: true,
video: deviceId ? { deviceId: { exact: deviceId }} : true
})
if (window.URL) {
this.$refs.video1.srcObject = window.stream
} else {
this.$refs.video1.src = window.stream
}
// 标记当前初始化摄像头成功
this.initCameraSuccess = true
} catch (error) {
console.error('Error accessing media devices.', error)
}
this.loading = false
},
/**
* 关闭视频录制
*/
handleStopRecording() {
// 如果视频正在录制中,则关闭视频录制,并将状态置为成功
if (this.status === VIDEO_STATUS.TAKING) {
this.stopRecording()
this.status = VIDEO_STATUS.SUCCESS
}
},
/**
* 开始录制视频
*/
handleStartRecording() {
// 如果当前是准备中|已经录完需要重新录制,则将状态置为录制中
if ([VIDEO_STATUS.READY, VIDEO_STATUS.SUCCESS].includes(this.status)) {
// 开始录制
this.startRecording()
// 从第一个动作开始
this.actionIndex = 0
// 如果当前是动作系统,则开始倒计时
if (this.livenessType === LIVENESS_TYPE.ACTION) {
this.startActionCountDown()
}
// 将视频状态置为拍摄中
this.status = VIDEO_STATUS.TAKING
}
},
/**
* 录制视频
*/
startRecording() {
this.recordedBlobs = []
let options = { mimeType: 'video/webm;codecs=vp9' }
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
console.log(options.mimeType + ' is not Supported')
options = { mimeType: 'video/webm;codecs=vp8' }
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
console.log(options.mimeType + ' is not Supported')
options = { mimeType: 'video/webm' }
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
console.log(options.mimeType + ' is not Supported')
options = { mimeType: '' }
}
}
}
try {
this.mediaRecorder = new MediaRecorder(window.stream, options)
} catch (e) {
console.log('Exception while creating MediaRecorder: ' + e + '. mimeType: ' + options.mimeType)
return
}
console.log('Created MediaRecorder', this.mediaRecorder, 'with options', options)
this.mediaRecorder.onstop = event => console.log('Recorder stopped: ', event)
this.mediaRecorder.ondataavailable = this.handleDataAvailable
this.mediaRecorder.start(10)
console.log('MediaRecorder started', this.mediaRecorder)
},
/**
* 停止录制
*/
stopRecording() {
this.mediaRecorder.stop()
const buffer = new Blob(this.recordedBlobs, { type: 'video/mp4' })
this.$refs.video2.src = window.URL.createObjectURL(buffer)
console.log('Recorded Blobs: ', this.recordedBlobs)
},
handleDataAvailable(event) {
if (event.data && event.data.size > 0) {
this.recordedBlobs.push(event.data)
}
},
/**
* 播放已经录制的视频
*/
handlePlayVideo() {
// 如果当前是播放中,则停止
if (this.isPlaying) {
this.$refs.video2.pause()
} else {
this.$refs.video2.play()
// 视频播放方法
const videoPlay = () => {
// 移除监听事件
this.$refs.video2.removeEventListener('ended', videoPlay)
// 关闭播放状态
this.isPlaying = false
}
// 如果播放结束,则重置
this.$refs.video2.addEventListener('ended', videoPlay)
}
// 置反播放状态
this.isPlaying = !this.isPlaying
},
/**
* 关闭弹窗
* @time 2022-05-10 16:21:17
*/
closeDialog() {
// 如果当前正在录制中
if (this.status === VIDEO_STATUS.TAKING) {
return this.$message({
message: '视频正在录制中,请先停止录制',
type: 'warning'
})
}
// 将视频状态重置为准备状态
this.status = VIDEO_STATUS.READY
// 重置动作索引
this.actionIndex = 0
// 关闭弹框
this.$emit('update:show', false)
},
/**
* 开始呈现倒计时
*/
startActionCountDown(time = 2) {
// 如果有倒计时,清空倒计时
this.timer && clearTimeout(this.timer)
// 取出当前动作
const actions = this.randomNum.split(',')
// 如果动作执行完成,则不继续进行定时器
if (this.actionIndex >= actions.length) {
return
}
// 记录当前倒计时
this.countdown = time
// 开始倒计时
this.timer = setTimeout(() => {
// 如果当前倒计时到1s,则动作索引累加
if (time === 1) {
this.actionIndex++
}
// 继续倒计时
this.startActionCountDown(time === 1 ? 2 : 1)
}, 1000)
},
/**
* 上传视频
*/
async uploadPhoto() {
// 如果当前正在录制中
if (this.status === VIDEO_STATUS.TAKING) {
return this.$message({
message: '视频正在录制中,请先停止录制',
type: 'warning'
})
}
// 标记当前正在上传
this.isUploading = true
const data = new FormData()
const opts = {
url: postVinUpload,
headers: {
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8',
'Authorization': `bearer ${getToken()}`
},
data: {
rootPath: '',
path: ''
}
}
data.append('file', new File([new Blob(this.recordedBlobs, { type: 'video/mp4' })], `${Date.now()}.mp4`))
data.append('path', opts.data.path)
data.append('rootPath', opts.data.rootPath)
opts.data = data
try {
const respData = await request(opts)
this.$emit('upload-success', respData)
// 关闭弹框
this.closeDialog()
} catch (error) {
console.error('上传错误:', error)
}
// 标记上传结束
this.isUploading = false
}
}
}
</script>
<style lang="scss" scoped>
.take-camera {
position: relative;
video {
background-color: #000000;
display: block;
width: 432px;
height: 220px;
margin: 0 auto;
}
.record-video-wrapper {
position: relative;
.btn-video {
color: #ffffff;
font-size: 48px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
.take-camera-operation {
align-items: center;
display: flex;
justify-content: center;
margin-top: 16px;
.operation {
color: #ffffff;
cursor: pointer;
font-size: 20px;
margin-left: 30px;
&:first-child {
margin-left: 0px;
}
}
.btn-record {
height: 32px;
padding: 0 16px;
.imageicon-video-pause {
background-image: url(../../assets/image/ico_video_pause.png);
background-size: 100% 100%;
display: inline-block;
width: 16px;
height: 16px;
margin-right: 2px;
margin-bottom: -3px;
}
}
}
.random-num-wrapper {
.important-tips{
color: #71747B;
font-family: PingFangSC-Regular, PingFang SC;
font-size: 14px;
font-weight: 400;
line-height: 20px;
text-align: center;
margin: 0px;
}
.random-num {
align-items: center;
color: #212026;
display: flex;
font-size: 18px;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
line-height: 26px;
height: 26px;
margin: 4px 0 8px;
justify-content: center;
i {
color: #25c343;
font-size: 20px;
margin-right: 4px;
}
.countdown {
align-items: center;
background: #E7EBFF;
border-radius: 10px;
color: #2A68FF;
display: flex;
font-size: 14px;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
width: 20px;
height: 20px;
justify-content: center;
margin-left: 8px;
}
}
}
}
.dialog-footer {
text-align: right;
}
</style>
/* eslint-disable */
var clientId = '1234567890'
var videoutil = {
videoWsStatus: false,
// 主通道,用于打开设备、关闭设备等
websocket: null,
wsUrl: 'ws://127.0.0.1:34256/',
// wsUrl: 'ws://192.168.200.56:34256/',
connectCallback: null,
// 用于视频图片返回数据的通道
websocketSc: null,
wsUrlSc: 'ws://127.0.0.1:34257/',
// wsUrlSc: 'ws://192.168.200.56:34257/',
WebSocketScCallback: null,
// 拍照设备信息
videoDeviceParams: {
cmd: 'get_camera_info',
client_id: clientId
},
// 打开视频
singleVideoCheckParams: {
cmd: 'playvideo',
camera_type: 0,
camera_index: -1,
camera_name: '',
client_id: clientId,
camera_path: '',
video_frame_w: '',
video_frame_h: '',
recv_video_port: 34257
},
// 静态边缘检测
edgeParams: {
cmd: 'show_static_edge',
camera_type: 0,
camera_index: -1,
camera_name: '',
client_id: clientId,
statuCode: 1,
camera_path: '',
message: ''
},
// 拍照
takeCutPicParams: {
cmd: 'take_pic',
camera_type: 0,
camera_index: -1,
camera_name: '',
client_id: clientId,
camera_path: '',
rotate_angle: 0
},
// 关闭视频
stopvideoParams: {
cmd: 'stopvideo',
camera_type: 0,
camera_index: -1,
camera_name: '',
client_id: clientId,
camera_path: ''
},
deviceInfoCallback: null, // 获取设备信息
singleVideoCheckCallback: null, // 打开视频
startEdgeCheckCallback: null, // 静态边缘
takeCutPicCallback: null, // 拍照
videoConnectCallback: null, // 拍照连接回调
// 34257的链接回调
startWebSocketCallback: null,
playVideoCallback: null,
stopVideoCallback: null, // 停止拍照
logMessage: function(message) {
if (typeof window.onHandleMessage !== 'undefined') { window.onHandleMessage(message) } else { console.log(message) }
},
// 连接主通道的websocket
startWebSocket: function(callback) {
videoutil.connectCallback = callback
if ('WebSocket' in window) {
videoutil.websocket = new WebSocket(videoutil.wsUrl)
} else if ('MozWebSocket' in window) {
videoutil.websocket = new MozWebSocket(videoutil.wsUrl)
} else {
window.alert('浏览器不支持WebSocket')
return
}
videoutil.websocket.binaryType = 'arraybuffer'
videoutil.websocket.onopen = function() {
console.log('Connected 主通道的URL: ', videoutil.wsUrl)
if (videoutil.websocket.readyState == 1) {
console.log('链接成功')
videoutil.connectCallback(true)
}
}
videoutil.websocket.onmessage = function(evt) {
videoutil.wsMessage(evt)
}
videoutil.websocket.onclose = function(evt) {
if (videoutil.websocket.readyState == 3) {
console.log('链接关闭', evt)
videoutil.connectCallback(false)
}
}
videoutil.websocket.onerror = function(evt) {
if (videoutil.websocket.readyState == 3) {
console.log('链接报错', evt)
videoutil.connectCallback(false)
}
}
},
// 连接34257通道的websocket
startWebSocketSc: function(callback1, callback2) {
var WebSocketScCallback = callback1
videoutil.getCoordinateCallback = callback2
if ('WebSocket' in window) {
videoutil.websocketSc = new WebSocket(videoutil.wsUrlSc)
} else if ('MozWebSocket' in window) {
videoutil.websocketSc = new MozWebSocket(videoutil.wsUrlSc)
} else {
window.alert('浏览器不支持WebSocket')
return
}
videoutil.websocketSc.binaryType = 'arraybuffer'
videoutil.websocketSc.onopen = function() {
console.log('Connected 34257端口: ', videoutil.wsUrlSc)
if (videoutil.websocketSc.readyState == 1) {
console.log('34257端口链接成功')
WebSocketScCallback(true)
}
}
videoutil.websocketSc.onmessage = function(evt) {
videoutil.wsMessageSc(evt)
}
videoutil.websocketSc.onclose = videoutil.onSocketClose
videoutil.websocketSc.onerror = videoutil.onSocketError
},
// 发送信息
sendMsg: function(param) {
// console.log('发送信息', videoutil.websocket, param)
if (videoutil.websocket && param) {
videoutil.websocket.send(JSON.stringify(param))
}
},
// websocket主通道的数据返回
wsMessage: function(res) {
// console.log('主通道的返回', res)
var res = JSON.parse(res.data)
var cmd = res.cmd
switch (cmd) {
case 'get_camera_info': // 获取设备信息
videoutil.deviceInfoCallback(res)
break
case 'play_video_status': // 打开视频
if (videoutil.singleVideoCheckCallback) {
videoutil.singleVideoCheckCallback(res)
}
break
case 'show_static_edge': // 静态边缘
if (videoutil.startEdgeCheckCallback) {
videoutil.startEdgeCheckCallback(res)
}
break
case 'take_pic':
videoutil.takeCutPicCallback(res)
break
case 'stopvideo': // 停止拍照
videoutil.stopVideoCallback(res)
break
}
},
// 视频通道的数据返回
wsMessageSc: function(res) {
// console.log('视频通道的数据返回', res)
videoutil.getCoordinateCallback(res)
},
// 断开检测服务器连接
cwStopWebSocket: function() {
if (videoutil.websocket || videoutil.websocketSc) {
if (videoutil.websocket.readyState == 1 || videoutil.websocketSc.readyState == 1) { videoutil.websocket.close() }
videoutil.websocketSc.close()
videoutil.websocket = null
videoutil.websocketSc = null
return true
} else {
return false
}
},
// 服务连接出错
onSocketError: function(evt) {
videoutil.logMessage('连接检测服务有问题...')
},
// 服务连接关闭onSocketClose
onSocketClose: function(evt) {
// websocket = null;
videoutil.websocketSc = null
videoutil.logMessage('服务已断开...')
},
// 获取设备信息
getDeviceInfo: function(callback) {
videoutil.deviceInfoCallback = callback
videoutil.sendMsg(videoutil.videoDeviceParams)
},
// 打开视频
singleVideoCheck: function(param, callback) {
console.log('ws打开视频参数', param)
videoutil.singleVideoCheckCallback = callback
videoutil.singleVideoCheckParams.camera_type = param.camera_type
videoutil.singleVideoCheckParams.camera_index = param.camera_index
videoutil.singleVideoCheckParams.camera_name = param.camera_name
videoutil.singleVideoCheckParams.camera_path = param.camera_path
videoutil.singleVideoCheckParams.video_frame_w = parseInt(param.video_frame_w)
videoutil.singleVideoCheckParams.video_frame_h = parseInt(param.video_frame_h)
videoutil.sendMsg(videoutil.singleVideoCheckParams)
},
// 开启静态边缘检测
startEdgeCheck: function(param, callback) {
videoutil.singleVideoCheckCallback = callback
videoutil.edgeParams.camera_type = param.camera_type
videoutil.edgeParams.camera_index = param.camera_index
videoutil.edgeParams.camera_name = param.camera_name
videoutil.edgeParams.camera_path = param.camera_path
videoutil.edgeParams.statuCode = 1
videoutil.sendMsg(videoutil.edgeParams)
videoutil.startEdgeCheckCallback = callback
},
// 关闭静态边缘检测
stopEdgeCheck: function(param, callback) {
videoutil.singleVideoCheckCallback = callback
videoutil.edgeParams.camera_type = param.camera_type
videoutil.edgeParams.camera_index = param.camera_index
videoutil.edgeParams.camera_name = param.camera_name
videoutil.edgeParams.camera_path = param.camera_path
videoutil.edgeParams.statuCode = 0
videoutil.sendMsg(videoutil.edgeParams)
videoutil.startEdgeCheckCallback = callback
},
// 停止摄像头
stopVideo: function(param, callback) {
videoutil.stopVideoCallback = callback
videoutil.stopvideoParams.camera_type = param.camera_type
videoutil.stopvideoParams.camera_index = param.camera_index
videoutil.stopvideoParams.camera_name = param.camera_name
videoutil.stopvideoParams.camera_path = param.camera_path
videoutil.sendMsg(videoutil.stopvideoParams)
},
// 拍照
takeCutPic: function(param, callback) {
videoutil.takeCutPicCallback = callback
videoutil.takeCutPicParams.camera_type = param.camera_type
videoutil.takeCutPicParams.camera_index = param.camera_index
videoutil.takeCutPicParams.camera_name = param.camera_name
videoutil.takeCutPicParams.camera_path = param.camera_path
videoutil.takeCutPicParams.rotate_angle = param.rotate_angle * 1
videoutil.sendMsg(videoutil.takeCutPicParams)
}
}
export default videoutil
var wzh = {};
var address = "";
var appSecret = "30b5c231a8ea42c09c87f75d22ebc9ea"; // appId对应的加密密钥
var appId = "1035";
var nonce = "jfoiiuylkjljpohi";
var business = "{'areaCode':'020','busiSerial':'12345','channelCode':'2001','clientIP':'','deviceModel':'llllllllsdfsdffsdfjka sdfjfsdfsdfsddddddddddd','deviceSerial':'','osType':'','srcSystem':'CRM','staffCode':'110011','teminalType':'PC'}";
var host = "http://127.0.0.1:18889"
wzh.getBase64PdfT = function (filePath) {
var resultInfo;
var time = new Date();
var tes = host + "/api/getBase64file?time=" + time;
var dataInfo = "{\"filePath\":\"" + filePath + "\"}";
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
data: dataInfo,
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
}
})
return resultInfo;
}
wzh.getStatusT = function () {
var resultInfo;
var time = new Date();
var tes = host + "/api/getMutiStatus?time=" + time;
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
}
})
return resultInfo;
}
wzh.previewT = function (fileStr, filetype) {
var resultInfo;
var time = new Date();
var tes = host + "/api/preview?time=" + time
var dataInfo = "{\"fileStr\":\"" + fileStr + "\",\"filetype\":\"" + filetype + "\"}";
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
timeout: 10000, //超时时间设置,单位毫秒
data: dataInfo,
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
},
complete: function (XMLHttpRequest, status) { //请求完成后最终执行参数
if (status == 'timeout') { //超时,status还有success,error等值的情况
//ajaxTimeoutTest.abort();
wzh.closeSIGNT();
}
}
})
return resultInfo;
}
wzh.previewSyncT = function (fileStr, filetype) {
var resultInfo;
var time = new Date();
var tes = host + "/api/previewSync?time=" + time
var dataInfo = "{\"fileStr\":\"" + fileStr + "\",\"filetype\":\"" + filetype + "\"}";
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
timeout: 10000, //超时时间设置,单位毫秒
data: dataInfo,
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
},
complete: function (XMLHttpRequest, status) { //请求完成后最终执行参数
if (status == 'timeout') { //超时,status还有success,error等值的情况
//ajaxTimeoutTest.abort();
wzh.closeSIGNT();
}
}
})
return resultInfo;
}
wzh.setDialogTimeOutT = function (timeout) {
var resultInfo;
var time = new Date();
var tes = host + "/api/setDialogTimeOut?time=" + time
var dataInfo = "{\"timeout\":\"" + timeout + "\"}";
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
timeout: 10000, //超时时间设置,单位毫秒
data: dataInfo,
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
},
complete: function (XMLHttpRequest, status) { //请求完成后最终执行参数
if (status == 'timeout') { //超时,status还有success,error等值的情况
//ajaxTimeoutTest.abort();
wzh.closeSIGNT();
}
}
})
return resultInfo;
}
wzh.getCameraT = function () {
var resultInfo;
var time = new Date();
var tes = host + "/api/getCamera?time=" + time;
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
}
})
return resultInfo;
}
wzh.previewSignT = function (fileStr, filetype = 'pdf', location = '0,1,50,50,1', ifPreview = '1') {
jQuery.support.cors = true;
return new Promise((resolve, reject) => {
$.ajax({
type: "post",
async: false,
url: host + "/api/previewSign?time=" + (new Date()),
timeout: 5000, //超时时间设置,单位毫秒
data: "{\"fileStr\":\"" + fileStr + "\",\"filetype\":\"" + filetype + "\",\"location\":\"" + location + "\",\"ifPreview\":\"" + ifPreview + "\"}",
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resolve(JSON.parse(result.substring(14, result.length - 1)).resultEntity)
} else {
resolve(result)
}
},
error: function () {
reject({resultFlag: -1, errorMsg: '网络问题,请确认服务是否存在'})
},
complete: function (XMLHttpRequest, status) { //请求完成后最终执行参数
if (status == 'timeout') { //超时,status还有success,error等值的情况
//ajaxTimeoutTest.abort();
wzh.closeSIGNT();
}
}
})
});
}
wzh.signFileT = function (fileStr, filetype, location, ifPreview) {
var resultInfo;
var time = new Date();
var tes = host + "/api/signFile?time=" + time
var dataInfo = "{\"fileStr\":\"" + fileStr + "\",\"filetype\":\"" + filetype + "\",\"location\":\"" + location + "\",\"ifPreview\":\"" + ifPreview + "\"}";
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
timeout: 10000, //超时时间设置,单位毫秒
data: dataInfo,
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
},
complete: function (XMLHttpRequest, status) { //请求完成后最终执行参数
if (status == 'timeout') { //超时,status还有success,error等值的情况
//ajaxTimeoutTest.abort();
wzh.closeSIGNT();
}
}
})
return resultInfo;
}
var getParam;
wzh.signPoll = function () {
var resultInfo;
if (address == "") {
json = eval('(' + wzh.getSystemIPT() + ')');
address = json.IP;
}
var time = new Date();
var tes = host + "/api/intervalSign?time=" + time
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result == "") {
return result;
}
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
}
})
return resultInfo;
}
wzh.closeSIGNT = function () {
var resultInfo;
var time = new Date();
var tes = host + "/api/closeSIGN?time=" + time;
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
}
})
return resultInfo;
}
wzh.closeVideoT = function () {
var resultInfo;
var time = new Date();
var tes = host + "/api/closeVideo?time=" + time;
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
}
})
return resultInfo;
}
wzh.toMainScanT = function () {
var resultInfo;
var dataInfo = "{\"param\":\"1600X1200;1; ;-1;0;0;0;0;-1;-1;Main;\"}";
var time = new Date();
var tes = host + "/api/toScan?time=" + time;
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
data: dataInfo,
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
}
})
return resultInfo;
}
wzh.toSecScanT = function () {
var resultInfo;
var dataInfo = "{\"param\":\"640X480;0; ;-1;0;0;0;0;-1;-1;Sec;\"}";
var time = new Date();
var tes = host + "/api/toScan?time=" + time;
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
data: dataInfo,
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
}
})
return resultInfo;
}
wzh.getParam = function (param) {
getParam = param;
return getParam;
}
wzh.toScanPollT = function () {
var resultInfo;
var time = new Date();
var tes = host + "/api/toSurePoll?time=" + time;
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
//data:dataInfo,
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
json = eval('(' + resultInfo + ')');
if (json.errorCode != 99) {
//alert(resultInfo)
clearInterval(getParam);
}
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
}
})
return resultInfo;
}
wzh.GetICCIDT = function () {
var time = new Date();
var tes = host + "/api/getCCID?time=" + time;
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
//dataType : "jsonp",//数据类型为jsonp
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
}
})
return resultInfo;
}
wzh.keyBoardPasswordT = function (limitLen, timeOut) {
var time = new Date();
var dataInfo = "{\"limitLen\":" + limitLen + ",\"timeOut\":" + timeOut + "}";
var tes = host + "/api/keyBoardPassword?time=" + time;
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
data: dataInfo,
//dataType : "jsonp",//数据类型为jsonp
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
}
})
return resultInfo;
}
wzh.startSelectNumT = function (url, data, timeout) {
var resultInfo;
var time = new Date();
var dataInfo = {}
dataInfo.url = url
dataInfo.phone_numbers = data
dataInfo.timeout = timeout
dataInfo = JSON.stringify(dataInfo)
var tes = host + "/api/StartSelectNum?time=" + time;
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
data: dataInfo,
//dataType : "jsonp",//数据类型为jsonp
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
}
})
return resultInfo;
}
wzh.getSelectedNumT = function (callback) {
var resultInfo;
var time = new Date();
var tes = host + "/api/GetSelectedNum?time=" + time;
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
//dataType : "jsonp",//数据类型为jsonp
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
callback(resultInfo)
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
callback(resultInfo)
}
})
return resultInfo;
}
wzh.cancelSelectNumT = function () {
var time = new Date();
var tes = host + "/api/CancelSelectNum?time=" + time;
jQuery.support.cors = true;
$.ajax({
type: "post",
async: false,
url: tes,
jsonp: "jsonpCallback", //服务端用于接收callback调用的function名的参数
success: function (result) {
if (result.indexOf("jsonpCallback") != -1) {
resultInfo = result.substring(14, result.length - 1);
} else {
resultInfo = result;
}
},
error: function () {
resultInfo = "{\"resultFlag\":-1,\"errorMsg\":\"网络问题,请确认服务是否存在\"}";
}
})
return resultInfo;
}
export default wzh
\ No newline at end of file
<template>
<el-form ref="form" :rules="rules" :model="form" label-width="80px" label-position="top" size="small">
<el-row :gutter="40">
<el-col :span="24">
<el-form-item :label="label" prop="fileList">
<div class="device-wrap">
<uploader v-if="mode === 'preview'" v-model="form.fileList" list-type="picture-card" mode="preview" />
<uploader
v-else
ref="fileUpload"
v-model="form.fileList"
:data="postData"
:action="upload.action"
:before-upload="onUploadBefore"
:limit="limit"
:placeholder="placeholder"
:accept="accept"
:upload-type="uploadType"
:sign-key="signKey"
:attachment-url="attachmentUrl"
list-type="picture-card"
@on-error="onUploadError"
@on-success="onUploadSuccess"
@on-remove="onUploadRemove"
/>
<i v-if="device.showTakePhoto && mode == 'upload'" class="icon-device-video" @click="showDialog = true" />
</div>
</el-form-item>
</el-col>
</el-row>
<device-dialog :show.sync="showDialog" :data="form.fileList" :sign="sign" :attachment-url="attachmentUrl" :sign-key="signKey" :limit="limit" @getPhoto="getPhoto" />
</el-form>
</template>
<script>
import { mapGetters } from 'vuex'
import { getToken } from '@/utils/auth'
import { postVinUpload } from '@/api/realname-enterprise'
import Uploader from '@/components/Uploader'
import deviceDialog from '@/components/Device/dialog'
export default {
name: 'FileInfo',
components: {
Uploader,
deviceDialog
},
props: {
mode: {
type: String,
default: 'upload' // upload/preview
},
sign: {
type: Boolean,
default: false
},
limit: {
type: Number,
default: 5
},
label: {
type: String,
default: '文件信息'
},
placeholder: {
type: String,
default: '上传文件'
},
fileSize: {
type: Number,
default: 5
},
accept: {
type: String,
default: 'image/png,image/jpeg,image/jpg,application/pdf'
},
postData: {
type: Object,
default() {
return {
rootPath: '',
path: ''
}
}
},
data: {
type: Object,
default() {
return {}
}
},
uploadType: {
type: Array,
default: undefined
},
signKey: {
type: String,
default: ''
},
attachmentUrl: {
type: String,
default: ''
}
},
data() {
const checkFileList = (rule, value, callback) => {
if (value.length === 0) {
callback(new Error(`请上传${this.label}`))
} else {
callback()
}
}
return {
showDialog: false,
upload: {
headers: {
'Authorization': `bearer ${getToken()}`
},
action: postVinUpload
},
form: {
fileList: []
},
rules: {
fileList: [
{ required: true, validator: checkFileList, trigger: 'blur, change' }
]
}
}
},
computed: {
...mapGetters(['dict', 'device']),
previewList() {
if (this.mode === 'preview') {
return this.form.fileList.map(item => {
return item.accessUrl
})
}
return []
}
},
watch: {
data() {
this.updateInternalData()
}
},
created() {
this.init()
},
methods: {
init() {
this.updateInternalData()
},
updateInternalData() {
if (typeof this.data === 'object' && this.data !== null) {
this.form.fileList = this.data.fileList || []
} else {
this.form.fileList = []
}
},
/**
* 获取图片
* @time 2022-05-11 23:32:36
*/
getPhoto(data) {
this.form.fileList = data
},
/**
* 凭证上传校验
* @time 2022-04-11 09:16:44
*/
onUploadBefore(file) {
const accept = this.accept.split(',')
const fileSize = file.size / 1024 / 1024 < this.fileSize
if (!accept.includes(file.type)) {
this.$message.error(`${this.label}格式不对`)
return false
}
if (!fileSize) {
this.$message.error(`${this.label}大小不能超过5MB!`)
return false
}
},
/**
* 凭证上传成功
* @time 2022-04-09 17:22:21
*/
onUploadSuccess() {
this.$nextTick(() => {
this.$refs.form.validateField('fileList')
})
},
/**
* 凭证上传失败
* @time 2022-04-09 17:22:21
*/
onUploadError() {
this.$message.error('文件上传失败')
this.onUploadRemove()
},
/**
* 移除上传凭证
* @time 2022-04-09 17:22:21
*/
onUploadRemove() {
this.$nextTick(() => {
this.$refs.form.validateField('fileList')
})
},
/**
* 获取当前模块数据
* @time 2022-04-23 15:50:05
*/
async validate() {
try {
await this.$refs.form.validate()
return true
} catch (e) {
let temp = {
anchor: () => {
this.$refs.form.fields.filter(item => item.validateState === 'error')[0].$el.scrollIntoView(true)
temp = null
}
}
throw temp
}
},
/**
* 获取表单数据
* @time 2022-04-23 18:02:02
*/
getFormData() {
return this.form
},
getPendingCacheData() {
return this.form
},
/**
* 清空校验值
*/
clearValidate() {
this.$refs.form.clearValidate()
}
}
}
</script>
<template>
<div class="el-input-group">
<div v-if="$slots.prepend" class="el-input-group__prepend">
<slot name="prepend" />
</div>
<div class="el-input-group__content" :class="{ 'left-angle' : !!$slots.prepend, 'right-angle' : !!$slots.append }">
<el-select
v-if="type == 'el-select'"
v-bind="$attrs"
:value="value"
@change="handleValueChange"
>
<el-option v-for="(option, i) in options" :key="`${option.value}_${i}`" v-bind="option" />
</el-select>
<component :is="type" v-else v-bind="$attrs" :options="options" :value="value" @change="handleValueChange" @input="handleValueChange" />
</div>
<div v-if="$slots.append" class="el-input-group__append">
<slot name="append" />
</div>
</div>
</template>
<script>
export default {
name: 'FormItemWrap',
props: {
type: {
type: [Object, String],
default: null
},
options: {
type: Array,
default: () => ([])
},
value: {
type: [Object, String, Number],
default: null
}
},
methods: {
/**
* 监听值变化
* @param value 当前变化的值
*/
handleValueChange(value) {
this.$emit('input', value)
}
}
}
</script>
<style lang="scss">
.el-input-group__content {
display: table-cell;
&.left-angle .el-input__inner{
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
&.right-angle .el-input__inner {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}
</style>
<template>
<div class="form-render">
<el-form ref="formRender" :disabled="disabled" :model="formData" v-bind="bindAttrs(formConfig, 'el-form')">
<el-row :gutter="config.gutter">
<el-col v-for="(s, key) in filterUnShowSchema(schema)" :key="key" :span="s.span || 6">
<el-form-item :prop="key" v-bind="bindAttrs(s, 'el-form-item')" class="full-wrap">
<el-select
v-if="s.type == 'el-select'"
v-model="formData[key]"
v-bind="s.attrs"
:placeholder="placeholder(s)"
v-on="initBindFunction(s.on)"
@change="value => handleFormDataChange(value, key)"
>
<template v-if="formOptions[key]">
<el-option v-for="(option, i) in formOptions[key]" :key="`${option.value}_${i}`" v-bind="option" />
</template>
<template v-for="(slotName, takeSlot) in s.slotRender" v-slot:[takeSlot]>
<slot :name="slotName" />
</template>
</el-select>
<component
:is="s.type"
v-else
v-model="formData[key]"
v-bind="s.attrs"
:placeholder="placeholder(s)"
:options="formOptions[key]"
v-on="initBindFunction(s.on)"
@change="value => handleFormDataChange(value, key)"
@input="value => handleFormDataChange(value, key)"
>
<template v-for="(slotName, takeSlot) in s.slotRender" v-slot:[takeSlot]>
<slot :name="slotName" />
</template>
</component>
</el-form-item>
</el-col>
<div class="operation-wrap">
<el-form-item label-width="0">
<slot v-if="!config.hideSearch" name="slot-operation-search">
<el-button round size="small" plain type="primary" icon="iconfont icon-search" @click="handleSubmit">搜索</el-button>
</slot>
<slot v-if="!config.hideReset" name="slot-operation-reset">
<el-button round size="small" type="info" icon="iconfont icon-clear" @click="handleReset">重置</el-button>
</slot>
<slot name="slot-operation-extend" />
</el-form-item>
</div>
</el-row>
</el-form>
</div>
</template>
<script>
import mixin from './mixin'
export default {
mixins: [mixin]
}
</script>
<style lang="scss">
@import url("./index.scss");
</style>
.form-render .operation-wrap {
display: inline-block;
}
.form-render .full-wrap {
width: 100%;
}
.form-render .el-form-item--small.el-form-item {
margin-bottom: 16px;
}
.form-render .el-input--prefix .el-input__prefix {
left: 12px;
}
.form-render .el-input--prefix .el-input__inner {
padding-left: 38px;
}
.form-render .el-form--inline{
display: flex;
align-items: center;
}
.form-render .el-form--inline .el-form-item {
margin-right: 8px;
}
.form-render .mar-t-16 {
margin-top: 16px;
}
<template>
<div class="form-render">
<el-form ref="formRender" :disabled="disabled" :model="formData" v-bind="bindAttrs({ inline: true, ...formConfig }, 'el-form')">
<el-form-item v-for="(s, key) in filterUnShowSchema(schema)" :key="key" :prop="key" :class="s.firstRow ? '' : 'mar-t-16'" v-bind="bindAttrs(s, 'el-form-item')">
{{ s.firstRow }}
<!-- 下拉选择需要单独拎出来,他的选项再el-option中 -->
<el-select
v-if="formType[key] == 'el-select'"
v-model="formData[key]"
v-bind="s.attrs"
:placeholder="placeholder(s)"
v-on="initBindFunction(s.on)"
@change="value => handleFormDataChange(value, key)"
>
<template v-if="formOptions[key]">
<el-option v-for="option in formOptions[key]" :key="option.value" v-bind="option" />
</template>
<template v-for="(slotName, takeSlot) in s.slotRender" v-slot:[takeSlot]>
<slot :name="slotName" />
</template>
</el-select>
<!-- 渲染除el-select的其他任意组件,包括原生,element组件以及自定义组件 -->
<component
:is="formType[key]"
v-else
v-model="formData[key]"
v-bind="s.attrs"
:placeholder="placeholder(s)"
:options="formOptions[key]"
v-on="initBindFunction(s.on)"
@change="value => handleFormDataChange(value, key)"
@input="value => handleFormDataChange(value, key)"
>
<template v-for="(slotName, takeSlot) in s.slotRender" v-slot:[takeSlot]>
<slot :name="slotName" />
</template>
</component>
</el-form-item>
<el-form-item v-if="showOperation" label-width="0">
<slot v-if="!config.hideSearch" name="slot-operation-search">
<el-button round size="small" plain type="primary" icon="iconfont icon-search" @click="handleSubmit">搜索</el-button>
</slot>
<slot v-if="!config.hideReset" name="slot-operation-reset">
<el-button round size="small" type="info" icon="iconfont icon-clear" @click="handleReset">重置</el-button>
</slot>
<slot name="slot-operation-extend" />
</el-form-item>
</el-form>
</div>
</template>
<script>
import mixin from './mixin'
export default {
mixins: [mixin],
computed: {
showOperation() {
const slots = ['slot-operation-search', 'slot-operation-reset', 'slot-operation-extend']
const show = slots.some(name => {
return this.$slots[`${name}`]
})
return show
}
}
}
</script>
<style lang="scss">
@import url(./index.scss);
</style>
import { Form, FormItem } from 'element-ui'
import FormItemWrap from '@/components/FormItemWrap'
import { iterator } from './shared'
const ISchema = {
type: Object,
default: () => ({})
}
const IConfig = {
type: Object,
default: () => ({})
}
const IValue = {
type: Object,
default: () => ({})
}
// el-form组件props的key
const EL_FORM_PROP_KEYS = Object.keys(Form.props)
// el-form-item组件props的key
const EL_FORM_ITEM_PROP_KEYS = Object.keys(FormItem.props)
export default {
components: { FormItemWrap },
props: {
schema: ISchema,
config: IConfig,
value: IValue,
disabled: {
type: Boolean,
default: false
}
},
data() {
return {
formData: {
...(this.value || {})
},
formType: {},
formOptions: {},
formConfig: {
size: 'small',
queryAfterReset: true,
...(this.config || {})
},
lastLinkageValue: {}
}
},
mounted() {
// 先初始化default的值
this.initDefault()
// 初始化类型
this.initType()
// 再初始化options的值
this.initOptions()
},
watch: {
/**
* 监听value变化,如果value发生变化,引用地址发生变化,则覆盖formData
*/
value: {
handler() {
// 取出当前值
const { value, formData } = this
// 如果值发生变化
if (value === formData) {
return
}
// 遍历数据
Object.keys(value).forEach(key => {
// 如果当前项的值和传入的值不一致,则使用传入的值
if (formData[key] !== value[key]) {
formData[key] = value[key]
}
})
},
deep: true
}
},
methods: {
/**
* 先将default的值初始化至formData中
*/
initDefault() {
iterator(this.schema, (schema, key) => {
this.$set(this.formData, key, (this.value[key] === undefined || this.value[key] === null) ? schema.default : this.value[key])
})
// 数据开始双向绑定
this.$emit('input', this.formData)
// 触发change方法
this.$emit('change', this.formData)
},
/**
* 初始化formType类型
*/
initType() {
iterator(this.schema, (schema, key) => {
// 重新生成的类型
const schemaType = schema.type instanceof Function ? schema.type(this.formData) : schema.type
// 如果当前和上一次的类型不一致,则重新赋值
if (this.formType[key] !== schemaType) {
this.$set(this.formType, key, schemaType)
}
})
},
/**
* 初始化select等的下拉选项
*/
initOptions() {
iterator(this.schema, async(schema, key) => {
// 如果当前是数组,则直接赋值
if (schema.options instanceof Array) {
this.$set(this.formOptions, key, schema.options)
}
// 如果当前options是promise,则直接在then中获取结果
if (schema.options instanceof Promise) {
const options = await schema.options
this.$set(this.formOptions, key, options)
}
// 如果当前是方法,则根据是否有联动项,判断是否立即执行
if (schema.options instanceof Function) {
const options = await schema.options(this.formData)
this.$set(this.formOptions, key, options)
}
})
},
/**
* 初始化绑定的方法
* @param schemaOn 当前组件上需要绑定的方法
*/
initBindFunction(schemaOn) {
// 如果没有需要绑定的方法
if (!schemaOn) {
return {}
}
// 将值绑定上formData
return Object.keys(schemaOn).reduce((memo, key) => {
memo[key] = schemaOn[key].bind(this, this.formData)
return memo
}, {})
},
/**
* 绑定属性
* @param {Object} attrs 当前属性值
* @param {String} type 当前组件类型
*/
bindAttrs(attrs, type) {
// 组件有的属性
let componentAttrs = []
// 判断类型
switch (type) {
case 'el-form':
componentAttrs = EL_FORM_PROP_KEYS
break
case 'el-form-item':
componentAttrs = EL_FORM_ITEM_PROP_KEYS
break
}
return Object.keys(attrs).reduce((memo, current) => {
// 如果选择的属性,是组件的属性
if (componentAttrs.includes(current)) {
memo[current] = attrs[current]
}
return memo
}, {})
},
/**
* 过滤掉不需要显示的schema
*/
filterUnShowSchema() {
// 如果当前没有数据
if (!this.schema) {
return {}
}
// 当前需要显示的formSchema
const formSchema = {}
// 便利原始数据
iterator(this.schema, (value, key) => {
// 如果vif是方法
if (value.vif instanceof Function) {
// 如果当前vif返回结果是true,则需要直接展示
if (value.vif(this.formData)) {
formSchema[key] = value
}
} else {
formSchema[key] = value
}
})
return formSchema
},
/**
* 占位文案
* @param schema 当前schema对象
*/
placeholder(schema) {
// 如果当前设置了占位文案
if (schema.attrs && schema.attrs.placeholder) {
return schema.attrs.placeholder
}
// 如果当前时下拉或者级联组件,则展示选择,其他展示输入
return `请${['el-select', 'el-cascader'].includes(schema.type) ? '选择' : '输入'}${schema.label || ''}`
},
/**
* 表单数据发生变化:触发一次options方法
* @param value 当前发生值变化的表单的值
* @param key 当前发生值变化的表单的key
*/
handleFormDataChange(value, key) {
const { lastLinkageValue } = this
// 便利数据
iterator(this.schema, async(schema, schemaKey) => {
// 如果当前是方法,且配置了联动项,则当联动项发生变化才会触发options
if (schema.options instanceof Function && schema.linkage) {
// 如果当前改变的值是联动项,则和上一次改变的值不一样,则执行options方法
if (schema.linkage.includes(key) && value !== lastLinkageValue[key]) {
// 重新赋值,防止保留了上一次的记过
this.formData[schemaKey] = schema.default
// 将options的值初始化为空
this.$set(this.formOptions, schemaKey, [])
// 开始执行options方法
const options = await schema.options(this.formData)
// 开始绑定值,渲染页面
this.$set(this.formOptions, schemaKey, options)
}
}
})
// 记录下当前的联动项的值
lastLinkageValue[key] = value
// 开始初始化type
this.initType()
// 开始双向绑定数据
this.$emit('input', this.formData)
// 通过change方法,向上级暴露当前表单输入的值
this.$emit('change', this.formData)
// 双向绑定数据
this.$emit('input', this.formData)
},
/**
* 点击提交方法
*/
handleSubmit() {
this.$emit('submit', this.formData)
},
/**
* 点击重置方法
*/
handleReset() {
// 去掉输入内容以及校验结果
this.$refs.formRender.resetFields()
// 通知父组件
this.$emit('reset')
// 如果当前需要点击重置后发起一起请求
this.formConfig.queryAfterReset && this.handleSubmit()
},
/**
* 拿到对应key的options
* @param fieldKey 字段key
*/
getOptions(fieldKey) {
return this.formOptions[fieldKey]
},
/**
* 拿到指定field的,具体值得option
* @param fieldKey 字段key
* @param optionValue 字段的值
*/
getOption(fieldKey, optionValue) {
return this.formOptions[fieldKey].find(option => option.value === optionValue)
},
/**
* 重新加载options方法
*/
async reloadOptions(key, data) {
// 生成options的方法
const { options } = this.schema[key]
// 如果当前是方法
if (options instanceof Function) {
// 开始调用方法
const option = await options(this.formData, data)
// 开始赋值为默认值
this.formData[key] = this.schema[key].default
// 开始绑定值,渲染页面
this.$set(this.formOptions, key, option)
}
}
}
}
/**
* 对象迭代器,后期可扩展成对象数组迭代器
* @param target 需要迭代的对象
* @param callback 需要回调的方法
*/
export function iterator(target, callback) {
Object.keys(target).forEach(key => {
callback(target[key], key)
})
}
<template>
<div :class="isTablet ? 'tablet-adapter' : ''" class="app-layout">
<TopNav />
<div :class="{'has-left-menu': showMenus}" class="app-main">
<div v-if="showMenus" class="app-menu">
<LeftMenu/>
</div>
<div :class="showMenus ? 'fix-height' : ''" class="app-content">
<keep-alive :include="cachedViews">
<router-view :key="key"/>
</keep-alive>
</div>
</div>
</div>
</template>
<script>
import TopNav from '@/components/TopNav'
import LeftMenu from '@/components/LeftMenu'
export default {
name: 'Layout',
components: { TopNav, LeftMenu },
data() {
return {
isTablet: window.OS.isTablet
}
},
computed: {
cachedViews() {
return this.$store.state.stepView.cachedViews
},
key() {
return this.$route.path
},
showMenus() {
const name = this.$route.name
return ['CarAuth1', 'CarAuth2', 'DealerManagement', 'UserManagement', 'CarCardManagement', 'OrganizationManagement', 'AgreementManagement', 'SystemLog', 'CarInfoManagement', 'AuditList', 'WorkOrderRnrList'].includes(name)
}
}
}
</script>
<style lang="scss">
.app-layout {
min-height: 100%;
height: 100%;
width: 100%;
.app-main {
padding: 0;
display: flex;
min-height: calc(100% - 60px);
&.has-left-menu {
border-top: 1px solid #E5E6EC;
}
.app-menu {
position: relative;
padding-top: 12px;
max-width: 209px;
border-right: 1px solid #E5E6EC;
background: #fff;
flex: none;
}
.app-content {
flex: 1;
width: 0;
overflow: auto;
&.fix-height {
height: calc(100vh - 61px);
}
}
.el-menu {
border-right: 0 none;
.icon-menu {
width: 18px;
height: 18px;
display: inline-block;
&.basic {
background: url(../../assets/image/menu_basic.svg) no-repeat 0 0 / 18px 18px;
}
&.count {
background: url(../../assets/image/menu_count.svg) no-repeat 0 0 / 18px 18px;
}
}
}
.el-menu--collapse {
width: 60px;
.icon-menu {
width: 24px;
height: 24px;
display: inline-block;
&.basic {
background: url(../../assets/image/menu_basic.svg) no-repeat 0 0 / 24px 24px;
&:hover {
background: url(../../assets/image/menu_basic_hover.svg) no-repeat 0 0 / 24px 24px;
}
}
&.count {
background: url(../../assets/image/menu_count.svg) no-repeat 0 0 / 24px 24px;
&:hover {
background: url(../../assets/image/menu_count_hover.svg) no-repeat 0 0 / 24px 24px;
}
}
}
.el-submenu__title {
padding: 0 18px !important;
}
}
.el-submenu__title {
padding: 0 30px !important;
&:hover {
background: #fff;
color: #2A68FF;
}
}
.el-submenu__title,
.el-submenu .el-menu-item {
margin-top: 8px;
height: 40px;
line-height: 40px;
}
.el-submenu__icon-arrow {
margin-top: -5px;
}
.el-submenu .el-menu-item {
border-radius: 2px;
min-width: auto;
margin: 8px 18px 0 12px;
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
position: relative;
&.is-active,
&:hover {
background: rgba(42, 104, 255, 0.08);
color: #2A68FF;
}
&.is-active {
&:after {
content: '';
width: 3px;
height: 24px;
background: #2A68FF;
position: absolute;
top: 50%;
right: 0;
margin-top: -12px;
}
}
}
}
&.tablet-adapter {
.page-home {
.page-home-other-service {
.page-home-row {
width: 100%;
max-width: 960px;
.page-home-column-middle {
width: 33.33%;
}
.page-home-column-large {
width: 50%;
}
}
}
}
.el-dialog {
max-width: 800px;
}
.page {
.vin-input-wrapper {
width: 100%;
}
}
.page-steps, .page-module {
width: 100%;
max-width: 960px;
}
.cbt-el-radio-group {
.cbt-el-radio {
width: 100%;
max-width: 960px;
}
}
}
}
</style>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment