123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- <template>
- <view class="interview-container">
- <view class="header">
- <view class="title">补拍检测</view>
- <view class="subtitle">请按提示重新拍摄失败项</view>
- </view>
-
- <view class="camera-container" v-if="showCamera">
- <camera class="camera" device-position="front" flash="off" @error="handleError">
- <view class="gesture-overlay">
- <image class="gesture-image" :src="currentStep.tipImage"></image>
- <cover-view class="tip-text">{{currentStep.tipText}}</cover-view>
- </view>
- <view class="camera-tip" :style="currentStep.type.includes('right_')?'left:30rpx':'right:30rpx'">
- <view class="guide-icon">
- <video
- :src="currentStep.guideVideo"
- id="mpVideo"
- autoplay
- class="mp-video-player"
- :controls="false"
- loop
- muted
- object-fit="contain"
- @error="handleVideoError"
- @loadedmetadata="handleVideoLoaded"
- ></video>
- <view class="video-loading" v-if="isVideoLoading">
- <text>加载中...</text>
- </view>
- <view class="video-error" v-if="videoError">
- <text>{{videoErrorMsg}}</text>
- </view>
- </view>
- </view>
- </camera>
- <view class="capture-btn" @tap="takePhoto"></view>
- </view>
-
- <view class="preview-container" v-else>
- <image :src="tempImagePath" mode="aspectFit"></image>
- <view class="preview-buttons">
- <button class="btn-retake" @tap="retakePhoto">重拍</button>
- <button class="btn-confirm" @tap="confirmPhoto">确认</button>
- </view>
- </view>
-
- <view class="progress-bar">
- <view class="step-item" v-for="(item, index) in failedItems" :key="index"
- :class="{'active': currentIndex === index, 'completed': index < currentIndex}">
- {{item.name}}
- </view>
- </view>
- </view>
- </template>
- <script>
- import { apiBaseUrl } from '@/common/config.js'; // 导入基础URL配置
- export default {
- data() {
- return {
- failedItems: [],
- currentIndex: 0,
- showCamera: true,
- tempImagePath: '',
- uploadedImages: {},
- isH5: false,
- cameraPosition: 'front',
- cameraMode: 'normal',
- cameraContext: null,
- cameraInitRetries: 0,
- // 添加手势引导图片
- gestureOverlays: {
- left_palm: 'http://data.qicai321.com/minlong/18590378-6792-4a26-be18-ad9f2bcc4159.png',
- left_back: 'http://data.qicai321.com/minlong/e08cff2b-3b1d-478f-a62b-766b38445a16.png',
- left_fist: 'http://data.qicai321.com/minlong/d30ccab7-9cfe-4386-bf0e-ac1f0045bd76.png',
- right_palm: 'http://data.qicai321.com/minlong/8068a698-ac40-4a5d-a737-54dba47a668d.png',
- right_back: 'http://data.qicai321.com/minlong/f04a1f7e-4f79-49c7-9a02-bbad296672ea.png',
- right_fist: 'http://data.qicai321.com/minlong/1d9bbc36-2fb8-4489-bff6-c36e7a31bd37.png'
- },
- // 添加引导视频
- guideVideos: {
- left_palm: 'http://data.qicai321.com/minlong/fd080e3b-67be-4ce7-87a3-58d047cfbbb1.mp4',
- left_back: 'http://data.qicai321.com/minlong/394a858c-672a-44bd-8206-450913863900.mp4',
- left_fist: 'http://data.qicai321.com/minlong/37153e68-a8f0-4a93-b677-5e5678379243.mp4',
- right_palm: 'http://data.qicai321.com/minlong/ebf6e43f-ffdf-49bb-8b00-adeff9ce1b56.mp4',
- right_back: 'http://data.qicai321.com/minlong/e0ca45e2-af3e-42a0-9dd1-4de545e9c720.mp4',
- right_fist: 'http://data.qicai321.com/minlong/77028dd6-22bb-41b4-8879-19699e8b1dbb.mp4'
- },
- isVideoLoading: true,
- videoError: false,
- videoErrorMsg: '',
- videoContext: null
- }
- },
- computed: {
- currentStep() {
- if (!this.failedItems.length) return null
- const current = this.failedItems[this.currentIndex]
- return {
- ...current,
- tipText: `${current.name}`,
- tipImage: this.gestureOverlays[current.type],
- guideVideo: this.guideVideos[current.type]
- }
- }
- },
- onLoad(options) {
- if (options.failedItems) {
- const items = JSON.parse(options.failedItems)
- this.failedItems = items.map(item => ({
- name: this.getNameByType(item.type),
- type: item.type
- }))
- }
- },
- methods: {
- getNameByType(type) {
- const nameMap = {
- left_palm: '展示完整的左手手掌',
- left_back: '展示完整的左手手背',
- left_fist: '展示完整的左手握拳',
- right_palm: '展示完整的右手手掌',
- right_back: '展示完整的右手手背',
- right_fist: '展示完整的右手握拳'
- }
- return nameMap[type] || ''
- },
- takePhoto() {
- const camera = uni.createCameraContext()
- camera.takePhoto({
- quality: 'high',
- success: (res) => {
- this.tempImagePath = res.tempImagePath
- this.showCamera = false
- },
- fail: (err) => {
- uni.showToast({
- title: '拍照失败',
- icon: 'none'
- })
- }
- })
- },
- retakePhoto() {
- this.showCamera = true
- this.tempImagePath = ''
- },
- async confirmPhoto() {
- try {
- const uploadResult = await this.uploadImage(this.tempImagePath)
- this.uploadedImages[this.currentStep.type] = uploadResult.url
-
- if (this.currentIndex < this.failedItems.length - 1) {
- this.currentIndex++
- this.showCamera = true
- this.tempImagePath = ''
- } else {
- // 所有补拍完成,返回结果
- this.completeRetake()
- }
- } catch (err) {
- uni.showToast({
- title: '上传失败,请重试',
- icon: 'none'
- })
- }
- },
- async uploadImage(tempFilePath) {
- const tenant_id = uni.getStorageSync('tenant_id') || '1';
- const description = `补拍-${this.currentStep.name}`;
- return new Promise((resolve, reject) => {
- uni.uploadFile({
- url: `${apiBaseUrl}/job/upload_posture_photo`,
- filePath: tempFilePath,
- name: 'photo',
- formData: {
- 'application_id': uni.getStorageSync('appId'),
- 'tenant_id': tenant_id,
- 'description': description
- },
- success: (uploadRes) => {
- try {
- const result = typeof uploadRes.data === 'string' ?
- JSON.parse(uploadRes.data) : uploadRes.data;
- resolve(result);
- } catch (e) {
- reject(new Error('解析上传结果失败'));
- }
- },
- fail: reject
- });
- });
- },
- completeRetake() {
- // 返回检测结果页面并传递补拍结果
- const pages = getCurrentPages()
- const prevPage = pages[pages.length - 2]
-
- // 更新上一页的检测结果
- if (prevPage && prevPage.$vm) {
- prevPage.$vm.updateCheckResults(this.uploadedImages)
- }
-
- // 显示成功提示
- uni.showToast({
- title: '补拍完成',
- icon: 'success',
- duration: 1500,
- success: () => {
- // 延迟跳转到成功页面
- setTimeout(() => {
- uni.redirectTo({
- url: '/pages/success/success?type=retake&message=手部照片补拍完成,请等待系统审核'
- });
- }, 1500);
- }
- });
- },
- handleError(e) {
- console.error('相机错误:', e.detail);
- uni.showToast({
- title: '相机启动失败,将使用系统相机',
- icon: 'none'
- });
- this.fallbackToChooseImage();
- },
- fallbackToChooseImage() {
- this.showCamera = false;
- uni.chooseImage({
- count: 1,
- sourceType: ['camera'],
- success: async (res) => {
- try {
- const uploadResult = await this.uploadImage(res.tempFilePaths[0]);
- this.uploadedImages[this.currentStep.type] = uploadResult.data.url;
- this.handlePhotoSuccess();
- } catch (err) {
- this.handlePhotoError();
- }
- },
- fail: () => this.handlePhotoError()
- });
- },
- handlePhotoSuccess() {
- if (this.currentIndex < this.failedItems.length - 1) {
- this.currentIndex++;
- this.showCamera = true;
- this.tempImagePath = '';
- } else {
- this.completeRetake();
- }
- },
- handlePhotoError() {
- uni.showToast({
- title: '拍照失败,请重试',
- icon: 'none'
- });
- },
- handleVideoError(e) {
- console.error('视频加载错误:', e);
- this.videoError = true;
- this.videoErrorMsg = '视频加载失败,请检查网络';
- this.isVideoLoading = false;
-
- // 尝试重新加载视频
- if (this.videoContext) {
- setTimeout(() => {
- this.videoContext.play();
- }, 1000);
- }
- },
- handleVideoLoaded() {
- this.isVideoLoading = false;
- this.videoError = false;
- this.videoErrorMsg = '';
- },
- initVideoContext() {
- // 初始化视频上下文
- if (!this.videoContext) {
- this.videoContext = uni.createVideoContext('mpVideo', this);
- }
- },
- onShow() {
- this.initVideoContext();
- }
- }
- }
- </script>
- <style>
- .interview-container {
- height: 100vh;
- display: flex;
- flex-direction: column;
- }
- .header {
- padding: 40rpx;
- text-align: center;
- }
- .title {
- font-size: 36rpx;
- font-weight: bold;
- }
- .subtitle {
- font-size: 28rpx;
- color: #666;
- margin-top: 20rpx;
- }
- .camera-container {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background-color: #000;
- z-index: 999;
- }
- .camera {
- width: 100%;
- height: 100%;
- }
- .gesture-overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- pointer-events: none;
- z-index: 10;
- }
- .gesture-image {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 90%;
- opacity: 0.5;
- /* 设置图片混合模式,使绿色更突出 */
- mix-blend-mode: screen;
- }
- .camera-tip {
- position: absolute;
- top: 20rpx;
- display: flex;
- flex-direction: column;
- align-items: center;
- z-index: 11;
- }
- .guide-icon {
- width:160rpx;
- height: 200rpx;
- background-color: rgba(0, 0, 0, 0.3);
- border-radius: 20rpx;
- overflow: hidden;
- }
- .mp-video-player {
- width: 100%;
- height: 100%;
- object-fit: cover;
- }
- .tip-text {
- position: absolute;
- top:20rpx;
- left: 30%;
- color: #fff;
- font-size: 28rpx;
- margin-top: 16rpx;
- background-color: rgba(0, 0, 0, 0.3);
- padding: 8rpx 20rpx;
- border-radius: 30rpx;
- }
- .capture-btn {
- position: absolute;
- bottom: 100rpx;
- left: 50%;
- transform: translateX(-50%);
- width: 140rpx;
- height: 140rpx;
- border-radius: 50%;
- background: #fff;
- border: 10rpx solid #0039b3;
- z-index: 999;
- }
- .preview-container {
- flex: 1;
- display: flex;
- flex-direction: column;
- }
- .preview-container image {
- flex: 1;
- width: 100%;
- }
- .preview-buttons {
- display: flex;
- padding: 20rpx;
- justify-content: space-around;
- }
- .btn-retake, .btn-confirm {
- width: 45%;
- height: 80rpx;
- line-height: 80rpx;
- text-align: center;
- border-radius: 8rpx;
- }
- .btn-retake {
- background: #f5f5f5;
- color: #333;
- }
- .btn-confirm {
- background: #003399;
- color: #fff;
- }
- .progress-bar {
- display: flex;
- padding: 20rpx;
- background: #fff;
- }
- .step-item {
- flex: 1;
- text-align: center;
- font-size: 24rpx;
- color: #999;
- position: relative;
- padding: 20rpx 0;
- }
- .step-item.active {
- color: #003399;
- font-weight: bold;
- }
- .step-item.completed {
- color: #67c23a;
- }
- .video-loading, .video-error {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- display: flex;
- justify-content: center;
- align-items: center;
- background: rgba(0, 0, 0, 0.5);
- }
- .video-loading text, .video-error text {
- color: #fff;
- font-size: 24rpx;
- }
- .video-error text {
- color: #ff4d4f;
- }
- </style>
|