CRM_26-08-05#story#9126,车辆管理、服务商管理系统需求
This commit is contained in:
@@ -186,8 +186,8 @@ export default {
|
|||||||
video: { facingMode: 'environment' },
|
video: { facingMode: 'environment' },
|
||||||
audio: false
|
audio: false
|
||||||
})
|
})
|
||||||
// 等待期间组件已被关闭,立即释放
|
// 等待期间组件已被关闭或销毁,立即释放
|
||||||
if (!this.visible) {
|
if (!this.visible || this._isDestroyed || this._isBeingDestroyed) {
|
||||||
stream.getTracks().forEach(t => t.stop())
|
stream.getTracks().forEach(t => t.stop())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -363,7 +363,9 @@ export default {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding-top: 80px;
|
padding-top: 80px;
|
||||||
padding-bottom: 100px;
|
/* 避让底部拍照栏(约 142px 高),防止小屏下提示被遮挡 */
|
||||||
|
padding-bottom: 150px;
|
||||||
|
padding-bottom: calc(150px + env(safe-area-inset-bottom));
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
.guideHero {
|
.guideHero {
|
||||||
@@ -408,16 +410,26 @@ export default {
|
|||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* 取景框:宽约为屏宽 80%,宽高比约 1.43:1 */
|
/* 取景框:宽约为屏宽 80%,宽高比约 1.43:1;小屏按可视高度自适应缩小,避免遮挡上下元素 */
|
||||||
.guideFrameOuter {
|
.guideFrameOuter {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
max-width: 340px;
|
/* 纵向可用高度 ≈ 100vh - (导航80 + 拍照栏150 + 提示卡92 + 底部提示92) ≈ 100vh - 420px,
|
||||||
padding-top: 69.93%; /* 宽高比约 1.43:1 */
|
换算成宽度(×1.43)并限制在 170~340px 之间,大屏表现与原 80%/340px 一致 */
|
||||||
|
width: clamp(170px, min(80%, calc((100vh - 420px) * 1.43)), 340px);
|
||||||
|
width: clamp(170px, min(80%, calc((100dvh - 420px) * 1.43)), 340px);
|
||||||
|
/* 宽高比约 1.43:1(与拍照裁剪比例一致);不用 padding-top 百分比——它相对父级宽度,无法跟随自适应宽度 */
|
||||||
|
aspect-ratio: 1.43 / 1;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
/* 不用 filter: drop-shadow,iOS 上父级 filter 会导致 video 合成层不绘制(黑屏) */
|
/* 不用 filter: drop-shadow,iOS 上父级 filter 会导致 video 合成层不绘制(黑屏) */
|
||||||
box-shadow: 0 4px 16px rgba(20, 30, 50, .12);
|
box-shadow: 0 4px 16px rgba(20, 30, 50, .12);
|
||||||
}
|
}
|
||||||
|
/* 兼容不支持 aspect-ratio 的旧浏览器:padding-top 相对父级宽度,80% / 1.43 ≈ 55.94% */
|
||||||
|
@supports not (aspect-ratio: 1 / 1) {
|
||||||
|
.guideFrameOuter {
|
||||||
|
padding-top: 55.94%;
|
||||||
|
}
|
||||||
|
}
|
||||||
.corner {
|
.corner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
|
|||||||
+10
-1
@@ -209,7 +209,16 @@ const routes = [
|
|||||||
name: 'vehicleAdd',
|
name: 'vehicleAdd',
|
||||||
component: () => import('@/views/index/vehicleAdd'),
|
component: () => import('@/views/index/vehicleAdd'),
|
||||||
meta: {
|
meta: {
|
||||||
title: '车辆新增或修改'
|
title: '车辆新增或修改',
|
||||||
|
cache: true // keep-alive 缓存:跳转拍摄页返回时保留表单,再次从其他页面进入时由 activated 重新初始化
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/cameraGuide",
|
||||||
|
name: 'cameraGuide',
|
||||||
|
component: () => import('@/views/index/cameraGuide'),
|
||||||
|
meta: {
|
||||||
|
title: '拍摄行驶证'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -454,15 +454,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</van-popup>
|
</van-popup>
|
||||||
|
|
||||||
<camera-guide
|
|
||||||
:visible="cameraGuideVisible"
|
|
||||||
:type="cameraGuideType"
|
|
||||||
:stream="cameraStream"
|
|
||||||
@confirm="onCameraConfirm"
|
|
||||||
@cancel="onCameraCancel"
|
|
||||||
@fallback="onCameraFallback"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -475,7 +466,6 @@ import {vehicleTypeList,saveVehicle,getInfoById,supplierServiceTree, uploadImage
|
|||||||
userOperationPermissions,saveSupplierApproval} from "@/api/mine"
|
userOperationPermissions,saveSupplierApproval} from "@/api/mine"
|
||||||
import TwoCommonBtn from "@/components/twoBtnCommon.vue"
|
import TwoCommonBtn from "@/components/twoBtnCommon.vue"
|
||||||
import CellGroup from "@/components/cellGroup.vue";
|
import CellGroup from "@/components/cellGroup.vue";
|
||||||
import CameraGuide from "@/components/CameraGuide.vue";
|
|
||||||
export default {
|
export default {
|
||||||
name: "vehicleAdd",
|
name: "vehicleAdd",
|
||||||
mixins:[myMixins],
|
mixins:[myMixins],
|
||||||
@@ -511,9 +501,7 @@ export default {
|
|||||||
oldSupplierServiceList:[],
|
oldSupplierServiceList:[],
|
||||||
show:false,
|
show:false,
|
||||||
imageUrl: require('@/assets/arr_right.png'),
|
imageUrl: require('@/assets/arr_right.png'),
|
||||||
cameraGuideVisible: false, // 拍摄引导弹层显示
|
|
||||||
cameraGuideType: 'front', // 当前拍摄槽位 front/back/car
|
cameraGuideType: 'front', // 当前拍摄槽位 front/back/car
|
||||||
cameraStream: null, // 探测成功的相机流,交给 CameraGuide 复用
|
|
||||||
vehicleLicenseFrontList: [],
|
vehicleLicenseFrontList: [],
|
||||||
vehicleLicenseBackList: [],
|
vehicleLicenseBackList: [],
|
||||||
vehicleLicenseCarPhotoList: [],
|
vehicleLicenseCarPhotoList: [],
|
||||||
@@ -710,6 +698,31 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
await this.initPage()
|
||||||
|
this.hasInited = true // 标记首次初始化完成(keep-alive 下 activated 据此判断再次进入需重新初始化)
|
||||||
|
},
|
||||||
|
activated() {
|
||||||
|
// 从拍摄页返回:取回拍照结果/降级信息,保留表单不重新初始化
|
||||||
|
const cam = this.$store.state.camera
|
||||||
|
if (cam.result || cam.fallback) {
|
||||||
|
const { result, fallback } = cam
|
||||||
|
this.$store.commit('camera/clear')
|
||||||
|
if (result) {
|
||||||
|
this.cameraGuideType = result.type
|
||||||
|
this.onCameraConfirm(result)
|
||||||
|
} else {
|
||||||
|
this.cameraGuideType = fallback.type
|
||||||
|
this.handleCameraFail(fallback.error)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 从车辆管理等其他页面再次进入(keep-alive 缓存复用):按当前路由重新初始化
|
||||||
|
if (this.hasInited) {
|
||||||
|
this.initPage()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
async initPage() {
|
||||||
this.id=this.$route.params?.id
|
this.id=this.$route.params?.id
|
||||||
this.approvalForm.supplierId=this.$route.params?.supplierId
|
this.approvalForm.supplierId=this.$route.params?.supplierId
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
@@ -733,7 +746,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods:{
|
|
||||||
showDateHandler() {
|
showDateHandler() {
|
||||||
if( this.id ) { // 修改
|
if( this.id ) { // 修改
|
||||||
if( this.permissonList.includes('hasInsuranceAudit') ) {
|
if( this.permissonList.includes('hasInsuranceAudit') ) {
|
||||||
@@ -948,30 +960,19 @@ export default {
|
|||||||
isCameraSupported() { // 是否支持 getUserMedia(不支持则降级系统选择器)
|
isCameraSupported() { // 是否支持 getUserMedia(不支持则降级系统选择器)
|
||||||
return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) && window.isSecureContext !== false
|
return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia) && window.isSecureContext !== false
|
||||||
},
|
},
|
||||||
async onLicenseSlotClick(type) { // 行驶证三槽位点击拦截:前置探测相机,可用才进引导页,否则直接降级系统选择器
|
onLicenseSlotClick(type) { // 行驶证三槽位点击拦截:跳独立拍摄页,不支持相机则直接降级系统选择器
|
||||||
this.cameraGuideType = type
|
this.cameraGuideType = type
|
||||||
if (!this.isCameraSupported()) {
|
if (!this.isCameraSupported()) {
|
||||||
// 不支持 getUserMedia(如 iOS 微信、非 HTTPS、老 WebView):直接调系统选择器
|
// 不支持 getUserMedia(如 iOS 微信、非 HTTPS、老 WebView):直接调系统选择器
|
||||||
this.triggerNativeUpload(type)
|
this.triggerNativeUpload(type)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
this.$router.push({ name: 'cameraGuide', query: { type } })
|
||||||
// 前置真实探测相机可用性;探测成功的流直接交给 CameraGuide 复用,
|
|
||||||
// 避免 iOS 上 stop 后立即二次 getUserMedia 拿到不渲染的“死流”(黑屏)
|
|
||||||
const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment' }, audio: false })
|
|
||||||
this.cameraStream = stream
|
|
||||||
this.cameraGuideVisible = true
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('probe getUserMedia error:', e && (e.name || e.message || e))
|
|
||||||
this.handleCameraFail(e)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
isCameraPermissionError(e) { // 是否为相机权限类错误
|
isCameraPermissionError(e) { // 是否为相机权限类错误
|
||||||
return !!(e && (e.name === 'NotAllowedError' || e.name === 'SecurityError' || e.name === 'PermissionDeniedError'))
|
return !!(e && (e.name === 'NotAllowedError' || e.name === 'SecurityError' || e.name === 'PermissionDeniedError'))
|
||||||
},
|
},
|
||||||
handleCameraFail(e) { // 相机不可用统一处理:权限类弹确认框,其他异常提示后由用户点击触发相册(保证点击手势有效)
|
handleCameraFail(e) { // 相机不可用统一处理:权限类弹确认框,其他异常提示后由用户点击触发相册(保证点击手势有效)
|
||||||
this.cameraGuideVisible = false
|
|
||||||
this.cameraStream = null
|
|
||||||
if (this.isCameraPermissionError(e)) {
|
if (this.isCameraPermissionError(e)) {
|
||||||
Dialog.confirm({
|
Dialog.confirm({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
@@ -1005,8 +1006,6 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
onCameraConfirm({ blob, dataURL }) { // 拍摄确认:构造 {file, content} 走现有压缩→上传→OCR 链路
|
onCameraConfirm({ blob, dataURL }) { // 拍摄确认:构造 {file, content} 走现有压缩→上传→OCR 链路
|
||||||
this.cameraGuideVisible = false
|
|
||||||
this.cameraStream = null
|
|
||||||
const type = this.cameraGuideType
|
const type = this.cameraGuideType
|
||||||
const file = new File([blob], `vehicle_license_${type}_${Date.now()}.jpg`, { type: 'image/jpeg' })
|
const file = new File([blob], `vehicle_license_${type}_${Date.now()}.jpg`, { type: 'image/jpeg' })
|
||||||
const fileItem = { file, content: dataURL }
|
const fileItem = { file, content: dataURL }
|
||||||
@@ -1024,13 +1023,6 @@ export default {
|
|||||||
this[listMap[type]] = [fileItem]
|
this[listMap[type]] = [fileItem]
|
||||||
this[handlerMap[type]](fileItem)
|
this[handlerMap[type]](fileItem)
|
||||||
},
|
},
|
||||||
onCameraCancel() { // 返回放弃
|
|
||||||
this.cameraGuideVisible = false
|
|
||||||
this.cameraStream = null
|
|
||||||
},
|
|
||||||
onCameraFallback(e) { // CameraGuide 内 getUserMedia 失败(前置探测已通过,概率极低):按异常类型统一处理
|
|
||||||
this.handleCameraFail(e)
|
|
||||||
},
|
|
||||||
async vehicleLicenseFrontHandler(file) { // 上传 行驶证首页
|
async vehicleLicenseFrontHandler(file) { // 上传 行驶证首页
|
||||||
console.log('file',file)
|
console.log('file',file)
|
||||||
try {
|
try {
|
||||||
@@ -1702,8 +1694,7 @@ export default {
|
|||||||
},
|
},
|
||||||
components:{
|
components:{
|
||||||
CellGroup,
|
CellGroup,
|
||||||
TwoCommonBtn,
|
TwoCommonBtn
|
||||||
CameraGuide
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user