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
<template>
<el-select :value="value" :placeholder="placeholder" clearable @change="handleCarCompanyChange">
<el-option v-for="item in list" v-bind="item" :key="item.value" />
</el-select>
</template>
<script>
import { queryOrgByList } from '@/api/management'
export default {
name: 'CarCompany',
props: {
value: {
type: [String, Number],
default: ''
},
placeholder: {
type: String,
default: '请选择'
},
// 默认选中第一项
defaultSelectFirst: {
type: Boolean,
default: false
}
},
data() {
return {
list: []
}
},
mounted() {
this.queryOrgByList()
},
methods: {
/**
* 查询车厂列表
*/
async queryOrgByList() {
const data = await queryOrgByList()
// 如果查询出来了车企
if (data && data.length > 0) {
this.list = data.map(item => {
return {
value: item.organCode,
label: item.organName
}
})
}
// 如果当前需要默认选中第一项
this.defaultSelectFirst && this.handleCarCompanyChange(this.list[0].value)
},
/**
* 车厂切换方法
* @param v 当前方法
*/
handleCarCompanyChange(v) {
this.$emit('input', v)
this.$emit('change', v)
}
}
}
</script>
export default {
computed: {
cacheKey() {
return this.$route.name
}
},
methods: {
cacheSetStepData(key, data) {
window.sessionStorage.setItem(key, JSON.stringify(data))
},
cacheGetStepData(key) {
try {
const ret = JSON.parse(window.sessionStorage.getItem(key))
return ret
} catch (e) {
return null
}
},
cacheDelStepData(key) {
window.sessionStorage.removeItem(key)
},
isValidObject(obj) {
return typeof obj === 'object' && obj !== null
},
cloneObject(obj) {
return JSON.parse(JSON.stringify(obj))
}
}
}
<script>
import html2canvas from 'html2canvas'
export default {
name: 'CompAttachment',
props: {
vin: {
type: [String, Array],
default: null
},
iccid: {
type: Array,
default: null
},
fileId: {
type: String,
default: ''
}
},
mounted() {
this.exportAttachmentUrl()
},
methods: {
/**
* 导出图片链接
*/
async exportAttachmentUrl() {
// 导出图片
const cvs = await html2canvas(document.querySelector('.component-attachment-wrapper'))
// 导出图片
this.$emit('change', cvs.toDataURL())
}
},
render() {
return (
<div class='component-attachment-wrapper'>
<h1>*附件</h1>
{
typeof this.vin === 'string' ? (
<div>
<div class='vin-title'>VIN: {this.vin}</div>
<div class='vin-title'>ICCID: {this.iccid.join(', ')}</div>
</div>
) : (
this.fileId ? (
<div>
<div class='vin-title'>文件id: {this.fileId}</div>
<div class='vin-content'></div>
</div>
) : (
<div>
{
this.vin.map(vin => (
<div style='border-top: 1px solid #666666;'>
<p>VIN: {vin.vin}</p>
<p>ICCID: {vin.iccidList.join(', ')}</p>
</div>
))
}
</div>
)
)
}
</div>
)
}
}
</script>
<style lang="scss">
.component-attachment-wrapper {
background-color: #ffffff;
width: 750px;
height: 500px;
padding: 20px 40px;
position: fixed;
top: 0px;
left: 100vw;
h1 {
font-size: 22px;
}
.vin-title {
font-size: 20px;
margin: 16px 0 12px;
}
}
</style>
<template>
<div class="comp-basic-info">
<el-form ref="formRef" :model="form" :rules="formRules" size="small" label-position="top">
<el-row v-if="type === 'person' && showVin" :gutter="40">
<el-col :span="12">
<el-form-item label="车辆VIN码" prop="vin">
<el-input v-model="form.vin" disabled/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="40">
<el-col :span="12">
<el-form-item label="姓名" prop="name">
<el-input v-model="form.name" placeholder="请输入姓名" clearable maxlength="50"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="性别" prop="sex">
<el-select v-model="form.sex" placeholder="请选择性别" clearable>
<el-option key="1" label="男" value="1"/>
<el-option key="2" label="女" value="2"/>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</template>
<script>
export default {
name: 'CompBasicInfo',
props: {
type: {
type: String,
default: 'person' // person: 自然人, enterprise: 企业
},
data: {
type: Object,
default: () => ({})
},
showVin: {
type: Boolean,
default: true
}
},
data() {
return {
form: {
vin: '',
name: '',
sex: ''
},
formRules: {
name: [
{ required: true, message: `请输入姓名`, trigger: 'blur' }
],
sex: [
{ required: true, message: `请选择性别`, trigger: 'change' }
]
}
}
},
watch: {
data: {
handler() {
this.updateInternalData()
},
deep: true,
immediate: true
}
},
created() {
this.init()
},
methods: {
init() {
this.updateInternalData()
},
updateInternalData() {
if (typeof this.data === 'object' && this.data !== null) {
this.form.vin = this.data.vin || ''
this.form.name = this.data.name || ''
this.form.sex = this.data.sex || '1'
if (this.data.sex && this.data.name) {
this.validate()
}
} else {
this.form.vin = ''
this.form.name = ''
this.form.sex = '1'
}
},
// 校验
async validate(isErrorAnchor = false) {
try {
await this.$refs.formRef.validate()
return true
} catch (e) {
if (isErrorAnchor) {
this.$refs.formRef.fields.filter(item => item.validateState === 'error')[0].$el.scrollIntoView(true)
}
let temp = {
anchor: () => {
this.$refs.formRef.fields.filter(item => item.validateState === 'error')[0].$el.scrollIntoView(true)
temp = null
}
}
throw temp
}
},
// 获取数据
getFormData() {
return {
vin: this.form.vin,
name: this.form.name,
sex: this.form.sex
}
},
// 获取待缓存数据
getPendingCacheData() {
return this.getFormData()
},
/**
* 清空校验结果
*/
clearValidate() {
this.$refs.formRef.clearValidate()
}
}
}
</script>
<template>
<div class="comp-bus-type">
<el-form ref="formRef" :model="form" :rules="formRules" size="small" label-position="top">
<el-row>
<el-col :span="24">
<el-form-item label=" " prop="userType">
<el-radio-group v-model="form.userType" class="cbt-el-radio-group" @change="handleUserTypeChange">
<el-row :gutter="40">
<el-col :span="10">
<el-radio label="new" border class="cbt-el-radio new">
<div class="cbt-radio-block">
<div class="cbt-rb-title">新车车主</div>
<div class="cbt-rb-desc">针对未实名/已解绑的车辆</div>
</div>
</el-radio>
</el-col>
<el-col :span="10">
<el-radio label="old" border class="cbt-el-radio old">
<div class="cbt-radio-block">
<div class="cbt-rb-title">二手车新车主</div>
<div class="cbt-rb-desc">针对未解绑的车辆</div>
</div>
</el-radio>
</el-col>
</el-row>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-form-item label-width="0" prop="isDelegate">
<el-checkbox v-model="form.isDelegate">是否委托人代办</el-checkbox>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</template>
<script>
export default {
name: 'CompBusType',
props: {
data: {
type: Object,
default: () => ({})
}
},
data() {
return {
form: {
userType: 'new',
isDelegate: false
},
formRules: {
isDelegate: [
{ required: true, message: '请选择是否委托人代办', trigger: ['change', 'blur'] }
]
},
isTablet: window.OS.isTablet
}
},
watch: {
data() {
this.updateInternalData()
}
},
created() {
this.init()
},
methods: {
init() {
this.updateInternalData()
},
updateInternalData() {
if (typeof this.data === 'object' && this.data !== null) {
this.form.userType = (this.data.userType === 'new' || this.data.userType === 'old') ? this.data.userType : 'new'
this.form.isDelegate = typeof this.data.isDelegate === 'boolean' ? this.data.isDelegate : false
} else {
this.form.userType = 'new'
this.form.isDelegate = false
}
},
// 校验
async validate(isErrorAnchor = false) {
try {
await this.$refs.formRef.validate()
return true
} catch (e) {
if (isErrorAnchor) {
this.$refs.formRef.fields.filter(item => item.validateState === 'error')[0].$el.scrollIntoView(true)
}
let temp = {
anchor: () => {
this.$refs.formRef.fields.filter(item => item.validateState === 'error')[0].$el.scrollIntoView(true)
temp = null
}
}
throw temp
}
},
// 获取数据
getFormData() {
return {
userType: this.form.userType,
isDelegate: this.form.isDelegate
}
},
// 获取待缓存数据
getPendingCacheData() {
return this.getFormData()
},
/**
* 监听用户类型改变的方法
*/
handleUserTypeChange() {
this.$emit('change', this.form.userType)
}
}
}
</script>
<style lang="scss">
.comp-bus-type {
.cbt-el-radio-group {
width: 100%;
}
.cbt-el-radio {
position: relative;
box-sizing: border-box;
display: flex;
align-items: center;
width: 346px;
height: 106px;
padding: 0 24px;
border-radius: 4px;
border-color: #f0f0f0;
&.is-checked {
background: rgba(42,104,255,0.04);
}
&:hover {
border-color: #2a68ff;
}
.el-radio__inner {
width: 16px;
height: 16px;
}
.el-radio__input {
flex: 0 0 auto;
}
.el-radio__label {
padding-left: 16px;
}
.cbt-radio-block {
display: inline-flex;
flex-direction: column;
position: relative;
z-index: 1;
.cbt-rb-title {
font-size: 16px;
color: #212026;
}
.cbt-rb-desc {
margin-top: 5px;
font-size: 12px;
color: #84858a;
}
}
&:after {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
display: block;
width: 156px;
height: 100%;
background-repeat: no-repeat;
background-size: cover;
background-position: right center;
}
&.new {
&:after {
background-image: url('../../assets/image/new_car_owner@2x.png');
}
}
&.old {
&:after {
background-image: url('../../assets/image/used_car_new_owner@2x.png');
}
}
}
}
</style>
<template>
<div class="comp-car-card">
<el-form ref="formRef" :model="form" :rules="formRules" size="small" label-position="top" @submit.native.prevent>
<el-row>
<el-col :span="24">
<el-form-item label="车辆VIN码" prop="vin">
<div class="el-input-wrapper">
<el-input
:value="form.vin"
clearable
maxlength="50"
placeholder="请输入您的VIN码"
class="ccc-vin-input"
@input="onVinInput"
@focus="onVinFocus"
@blur="onVinBlur"/>
<!-- <el-button v-if="isInApp" class="ccc-add-vin-btn" type="primary" icon="el-icon-camera" plain size="small" @click.stop="handleTakeScan">扫码添加</el-button> -->
</div>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<div class="add-iccid-btn-wrapper">
<el-form-item label="ICCID列表" prop="iccidList">
<el-button :disabled="!enableAddBtn" class="ccc-add-iccid-btn" type="primary" icon="el-icon-plus" plain size="small" @click.stop="clickAddIccid">添加ICCID卡</el-button>
</el-form-item>
</div>
<div class="iccid-table-wrapper">
<el-table
v-loading="loading"
:data="iccidList"
style="width: 100%">
<el-table-column
type="index"
label="序号"
width="180"/>
<el-table-column
prop="iccid"
label="ICCID"
min-width="320"
>
<template slot-scope="scope">
<template v-if="scope.row.state === 'readonly'">{{ scope.row.iccid }}</template>
<template v-else-if="scope.row.state === 'edit'">
<el-form :ref="scope.row._formId" :model="scope.row" :rules="iccidRules" size="small" label-width="0" @submit.native.prevent>
<el-form-item prop="iccid" class="ccc-el-form-item">
<el-input :ref="scope.row._inputId" :value="scope.row.iccid" clearable maxlength="50" class="ccc-iccid-el-input" @input="onIccidInput($event, scope.row)"/>
</el-form-item>
</el-form>
</template>
</template>
</el-table-column>
<el-table-column label="操作" width="120">
<template slot-scope="scope">
<el-link v-if="canDelIccidRecord(scope)" class="ccc-el-link-del" @click.stop="clickIccidItemDel(scope)">删除</el-link>
</template>
</el-table-column>
</el-table>
</div>
</el-col>
</el-row>
</el-form>
<comp-confirm
:show.sync="showDelConfirm"
:desc="delConfirmDesc"
:show-cancel-btn="true"
@on-sure="onDelSure"
/>
<comp-confirm
:show.sync="showTipConfirm"
:desc="tipConfirmDesc"
/>
</div>
</template>
<script>
import bridge, { isInApp } from '@/utils/bridge'
import { CancelToken } from 'axios'
import { queryUnBindCardByVin, queryBindCardByVin } from '@/api/iccid'
import CompConfirm from '@/components/CompConfirm'
let gid = 0
export default {
name: 'CompCarCard',
components: {
CompConfirm
},
props: {
data: {
type: Object,
default: () => ({})
},
maxIccidLength: {
type: Number,
default: 5
},
// 特殊业务卡解绑重新封装依据vin获取iccid等信息 SPECIAL
soucePage: {
type: String,
default: () => ''
},
// 是否查询绑定的iccid true:已绑定 false:未绑定
isBind: {
type: Boolean,
default: false
}
},
data() {
const checkVin = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入您的vin码'))
} else if (!this.checkVin(value)) {
callback(new Error('vin码格式不正确'))
} else {
callback()
}
}
const checkIccid = (rule, value, callback) => {
if (value !== '') {
if (this.checkIccid(value)) {
callback()
} else {
callback(new Error('iccid格式不正确'))
}
} else {
if (this.validIccidList.length) {
callback()
} else {
callback(new Error('请输入iccid'))
}
}
}
return {
form: {
vin: '',
iccidList: []
},
formRules: {
vin: [
{ required: true, validator: checkVin, trigger: 'blur' }
],
iccidList: [
{ type: 'array', required: true, message: '请添加ICCID数据', trigger: '' }
]
},
iccidRules: {
iccid: [
{ required: true, validator: checkIccid, trigger: 'blur' }
]
},
isInApp,
iccidList: [],
loading: false,
showDelConfirm: false,
delConfirmDesc: '确认删除当前行吗?',
showTipConfirm: false,
tipConfirmDesc: '',
inputIsFocus: false
}
},
computed: {
newAddedIccidList() {
return this.iccidList.filter(item => item.state === 'edit')
},
validIccidList() {
return this.iccidList.filter(item => (item.state === 'edit' ? this.checkIccid(item.iccid) : (item.iccid !== '')))
},
enableAddBtn() {
const ret = (!this.inputIsFocus && !this.loading) || (this.inputIsFocus && !this.form.vin)
this.$emit('on-canenable', ret)
return ret
}
},
watch: {
data() {
this.updateInternalData()
},
iccidList(val) {
if (this._iccidListInitMount) {
this._iccidListInitMount = false
return
}
if (val.length) {
this.$refs.formRef.clearValidate('iccidList')
} else {
this.$refs.formRef.validateField('iccidList')
}
}
},
created() {
this._iccidListInitMount = true
this.init()
},
methods: {
/**
* 扫码功能
*/
handleTakeScan() {
bridge.scanCode(true, code => {
this.form.vin = code
this.onVinBlur()
})
},
errorTips(errMsg) {
this.showTipConfirm = true
this.tipConfirmDesc = errMsg
},
init() {
this.updateInternalData()
},
updateInternalData() {
if (typeof this.data === 'object' && this.data !== null) {
this.form.vin = this.data.vin || ''
this.form.iccidList = []
this.iccidList = Array.isArray(this.data.iccidList) ? [...this.data.iccidList] : this.getDefaultIccidList()
} else {
this.form.vin = ''
this.form.iccidList = []
this.iccidList = this.getDefaultIccidList()
}
},
getDefaultIccidList() {
return []
},
// 点击添加 ICCID 按钮
clickAddIccid() {
if (this.iccidList.length >= this.maxIccidLength) {
this.errorTips(`最多只能添加${this.maxIccidLength}条ICCID数据`)
return
}
const newIccid = this.makeIccidTemplate()
newIccid.state = 'edit'
this.iccidList.push(newIccid)
this.$nextTick(() => {
this.$refs[`${newIccid._inputId}`].focus()
})
},
// 点击删除 ICCID 记录按钮
clickIccidItemDel(scope) {
this.showDelConfirm = true
this._curDelIndex = scope.$index
},
// 确认删除 ICCID
onDelSure() {
this.iccidList.splice(this._curDelIndex, 1)
},
onIccidInput($event, cur) {
if ($event === '' || $event.length <= 20) {
cur.iccid = $event
}
},
canDelIccidRecord(scope) {
// 卡解绑查询出来的数据不能删除
if (this.$route.name === 'CardUnbindingStepOne') {
if (scope.row.state === 'readonly') {
return false
}
}
return true
},
onVinFocus() {
this.inputIsFocus = true
},
// 光标移开 vin 码输入框时的动作
onVinBlur() {
this.inputIsFocus = false
this.$refs.formRef.validateField('vin', errorMsg => {
if (!errorMsg) {
this.onVinBlurHandler()
} else {
if (this._req) {
this._req.cancel()
}
}
})
},
// 光标移开 vin 码输入框处理
async onVinBlurHandler() {
this.loading = true
const retData = await this.getIccidListByVin()
this.loading = false
if (retData === 'cancel') {
return
}
const pendingAdd = retData.iccidList || []
this.iccidList = [
...this.normalizeIccidList(pendingAdd.slice(0, this.maxIccidLength))
]
this.prevIsBind = this.isBind
this._preVin = this.form.vin
},
// vin 码输入长度限制
onVinInput($event) {
if ($event === '' || $event.length <= 17) {
this.form.vin = $event
}
},
checkVin(vin) {
return vin.length === 17
},
checkIccid(iccid) {
return typeof iccid === 'string' && iccid !== '' && iccid.length === 20
},
async getIccidListByVin() {
if (this._req) {
this._req.cancel()
}
this._req = CancelToken.source()
let retData = null
const searchParams = {
vin: this.form.vin
}
try {
// 接口请求方法
const reqFuction = this.isBind ? queryBindCardByVin : queryUnBindCardByVin
// 开始请求接口
const respData = await reqFuction({
data: {
...searchParams,
...(this.soucePage === 'BIND' ? { filter: true } : {})
},
cancelToken: this._req.token,
hideToast: true
})
retData = respData || {}
} catch (e) {
if (e.code === '_CANCEL_REQUEST_') {
retData = 'cancel'
} else {
// 其他错误需要提示
this.errorTips(e.message || '系统异常')
}
}
return retData || {}
},
// ICCID 记录数据模板
makeIccidTemplate() {
return {
iccid: '',
state: 'readonly',
_formId: gid++,
_inputId: gid++
}
},
// 标准化远程获取的 ICCID 记录数据
normalizeIccidList(list = []) {
return list.map(item => {
const newIccid = this.makeIccidTemplate()
newIccid.iccid = item
newIccid.state = 'readonly'
return newIccid
})
},
async validateForm1(isErrorAnchor = false) {
this.form.iccidList = JSON.parse(JSON.stringify(this.iccidList))
try {
await this.$refs.formRef.validate()
return true
} catch (e) {
if (isErrorAnchor) {
this.$refs.formRef.fields.filter(item => item.validateState === 'error')[0].$el.scrollIntoView(true)
}
let temp = {
anchor: () => {
this.$refs.formRef.fields.filter(item => item.validateState === 'error')[0].$el.scrollIntoView(true)
temp = null
}
}
throw temp
}
},
async validateIccidListForm2(isErrorAnchor = false) {
const validateIccidList = this.newAddedIccidList
for (let i = 0, len = validateIccidList.length; i < len; i++) {
const item = validateIccidList[i]
try {
await this.$refs[item._formId].validate()
} catch (e) {
if (isErrorAnchor) {
this.$refs[item._inputId].$el.scrollIntoView(true)
}
let temp = {
anchor: () => {
this.$refs[item._inputId].$el.scrollIntoView(true)
temp = null
}
}
throw temp
}
}
return true
},
// 校验
async validate(isErrorAnchor = false) {
try {
await Promise.all([
this.validateForm1(),
this.validateIccidListForm2()
])
return true
} catch (e) {
throw e
}
},
// 获取数据
getFormData() {
return {
vin: this.form.vin,
iccidList: this.iccidList.map(item => item.iccid)
}
},
// 获取待缓存数据
getPendingCacheData() {
return {
vin: this.form.vin,
iccidList: JSON.parse(JSON.stringify(this.iccidList))
}
}
}
}
</script>
<style lang="scss">
.comp-car-card {
.ccc-vin-input {
width: 44%;
}
.ccc-iccid-el-input {
width: 60%;
}
.ccc-el-form-item {
.el-form-item__error {
left: 62%;
top: 50%;
transform: translateY(-50%);
}
}
.add-iccid-btn-wrapper {
margin-bottom: 8px;
.ccc-add-iccid-btn {
border-color: #B9CFFF;
color: #2A68FF;
background: #F5F7FF;
&:hover {
border-color: #5489FF;
background: #F5F7FF;
color: #5489FF;
}
}
}
.iccid-table-wrapper {
.el-form-item {
margin-bottom: 0 !important;
}
.ccc-el-link-del {
font-size: 14px;
color: #2A68FF;
&:hover {
&:after {
display: none;
}
}
}
}
.el-input-wrapper {
display: flex;
.ccc-add-vin-btn {
margin-left: 12px;
}
}
}
</style>
<template>
<div class="comp-certificate-info">
<div class="device-list">
<i v-if="device.showReadCard && form.type === 'IDCARD'" class="icon-device-card" @click="getCertData"/>
</div>
<el-form ref="formRef" :model="form" :rules="formRules" size="small" label-position="top">
<el-row :gutter="40">
<el-col :span="12">
<el-form-item label="证件类型" prop="type">
<el-select v-model="form.type" placeholder="请选择证件类型" clearable filterable @change="onTypeChange">
<el-option
v-for="item in dict.certType"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<template v-if="form.type !== ''">
<el-row :gutter="40">
<el-col :span="24">
<el-form-item v-if="form.photoUrls && form.photoUrls.length > 0" label="上传证件照片" prop="photoUrls" class="cci-form-item">
<template v-if="form.type === 'IDCARD'">
<el-row :gutter="18">
<el-col :span="6" style="margin-right:40px">
<comp-certificate-upload
v-model="form.photoUrls[0].url"
:limit="1"
:certs="[form.photoUrls[0]]"
:bg-img-url="positiveBgUrl"
:face="true"
desc="请上传人像面"
@on-cert="data => formatCardInfo(data, CERT_FRONT)"
@take-photo-success="(data)=>takePhotoSuccess(data, 0)"
@input="uploadAutoValidate"
/>
</el-col>
<el-col :span="6">
<comp-certificate-upload
v-model="form.photoUrls[1].url"
:limit="1"
:certs="[form.photoUrls[1]]"
:bg-img-url="negativeBgUrl"
:face="false"
desc="请上传国徽面"
@on-cert="data => formatCardInfo(data, CERT_BACK)"
@take-photo-success="(data)=>takePhotoSuccess(data, 1)"
@input="uploadAutoValidate"
/>
</el-col>
</el-row>
</template>
<template v-else>
<el-row>
<el-col v-for="(item, index) in form.photoUrls" :span="4" :key="item.id">
<comp-certificate-upload
v-model="item.url"
:limit="maxCertImgCount"
:certs="[form.photoUrls[0]]"
:bg-img-url="positiveBgUrl"
desc="请上传证件照片"
card-type="other"
@take-photo-success="(data)=>takePhotoSuccess(data)"
@input="uploadAutoValidate"
@on-success="onUploadItemSuccess(index)"
@on-del="onUploadItemDel(index)"/>
</el-col>
</el-row>
</template>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<div class="cci-upload-tips">文件类型为图片文件,扩展名要求使用小写字母,允许支持的照片格式包括bmp、jpg/jpeg、png,小于500K</div>
</el-col>
</el-row>
</template>
<el-row :gutter="40">
<el-col :span="12">
<el-form-item label="证件号码" prop="no">
<el-input v-model="form.no" placeholder="请输入证件号码" clearable maxlength="50" @blur="() => (form.no = form.no.toUpperCase())" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="证件地址" prop="address">
<el-input v-model="form.address" placeholder="请输入证件地址" clearable maxlength="50"/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="40">
<el-col :span="12">
<el-form-item label="证件起始时间" prop="certEffectiveDate">
<!-- 日期 -->
<el-date-picker v-model="form.certEffectiveDate" :picker-options="startTimePickerOptions" :editable="false" type="date" placeholder="请选择证件有效期" clearable value-format="yyyy-MM-dd"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="通讯地址" prop="connectAddress">
<el-input v-model="form.connectAddress" placeholder="请输入通讯地址" clearable maxlength="50"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="证件有效期" prop="validDate">
<!-- 长期 -->
<el-input v-if="form.validDate && form.validDate.length === 2" v-model="form.validDate" :readonly="true" placeholder="请填写证件有效期" clearable maxlength="2" @change="handleValidDateChange" />
<!-- 日期 -->
<el-date-picker v-else v-model="form.validDate" :picker-options="pickerOptions" :editable="false" type="date" placeholder="请选择证件有效期" clearable value-format="yyyy-MM-dd"/>
<!-- 是否长期有效 -->
<el-checkbox v-model="longTerm" @change="handleChangeValidData">长期有效</el-checkbox>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</template>
<script>
import dayjs from 'dayjs'
import bridge, { isInApp } from '@/utils/bridge'
import { mapGetters } from 'vuex'
import { validId } from '@/utils/validate'
import deviceService from '@/components/Device/service'
import { FIRM } from '@/store/modules/user'
import CompCertificateUpload from '@/components/CompCertificateUpload'
import positiveBgUrl from '@/assets/image/id_positive@2x.png'
import negativeBgUrl from '@/assets/image/id_negative@2x.png'
let gid = 0
// 证件信息正面
const CERT_FRONT = 'CERT_FRONT'
// 证件信息背面
const CERT_BACK = 'CERT_BACK'
export default {
name: 'CompCertificateInfo',
components: {
CompCertificateUpload
},
props: {
data: {
type: Object,
default: () => ({})
},
maxCertImgCount: {
type: Number,
default: 2
}
},
data() {
const checkphotoUrlsForId = (rule, value, callback) => {
if (!Array.isArray(this.form.photoUrls) || this.form.photoUrls.length === 0) {
callback(new Error('请上传人像面'))
} else if (this.form.photoUrls[0].url.id === null) {
callback(new Error('请上传人像面'))
} else if (this.form.photoUrls[1].url.id === null) {
callback(new Error('请上传国徽面'))
} else {
callback()
}
}
const checkphotoUrlsForOther = (rule, value, callback) => {
if (!Array.isArray(this.form.photoUrls) || this.form.photoUrls.length === 0) {
callback(new Error('请上传证件图片'))
} else if (this.form.photoUrls[0].url.id === null) {
callback(new Error('请上传证件图片'))
} else {
callback()
}
}
const checkphotoUrls = (rule, value, callback) => {
if (this.form.type === 'IDCARD') {
checkphotoUrlsForId(rule, value, callback)
} else {
checkphotoUrlsForOther(rule, value, callback)
}
}
const checkNo = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入证件号码'))
} else if (this.form.type === 'IDCARD' && !validId(value)) {
callback(new Error('证件号码格式不正确'))
} else {
callback()
}
}
return {
CERT_FRONT,
CERT_BACK,
showDialog: false,
form: {
type: '',
photoUrls: [],
no: '',
address: '',
validDate: '',
certEffectiveDate: '',
connectAddress: ''
},
formRules: {
type: [
{ required: true, message: '请选择证件类型', trigger: 'change' }
],
photoUrls: [
{ required: true, validator: checkphotoUrls, trigger: 'change' }
],
no: [
{ required: true, validator: checkNo, trigger: 'blur' }
],
address: [
{ required: true, message: '请输入证件地址', trigger: 'blur' }
],
certEffectiveDate: [
{ required: true, message: '请选择证件起始时间', trigger: 'change' }
],
validDate: [
{ required: true, message: '请选择证件有效期', trigger: 'change' }
]
},
longTerm: false,
positiveBgUrl,
negativeBgUrl
}
},
computed: {
...mapGetters(['dict', 'device']),
/**
* 时间选择器的选项(当前天之前不让选中)
*/
pickerOptions() {
return {
disabledDate(time) {
return time.getTime() < dayjs().startOf('date').valueOf()
}
}
},
/**
* 时间选择器的选项(当前天之前不让选中)
*/
startTimePickerOptions() {
return {
disabledDate(time) {
return time.getTime() >= dayjs().endOf('date').valueOf()
}
}
}
},
watch: {
data() {
this.updateInternalData()
}
},
created() {
this.init()
},
methods: {
// 获取证件信息
getCertData() {
if (this.form.type !== 'IDCARD') {
return
}
// 如果当前在app中
if (isInApp) {
return new Promise((resolve, reject) => {
bridge.readIDCardByOTG(data => {
data.gender = data.gender === '' ? '1' : '2'
this.formatCardInfo(data)
this.validate()
resolve()
})
})
}
// 设备回调
let deviceCallback
// 如果当前是卡尔的
if (this.device.firm === FIRM.KT) {
deviceCallback = deviceService.readKtCardEx()
} else {
deviceCallback = deviceService.readIntegratedCard()
}
return deviceCallback.then(data => {
this.formatCardInfo(data)
this.validate()
}).catch(err => {
this.$message.error(err)
})
},
/**
* 格式化身份证证件信息
* @param data 读取的信息
*/
formatCardInfo(data, type = '') {
// 如果当前是证件信息背面(国徽面)
if (!type || type === CERT_BACK) {
// 过期时间
const expireDate = dayjs(data.expireDate, 'YYYYMMDD')
// 开始时间
const effectDate = dayjs(data.effectDate, 'YYYYMMDD')
// 如果当前是非长期身份证
if (expireDate.isValid()) {
if (expireDate.isBefore(dayjs().startOf('date'))) {
this.$message.error('当前身份证已过期,请注意更换')
data.expireDate = ''
} else {
data.expireDate = expireDate.format('YYYY-MM-DD')
}
}
// 设置身份证生效时间
data.certEffectiveDate = effectDate.format('YYYY-MM-DD')
this.form.validDate = data.expireDate
this.form.certEffectiveDate = data.certEffectiveDate
this.longTerm = data.expireDate === '长期'
}
// 如果当前是证件信息正面(人像面)
if (!type || type === CERT_FRONT) {
this.form.no = data.idNum
this.form.address = data.address
this.$emit('read-cert', data)
}
},
init() {
this.updateInternalData()
},
updateInternalData() {
if (typeof this.data === 'object' && this.data !== null) {
// 如果当前是长期
this.longTerm = this.form.validDate === '长期'
this.form.type = this.data.type || this.getDefaultType()
this.form.photoUrls = this.data.photoUrls || this.getDefaultPhotoUrls()
this.form.no = this.data.no || ''
this.form.address = this.data.address || ''
this.form.validDate = this.data.validDate || ''
this.form.connectAddress = this.data.connectAddress || ''
} else {
this.form.type = this.getDefaultType()
this.form.photoUrls = this.getDefaultPhotoUrls()
this.form.no = ''
this.form.address = ''
this.form.validDate = ''
this.form.connectAddress = ''
}
},
getDefaultType() {
return 'IDCARD'
},
getDefaultPhotoUrls() {
if (this.form.type === 'IDCARD') {
return [
this.makeUploadDataTemplate(),
this.makeUploadDataTemplate()
]
}
return [this.makeUploadDataTemplate()]
},
// 高拍仪拍照上传成功回调
// index=0正面, index=1反面
takePhotoSuccess(data, index) {
if (data.length <= 0) {
return
}
// 如果当前是身份证
if (typeof index === 'number') {
this.form.photoUrls.splice(index, 1, {
id: gid++,
url: {
url: data[0].accessUrl,
id: data[0].uuid
}
})
} else {
this.form.photoUrls = data.map(item => {
return {
id: gid++,
url: {
url: item.accessUrl,
id: item.uuid
}
}
})
}
},
// 上传图片变化后自动校验
uploadAutoValidate() {
this.validatePhoto()
},
// 校验图片
validatePhoto() {
if (this.$refs.formRef) {
this.$nextTick(() => {
this.$refs.formRef.validateField('photoUrls')
})
}
},
// 上传成功自动添加空上传控件
onUploadItemSuccess(index) {
if (this.form.photoUrls.length >= this.maxCertImgCount) {
return
}
// 最后一个控件上传成功后,添加新的
if (index === this.form.photoUrls.length - 1) {
this.form.photoUrls.push(this.makeUploadDataTemplate())
}
},
// 删除上传控件
onUploadItemDel(index) {
if (this.form.photoUrls.length <= 1) {
return
}
this.form.photoUrls.splice(index, 1)
this.addPendingUploadItem()
},
// 添加待上传控件
addPendingUploadItem() {
if (this.form.photoUrls[this.form.photoUrls.length - 1].url.id !== null) {
this.form.photoUrls.push(this.makeUploadDataTemplate())
}
},
// 上传控件数据模板
makeUploadDataTemplate() {
return {
id: gid++,
url: {
id: null,
url: ''
}
}
},
onTypeChange() {
this.form.photoUrls = this.getDefaultPhotoUrls()
// 如果当前选择的是户口本,将则过期时间替换成长期
this.form.validDate = this.form.type === 'HOUSEHOLD' ? '长期' : this.form.validDate
this.validatePhoto()
},
// 校验
async validate(isErrorAnchor = false) {
try {
await this.$refs.formRef.validate()
return true
} catch (e) {
if (isErrorAnchor) {
this.$refs.formRef.fields.filter(item => item.validateState === 'error')[0].$el.scrollIntoView(true)
}
let temp = {
anchor: () => {
this.$refs.formRef.fields.filter(item => item.validateState === 'error')[0].$el.scrollIntoView(true)
temp = null
}
}
throw temp
}
},
// 获取数据
getFormData() {
return {
type: this.form.type,
no: this.form.no,
address: this.form.address,
validDate: this.form.validDate,
certEffectiveDate: this.form.certEffectiveDate,
connectAddress: this.form.connectAddress,
photoUrls: this.form.photoUrls
}
},
// 获取待缓存数据
getPendingCacheData() {
return this.getFormData()
},
/**
* 当前长期选项改变时
*/
handleChangeValidData() {
this.form.validDate = this.longTerm ? '长期' : ''
},
/**
* 日期改变时
*/
handleValidDateChange() {
// 如果当前是清空操作
if (!this.form.validDate) {
this.longTerm = false
}
},
/**
* 清空校验值
*/
clearValidate() {
this.$refs.formRef.clearValidate()
}
}
}
</script>
<style lang="scss">
.comp-certificate-info {
position: relative;
.device-list {
position: absolute;
top: -50px;
right: 0;
}
.icon-device-card,
.icon-device-video {
margin-left: 16px;
}
.cci-upload-tips {
margin-bottom: 22px;
font-size: 12px;
color: rgba(132,133,138,0.7);
}
.cci-form-item {
margin-bottom: 10px;
.el-form-item__error {
top: calc(100% + 24px);
}
}
}
</style>
<template>
<div class="comp-certificate-upload-wrap">
<PadBridge>
<el-upload
ref="uploadRef"
:auto-upload="true"
:show-file-list="false"
:multiple="false"
:on-success="onSuccess"
:on-error="onUploadError"
:before-upload="onBeforeUpload"
:on-progress="onProgress"
:data="reqParams"
:headers="reqHeaders"
:action="actionUrl"
class="comp-certificate-upload"
name="file"
accept=".jpg,.jpeg,.png,.bmp"
>
<template v-if="state === 'init'">
<div class="su-icon">
<img :src="bgImgUrl">
</div>
<div class="su-text">{{ desc }}</div>
</template>
<template v-else-if="state === 'uploading'">
<div class="su-loading-text">文件上传中</div>
<div class="su-loading-bar">
<div :style="{width: uploadPercent}" class="su-loading-bar-progress" />
</div>
</template>
<template v-else-if="state === 'success'">
<img :src="fileRemoteUrl.url" alt="" class="su-img">
<div class="su-success-operate-wrapper">
<i class="el-icon-delete su-operate-btn" @click.stop.prevent="clickDel"/>
<i class="el-icon-zoom-in su-operate-btn zoom" @click.stop.prevent>
<el-image
:src="fileRemoteUrl.url"
:preview-src-list="[fileRemoteUrl.url]"/>
</i>
</div>
</template>
<template v-else-if="state === 'error'">
<div class="su-text">上传失败</div>
<div class="su-success-operate-wrapper">
<i class="el-icon-delete su-operate-btn" @click.stop.prevent="clickDel"/>
</div>
</template>
</el-upload>
</PadBridge>
<div class="device-wrapper">
<i v-if="device.showTakePhoto" class="icon-device-video" @click.stop="showDialog = true" />
</div>
<device-dialog :show.sync="showDialog" :security="true" :data="photoList" :limit="limit" @getPhoto="getPhoto" />
</div>
</template>
<script>
import PadBridge from '@/components/PadBridge'
import { mapGetters } from 'vuex'
import CompressImg from '@/components/CompressImg'
import BlobUtil from '@/components/CompressImg/blobutil'
import { getToken } from '@/utils/auth'
import { getIdcardInfoByOcr } from '@/utils/upload'
import { imageViewer } from '@/utils/upload'
import defaultBgImgUrl from '@/assets/image/id_positive@2x.png'
import deviceDialog from '@/components/Device/dialog'
export default {
name: 'CompCertificateUpload',
components: {
deviceDialog,
PadBridge
},
props: {
value: {
type: Object,
default: () => ({})
},
maxSize: {
type: Number,
default: 0.5
},
rootPath: {
type: String,
default: 'common/imgs'
},
bgImgUrl: {
type: String,
default: defaultBgImgUrl
},
desc: {
type: String,
default: '请上传人像面'
},
limit: {
type: Number,
default: 1
},
certs: {
type: Array,
default: () => ([])
}
},
data() {
return {
showDialog: false,
file: null,
fileRemoteUrl: null,
state: 'init',
uploadPercent: '0%',
actionUrl: `${process.env.VUE_APP_BASIC_API}cuscImage/upload`,
reqParams: {
rootPath: this.rootPath
},
reqHeaders: {
'Authorization': ''
}
}
},
computed: {
...mapGetters(['device']),
photoList() {
const list = []
this.certs.forEach(item => {
if (item.url.url) {
list.push({
type: 'photo',
accessUrl: item.url.url,
uuid: item.url.id
})
}
})
return list
}
},
watch: {
'fileRemoteUrl.id'(v) {
if (this._fileRemoteUrlInitMount) {
this._fileRemoteUrlInitMount = false
return
}
this.$emit('input', this.fileRemoteUrl)
},
value(v) {
if (v && v.id !== this.fileRemoteUrl.id) {
this.updateInternalData()
}
}
},
created() {
this._fileRemoteUrlInitMount = true
this.init()
},
methods: {
/**
* 获取图片
* @time 2022-05-11 23:32:36
*/
getPhoto(data) {
if (data.length > 0) {
this.$emit('take-photo-success', data)
this.state = 'success'
this.$emit('on-success')
}
},
errorTips(msg) {
this.$message.error(msg)
},
init() {
this.updateInternalData()
},
updateInternalData() {
if (typeof this.value === 'object' && this.value !== null) {
const fileRemoteUrl = this.makeFileRemoteUrlTemplate()
fileRemoteUrl.id = this.value.id || null
fileRemoteUrl.url = this.value.url || ''
if (fileRemoteUrl.id === null) {
this.resetState()
return
}
this.file = null
this.fileRemoteUrl = fileRemoteUrl
this.state = 'success'
this.uploadPercent = '0%'
} else {
this.resetState()
}
},
resetState() {
this.file = null
this.fileRemoteUrl = this.makeFileRemoteUrlTemplate()
this.state = 'init'
this.uploadPercent = '0%'
},
// 点击删除
clickDel() {
this.$emit('on-del')
this.resetState()
},
makeExceedSizeTips() {
if (this.maxSize >= 1) {
return `上传文件大小不能超过${this.maxSize}M`
}
return `上传文件大小不能超过${this.maxSize * 1000}K`
},
// 上传前
onBeforeUpload(file) {
// 检查文件格式
if (!/jpg|jpeg|png|bmp/i.test(file.type)) {
this.errorTips('上传文件格式不正确')
return false
}
if (file.size > 60 * 1024 * 1024) {
this.errorTips(this.makeExceedSizeTips())
return false
}
if (file.size > this.maxSize * 1024 * 1024) {
return new Promise((resolve, reject) => {
// 压缩
CompressImg(file, {
size: 850,
quality: 70,
cb: (dataURL) => {
const abData = BlobUtil.base64ToArrayBuffer(dataURL)
if (abData.length > this.maxSize * 1024 * 1024) {
this.errorTips(this.makeExceedSizeTips())
reject(false)
} else {
// 如果当前有正在上传的请求,取消这些请求
if (this.state === 'uploading') {
this.$refs.uploadRef.abort()
}
// 设置token
this.reqHeaders['Authorization'] = `bearer ${getToken()}`
this.state = 'uploading'
// 设置上传文件为压缩后的文件
const compressedFile = BlobUtil.Blob([abData], { type: 'image/jpeg' })
resolve(new File([compressedFile], file.name || '压缩后的文件.jpeg'))
}
}
})
})
}
// 如果当前有正在上传的请求,取消这些请求
if (this.state === 'uploading') {
this.$refs.uploadRef.abort()
}
// 设置token
this.reqHeaders['Authorization'] = `bearer ${getToken()}`
this.state = 'uploading'
return true
},
// 上传中
onProgress(ev, file) {
if (ev.lengthComputable) {
this.uploadPercent = `${(ev.loaded / ev.total) * 100}%`
}
},
// 上传成功
async onSuccess(res, file) {
this.uploadPercent = '0%'
try {
res.data.accessUrl = await imageViewer(res.data.uuid)
} catch (error) {
console.error('加密图片加载失败:', error)
}
if (res && res.success && res.data && res.data.accessUrl) {
const fileRemoteUrl = this.makeFileRemoteUrlTemplate()
fileRemoteUrl.id = res.data.uuid
fileRemoteUrl.url = res.data.accessUrl
// 上传成功
this.state = 'success'
this.fileRemoteUrl = fileRemoteUrl
this.file = file
this.$emit('on-success')
} else {
// 上传失败
this.state = 'error'
this.fileRemoteUrl = this.makeFileRemoteUrlTemplate()
this.file = null
this.$emit('on-error')
}
},
// 上传失败
onUploadError(e) {
this.uploadPercent = '0%'
this.state = 'error'
this.fileRemoteUrl = this.makeFileRemoteUrlTemplate()
this.file = null
this.$emit('on-error')
},
// 生成远程数据模板
makeFileRemoteUrlTemplate() {
return {
id: null,
url: ''
}
},
/**
* 身份证ocr识别
*/
async handleIdcardOcr() {
// 通过ocr读取证件信息
const respData = await getIdcardInfoByOcr(this.file.raw)
// 返回读取的证件信息
this.$emit('on-cert', respData)
}
}
}
</script>
<style lang="scss">
.comp-certificate-upload-wrap {
position: relative;
width: 140px;
.device-wrapper {
position: absolute;
bottom: 0;
right: -36px;
}
}
.comp-certificate-upload {
width: 140px;
.el-upload {
box-sizing: border-box;
position: relative;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 140px;
height: 96px;
border: 1px dashed #DCDFE6;
border-radius: 8px;
background: #fff;
.su-icon {
width: 86px;
height: 52px;
margin: 0 auto 7px;
> img {
display: block;
width: 100%;
height: 100%;
}
}
.su-text,
.su-loading-text {
font-size: 12px;
line-height: 17px;
color: #84858A;
text-align: center;
}
.su-img {
display: block;
max-width: 100%;
max-height: 100%;
}
.su-loading-bar {
position: relative;
width: 100%;
height: 2px;
.su-loading-bar-progress {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 0;
background: blue;
}
}
.su-success-operate-wrapper {
position: absolute;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
height: 22px;
border-radius: 0 0 8px 8px;
background: rgba(0, 0, 0, 0.5);
transition: opacity .3s ease-out;
opacity: 0;
pointer-events: none;
> .su-operate-btn {
width: 50%;
text-align: center;
color: #fff;
opacity: 0.7;
&.zoom {
position: relative;
overflow: hidden;
border-left: 1px solid #fff;
.el-image {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0;
}
}
}
}
&:hover {
.su-success-operate-wrapper {
pointer-events: auto;
opacity: 1;
}
}
}
}
</style>
<template>
<div class="comp-client-confirm">
<el-form ref="form" :model="form" label-width="80px" label-position="top" size="small">
<page-module name="确认步骤" icon="step">
<el-row :gutter="40">
<el-col :span="24">
<div class="SMS-confirm-course">
<img :src="courseImg">
</div>
</el-col>
</el-row>
</page-module>
<page-module name="信息内容" icon="content">
<div class="SMS-confirm-content">
<el-row :gutter="40">
<el-col :span="12">
<div class="SMS-confirm-content-row marginTop0">
<div class="SMS-confirm-content-title">车主手机号</div>
<div class="SMS-confirm-content-content">{{ turnHidePwd(form.mobile) }}</div>
</div>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<div class="SMS-confirm-content-row">
<div class="SMS-confirm-content-title">短信内容</div>
<div class="SMS-confirm-content-content">{{ form.vin }}</div>
</div>
</el-col>
</el-row>
</div>
</page-module>
</el-form>
<!-- 操作等待 -->
<operation-wait-dialog
:wait-visible="msgVisible"
:break-timer="breakTimer"
:prompt-info="promptInfo"
@updateWaitVisible="updateWaitVisible"/>
<!-- 成功 -->
<comp-confirm
:show.sync="tipVisible"
:desc="desc"
:show-cancel-btn="false"
:type="msgType"
@on-sure="closeVisible"
/>
</div>
</template>
<script>
import { postIccidsByVin, postPollCardUnbindStatus, surplusCardResult, getUnBindIccidList } from '@/api/special-business'
import CompConfirm from '@/components/CompConfirm'
import OperationWaitDialog from '@/components/OperationWaitDialog'
export default {
name: 'CompClientConfirm',
components: {
CompConfirm,
OperationWaitDialog
},
props: {
formData: {
type: Object,
default: () => {}
},
waitVisible: {
type: Boolean,
default: () => false
},
type: {
type: String,
default: () => ''
},
promptInfo: {
type: String,
default: () => ''
},
opreatorUuid: {
type: String,
default: () => ''
}
},
data() {
return {
courseImg: require('@/assets/special-business/course.png'),
tboxImg: require('@/assets/special-business/tbox.png'),
bindImg: require('@/assets/special-business/bindImg.png'),
unbindImg: require('@/assets/special-business/course.png'),
form: {
mobile: '',
vin: ''
},
rnrInfo: {},
rnrId: '',
tipVisible: false,
msgType: 'info',
desc: '',
carUnbindMsg: {
3: '卡解绑成功',
4: '客户已取消操作',
9: '已超时, 如需操作请重试'
},
enterpriserUnbindMsg: {
3: '企业解绑成功',
4: '客户已取消操作',
9: '已超时, 如需操作请重试'
},
tboxMsg: {
3: '换件成功',
4: '客户已取消操作',
9: '已超时, 如需操作请重试'
},
bindMsg: {
// 2等待中 3成功 4用户取消 9超时
3: '卡绑定成功',
4: '客户已取消操作',
9: '已超时, 如需操作请重试'
},
intervals: null,
breakTimer: false,
msgVisible: false
}
},
watch: {
formData: {
handler(val, oldVal) {
if (val && val !== oldVal) {
this.displaySmsTxt()
}
},
deep: true,
immediate: true
},
waitVisible: {
handler(val, oldVal) {
if (val && val !== oldVal) {
this.msgVisible = val
// 轮询是否发送了短信
this.handleStatus()
}
}
}
},
methods: {
/**
* 展示短信内容
* @time 2022-04-24 06:49:47
*/
displaySmsTxt() {
let smsMsg = ''
const curVin = this.formData.vin instanceof Array ? this.formData.vin.map(vin => this.turnHidePwd(vin, 'vin')) : this.turnHidePwd(this.formData.vin, 'vin')
const iccidTxtArr = this.formData.iccidList ? this.formData.iccidList.map((item) => {
return this.type === 'TBOX' ? `${this.turnHidePwd(item.iccid, 'iccid')}换为${this.turnHidePwd(item.newiccid, 'iccid')}` : this.turnHidePwd(item.iccid, 'iccid')
}) : []
if (this.type === 'TBOX') {
// 换件
smsMsg = `尊敬的*先生/女士: 您正在对VIN号为${curVin}的车进行换件操作, 如下卡号: ${iccidTxtArr.join(',')}。确定更换请回复“Y”, 放弃更换请回复“N”`
this.courseImg = this.tboxImg
}
if (this.type === 'UNBIND') {
// 卡解绑
smsMsg = `尊敬的*先生/女士: 您正在对VIN号为${curVin}的车进行解绑操作,解绑如下卡号: ${iccidTxtArr.join(',')}。确定解绑请回复“Y”, 放弃解绑请回复“N”。解绑后该车将无法享受我们提供的联网服务, 请谨慎操作, 并确认您对该车的所属权。`
this.courseImg = this.unbindImg
}
if (this.type === 'BIND' || this.type === '') {
// 卡绑定
smsMsg = `尊敬的*先生/女士: 您正在对VIN号为${curVin}的车进行一车多卡绑定操作, 绑定如下卡号: ${iccidTxtArr.join(',')}。确定绑定请回复“Y”, 放弃绑定请回复“N”`
this.courseImg = this.bindImg
}
if (this.type === 'ENTERPRISE_UNBIND') {
smsMsg = `尊敬的*先生/女士:您作为企业责任人,正在对本企业${curVin.length}台车辆进行解绑操作。确定解绑请回复“Y”,放弃解绑请回复“N”。解绑后这批车辆将无法享受我们提供的联网服务,请谨慎操作,并确认本企业对该批车辆的所属权。`
}
this.form.vin = smsMsg
// 企业解绑不需要查询
if (this.type !== 'ENTERPRISE_UNBIND') {
this.getRnrInfoByVinFn()
} else {
this.form.mobile = this.formData.mobile
console.clear()
console.log('mobile: ', this.formData, this.form)
}
},
/**
* 加 * 号,置换首尾数字
* @param {String} val 手机号
* @time 2022-04-23 13:39:47
*/
turnHidePwd(val, type) {
const curVal = '' + val
if (type === 'vin') {
return curVal.replace(/^(\w{6})\w{8}(\w{3})$/, '$1********$2')
} else if (type === 'iccid') {
return curVal.replace(/^(\w{6})\w{8}(\w{6})$/, '$1********$2')
} else {
return curVal.replace(/^(\w{3})\w{4}(\w{4})$/, '$1****$2')
}
},
/**
* 获取实名ID以及手机号
* @time 2022-04-23 15:26:33
*/
getRnrInfoByVinFn() {
if (this.type === 'BIND') {
getUnBindIccidList({
data: {
vin: this.formData.vin
}
})
.then((res) => {
this.rnrId = res.rnrId
this.form.mobile = res.phone
})
} else {
postIccidsByVin({
data: {
vin: this.formData.vin
}
}).then(res => {
this.rnrInfo = res.mgRnrInfoDTO
this.rnrId = res.vehicleCardRnrDTO.rnrId
this.form.mobile = this.rnrInfo.phone
})
}
},
/**
* 操作等待弹出关闭后事件
* @time 2022-04-23 14:29:04
*/
updateWaitVisible(val) {
clearTimeout(this.intervals)
// 在倒计时结束后, 延迟200毫秒发出最后一次请求
setTimeout(() => {
this.postStatusFuc(true)
}, 200)
this.$emit('update:waitVisible', false)
},
/**
* 关闭解绑成功弹出
* @time 2022-04-23 15:59:13
*/
closeVisible() {
if (this.msgType === 'success') {
// 成功 - 前往首页
this.$router.replace({
name: 'Home'
})
} else {
// 失败 - 关闭弹出
this.tipVisible = false
}
},
/**
* 查询定时器
* @time 2022-04-23 15:59:13
*/
handleStatus() {
this.intervals = setTimeout(() => {
this.postStatusFuc()
}, 6000)
},
/**
* 查询是否发送短信
* @time 2022-04-23 15:59:13
*/
postStatusFuc(last) {
let reqFuc = postPollCardUnbindStatus
let reqData = { uuid: this.opreatorUuid }
if (this.type === 'BIND') {
reqFuc = surplusCardResult
reqData = { orderId: this.opreatorUuid }
}
reqFuc(reqData)
.then((res) => {
if (this.type === 'UNBIND') {
this.desc = this.carUnbindMsg[res.orderStatus] || '卡解绑失败'
}
if (this.type === 'ENTERPRISE_UNBIND') {
this.desc = this.enterpriserUnbindMsg[res.orderStatus] || '企业解绑失败'
}
if (this.type === 'TBOX') {
this.desc = this.tboxMsg[res.orderStatus] || '换件失败'
}
if (this.type === 'BIND') {
this.desc = this.bindMsg[res.orderStatus] || '卡绑定失败'
}
if (res.orderStatus === 3) {
this.msgType = 'success'
if (last) {
this.msgVisible = false
this.tipVisible = true
} else {
this.breakTimer = true
}
} else {
this.msgType = 'info'
if (last) {
this.msgVisible = false
this.tipVisible = true
} else {
if (res.orderStatus === 2 || res.orderStatus === 1) {
this.handleStatus()
} else {
this.breakTimer = true
}
}
}
})
.catch((err) => {
console.log(err)
if (last) {
this.msgVisible = false
this.tipVisible = true
} else {
this.breakTimer = true
}
this.msgType = 'info'
this.desc = err.message || '操作失败'
})
}
}
}
</script>
<style scoped lang="scss">
.SMS-confirm-course{
img{
width: 100%;
}
}
.marginTop0{
margin-top: 0 !important;
}
.SMS-confirm-content-row{
margin-top: 23px;
.SMS-confirm-content-title{
font-size: 14px;
color: #212026;
font-weight: 700;
}
.SMS-confirm-content-content{
background: #F2F3F5;
border-radius: 4px;
font-size: 16px;
color: #212026;
line-height: 24px;
font-weight: 400;
padding: 12px;
margin-top: 13px;
text-align: left;
word-break: break-all;
}
}
</style>
<template>
<div class="comp-concat-info">
<el-form ref="formRef" :model="form" :rules="formRules" size="small" label-position="top">
<el-row :gutter="40">
<el-col :span="12">
<el-form-item label="手机号" prop="phone">
<el-input v-model="form.phone" placeholder="请输入手机号" clearable maxlength="11"/>
</el-form-item>
</el-col>
<el-col v-if="needVcode" :span="12">
<el-row :gutter="8">
<el-col :span="16">
<el-form-item label="验证码" prop="veCode">
<el-input v-model="form.veCode" clearable maxlength="6" placeholder="请输入验证码"/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="验证码" class="cci-hidden-form-item-label">
<el-button :disabled="disableSendBtn" type="primary" size="small" @click.stop="clickSendVeCode">{{ sendBtnText }}</el-button>
</el-form-item>
</el-col>
</el-row>
</el-col>
</el-row>
</el-form>
</div>
</template>
<script>
import { validPhone } from '@/utils/validate'
import { sendSmsCaptcha } from '@/api/realname-person'
export default {
name: 'CompConcatInfo',
props: {
data: {
type: Object,
default: () => ({})
},
needVcode: {
type: Boolean,
default: true
},
bizType: {
type: String,
default: 'rnr'
}
},
data() {
const checkPhone = (rule, value, callback) => {
if (value === '') {
callback(new Error(`请输入手机号`))
} else if (!validPhone(value)) {
callback(new Error(`手机号格式不正确`))
} else {
callback()
}
}
return {
form: {
phone: '',
veCode: ''
},
formRules: {
phone: [
{ required: true, validator: checkPhone, trigger: 'blur' }
],
veCode: [
{ required: true, message: '请输入验证码', trigger: 'change' }
]
},
sendBtnText: '发送验证码',
countingDown: false
}
},
computed: {
disableSendBtn() {
return this.countingDown || (this.form.phone === '' || !validPhone(this.form.phone))
}
},
watch: {
data() {
this.updateInternalData()
}
},
created() {
this.init()
},
methods: {
init() {
this.updateInternalData()
},
updateInternalData() {
if (typeof this.data === 'object' && this.data !== null) {
this.form.phone = this.data.phone || ''
this.form.veCode = this.data.veCode || ''
} else {
this.form.phone = ''
this.form.veCode = ''
this.countingDown = false
this.sendBtnText = '发送验证码'
}
},
// 点击发送验证码
clickSendVeCode() {
if (this.countingDown) {
return
}
this.countingDown = true
this.sendVeCode()
this.startCountingDown()
},
// 倒计时
startCountingDown() {
clearTimeout(this._ctTid)
let ctNum = 59
this.sendBtnText = `${ctNum--}S后重发`
const countingDownHandler = () => {
if (ctNum > 0) {
this.sendBtnText = `${ctNum--}S后重发`
this._ctTid = setTimeout(countingDownHandler, 1000)
} else {
this.sendBtnText = '发送验证码'
this.countingDown = false
}
}
this._ctTid = setTimeout(countingDownHandler, 1000)
},
async sendVeCode() {
let ret = null
try {
ret = await sendSmsCaptcha({
phone: this.form.phone,
bizType: this.bizType
})
} catch (e) {
ret = {}
}
return ret || {}
},
// 校验
async validate(isErrorAnchor = false) {
try {
await this.$refs.formRef.validate()
return true
} catch (e) {
if (isErrorAnchor) {
this.$refs.formRef.fields.filter(item => item.validateState === 'error')[0].$el.scrollIntoView(true)
}
let temp = {
anchor: () => {
this.$refs.formRef.fields.filter(item => item.validateState === 'error')[0].$el.scrollIntoView(true)
temp = null
}
}
throw temp
}
},
// 获取数据
getFormData() {
return {
phone: this.form.phone,
veCode: this.form.veCode
}
},
// 获取待缓存数据
getPendingCacheData() {
return this.getFormData()
},
/**
* 清空校验值
*/
clearValidate() {
this.$refs.formRef.clearValidate()
}
}
}
</script>
<style lang="scss">
.comp-concat-info {
.cci-hidden-form-item-label {
.el-form-item__label {
visibility: hidden;
}
}
}
</style>
<template>
<div :class="{show}" class="comp-confirm">
<div class="cc-content-wrapper">
<div class="cc-content-main">
<div class="cc-icon">
<svg v-if="type === 'success'" width="100%" height="100%" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g fill="#25C343">
<path d="M14,0.875 C21.2487373,0.875 27.125,6.75126266 27.125,14 C27.125,21.2487373 21.2487373,27.125 14,27.125 C6.75126266,27.125 0.875,21.2487373 0.875,14 C0.875,6.75126266 6.75126266,0.875 14,0.875 Z M19.5344115,9.30436434 C19.3836132,9.15373919 19.1422439,9.14871835 18.9854074,9.28930183 L18.9694917,9.30436434 L11.9156158,16.3501294 L8.81175831,13.2499302 C8.66095994,13.0993051 8.41959062,13.0942842 8.26275419,13.2348677 L8.24683845,13.2499302 L7.11699873,14.3784721 C6.96620037,14.5290973 6.96117375,14.7701894 7.1019189,14.9268456 L7.11699873,14.9427431 L10.4381346,18.2601477 C10.4501112,18.2794965 10.4638977,18.2980447 10.4794941,18.3155504 L10.4956938,18.3326996 L11.6255335,19.4612415 C11.7440613,19.579633 11.9185436,19.6080705 12.0639378,19.5466213 C12.1068259,19.529948 12.1474116,19.5055961 12.1835744,19.4735657 L12.2012775,19.4569107 L13.3311172,18.3283688 C13.3407696,18.3187274 13.3498248,18.3087154 13.3582828,18.2983786 L13.3705217,18.2826356 L20.6642513,10.9971772 C20.8150496,10.846552 20.8200762,10.60546 20.6793311,10.4488037 L20.6642513,10.4329062 L19.5344115,9.30436434 Z"/>
</g>
</svg>
<svg v-if="type === 'info'" width="100%" height="100%" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-587.000000, -74.000000)" fill="#FF9C00">
<g transform="translate(587.000000, 74.000000)">
<path d="M14,0.875 C21.2487373,0.875 27.125,6.75126266 27.125,14 C27.125,21.2487373 21.2487373,27.125 14,27.125 C6.75126266,27.125 0.875,21.2487373 0.875,14 C0.875,6.75126266 6.75126266,0.875 14,0.875 Z M14.875,18.375 L13.125,18.375 C12.8906974,18.375 12.6994132,18.5591841 12.6880354,18.7906643 L12.6875,18.8125 L12.6875,20.5625 C12.6875,20.7968026 12.8716841,20.9880868 13.1031643,20.9994646 L13.125,21 L14.875,21 C15.1093026,21 15.3005868,20.8158159 15.3119646,20.5843357 L15.3125,20.5625 L15.3125,18.8125 C15.3125,18.5781974 15.1283159,18.3869132 14.8968357,18.3755354 L14.875,18.375 Z M14.875,7.984375 L13.125,7.984375 C12.8906974,7.984375 12.6994132,8.16855905 12.6880354,8.40003934 L12.6875,8.421875 L12.6875,16.1875 C12.6875,16.4218026 12.8716841,16.6130868 13.1031643,16.6244646 L13.125,16.625 L14.875,16.625 C15.1093026,16.625 15.3005868,16.4408159 15.3119646,16.2093357 L15.3125,16.1875 L15.3125,8.421875 C15.3125,8.18757238 15.1283159,7.99628825 14.8968357,7.98491042 L14.875,7.984375 Z"/>
</g>
</g>
</g>
</svg>
</div>
<div class="cc-desc">{{ desc }}</div>
</div>
<div class="cc-content-footer">
<el-button v-if="showCancelBtn" size="small" @click.stop="clickCancel">取消</el-button>
<el-button type="primary" size="small" @click.stop="clickSure">确定</el-button>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'CompConfirm',
props: {
show: {
type: Boolean,
default: false
},
desc: {
type: String,
default: ''
},
showCancelBtn: {
type: Boolean,
default: false
},
type: {
type: String,
default: 'info'
}
},
mounted() {
document.body.appendChild(this.$el)
},
destroyed() {
document.body.removeChild(this.$el)
},
methods: {
clickCancel() {
this.$emit('update:show', false)
this.$emit('on-cancel')
},
clickSure() {
this.$emit('update:show', false)
this.$emit('on-sure')
}
}
}
</script>
<style lang="scss">
.comp-confirm {
position: fixed;
left: 0;
top: 0;
z-index: 9999;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
opacity: 0;
transition: opacity .3s ease-out;
pointer-events: none;
.cc-content-wrapper {
box-sizing: border-box;
width: 480px;
padding: 24px;
border-radius: 4px;
font-size: 16px;
color: #111;
background: #fff;
transform: translate3d(0, -20px, 0);
transition: transform .3s ease-out;
.cc-content-main {
display: flex;
.cc-icon {
flex: 0 0 auto;
width: 28px;
height: 28px;
margin-right: 9px
}
.cc-desc {
line-height: 28px;
word-break: break-all;
}
}
.cc-content-footer {
margin-top: 26px;
text-align: right;
}
}
&.show {
opacity: 1;
pointer-events: auto;
.cc-content-wrapper {
transform: translate3d(0, 0, 0);
}
}
}
</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