Browse Source

拍照录像提交

dev-V1.10.2-temp
wangmingyong@163.com 2 months ago
parent
commit
2164a5a08f
  1. 67
      zdxt-web-client/zdxt-efcode-enterprise-app/src/util/w6sMedia.js
  2. 128
      zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/nos/components/NosDetailMaterialsTab.vue
  3. 4
      zdxt-web-client/zdxt-efcode-enterprise-app/vite-env/.env.enterprise-dev-wmy

67
zdxt-web-client/zdxt-efcode-enterprise-app/src/util/w6sMedia.js

@ -113,6 +113,47 @@ export const takePhotoWithWaterMark = async ({
})
}
// 将文件从公共目录复制到应用私有目录(解决 Android 11+ 分区存储导致的 EACCES 权限问题)
const copyFileToAppDir = (sourcePath) => {
return new Promise((resolve, reject) => {
if (!window.cordova?.file?.dataDirectory) {
reject(new Error('cordova.file.dataDirectory 不可用'))
return
}
window.resolveLocalFileSystemURL(
sourcePath,
(fileEntry) => {
window.resolveLocalFileSystemURL(
window.cordova.file.dataDirectory,
(dataDirEntry) => {
const fileName = fileEntry.name
fileEntry.copyTo(
dataDirEntry,
fileName,
(newFileEntry) => {
console.log('[w6sMedia] 文件已复制到应用私有目录:', newFileEntry.fullPath)
resolve(newFileEntry.fullPath)
},
(copyErr) => {
console.error('[w6sMedia] 复制文件失败:', copyErr)
reject(copyErr)
}
)
},
(dirErr) => {
console.error('[w6sMedia] 获取应用私有目录失败:', dirErr)
reject(dirErr)
}
)
},
(fileErr) => {
console.error('[w6sMedia] 解析源文件失败:', fileErr, 'sourcePath:', sourcePath)
reject(fileErr)
}
)
})
}
// 录制视频(使用 Cordova 原生 Media Capture API)
export const startVideoRecorder = async ({
duration = 60,
@ -130,17 +171,29 @@ export const startVideoRecorder = async ({
return
}
navigator.device.capture.captureVideo(
(mediaFiles) => {
async (mediaFiles) => {
console.log('[w6sMedia] captureVideo success, mediaFiles:', mediaFiles)
if (mediaFiles && mediaFiles.length > 0) {
const file = mediaFiles[0]
console.log('[w6sMedia] 视频文件: fullPath=', file.fullPath, 'name=', file.name, 'size=', file.size, 'type=', file.type)
resolve({
video_path: file.fullPath,
name: file.name,
size: file.size,
type: file.type || 'video/mp4',
})
try {
const appDirPath = await copyFileToAppDir(file.fullPath)
console.log('[w6sMedia] 视频已复制到应用私有目录:', appDirPath)
resolve({
video_path: appDirPath,
name: file.name,
size: file.size,
type: file.type || 'video/mp4',
})
} catch (copyErr) {
console.warn('[w6sMedia] 复制视频失败,尝试使用原路径:', copyErr)
resolve({
video_path: file.fullPath,
name: file.name,
size: file.size,
type: file.type || 'video/mp4',
})
}
} else {
reject(new Error('录制未生成视频文件'))
}

128
zdxt-web-client/zdxt-efcode-enterprise-app/src/views/enterprise/nos/components/NosDetailMaterialsTab.vue

@ -64,10 +64,36 @@
</template>
</van-uploader>
<!-- 生产环境:自定义上传按钮,点击弹出 action-sheet -->
<div v-else class="uploader-custom-btn" @click="onOpenUploadAction(item)">
<van-icon name="photograph" size="24" color="#dcdee0" />
</div>
<!-- 生产环境:已上传文件预览 + 上传按钮 -->
<template v-else>
<template v-for="(file, fileIndex) in item.fileList" :key="fileIndex">
<div v-if="file.status === 'uploading'" class="media-item media-item--uploading">
<div class="uploading-overlay">
<van-loading color="#fff" size="24" />
<span class="uploading-text">{{ file.message || '上传中...' }}</span>
</div>
</div>
<div v-else-if="file.status === 'failed'" class="media-item media-item--failed" @click="onRemoveFile(item, fileIndex)">
<div class="failed-overlay">
<van-icon name="cross" color="#fff" size="24" />
<span class="failed-text">{{ file.message || '上传失败' }}</span>
<span class="failed-hint">点击删除重试</span>
</div>
</div>
<div v-else-if="file.isImage" class="media-item" @click="onPreviewItem(item, fileIndex)">
<van-image :src="file.url" fit="cover" class="media-item-img" />
<van-icon name="cross" class="media-item--remove" @click.stop="onRemoveFile(item, fileIndex)" />
</div>
<div v-else class="media-item media-item--video" @click="onPreviewItem(item, fileIndex)">
<video :src="file.url" muted preload="metadata" class="video-thumb"></video>
<van-icon name="play-circle-o" class="play-icon" />
<van-icon name="cross" class="media-item--remove" @click.stop="onRemoveFile(item, fileIndex)" />
</div>
</template>
<div v-if="(item.fileList?.length || 0) < 20" class="uploader-custom-btn" @click="onOpenUploadAction(item)">
<van-icon name="photograph" size="24" color="#dcdee0" />
</div>
</template>
</div>
<div class="objection-card" v-if="Number(item.pass) === 0">
@ -540,6 +566,34 @@ const onPreview = (url, itemValue) => {
startPosition: start >= 0 ? start : 0
})
}
const onPreviewItem = (item, fileIndex) => {
const file = item.fileList?.[fileIndex]
if (!file) return
if (file.isImage) {
const images = (item.fileList || [])
.filter((f) => f.isImage && f.status === 'done')
.map((f) => f.url)
const start = images.indexOf(file.url)
showImagePreview({
images,
startPosition: start >= 0 ? start : 0
})
} else {
previewVideoUrl.value = file.url
showVideoPreview.value = true
}
}
const onRemoveFile = (item, fileIndex) => {
showConfirmDialog({
title: '确认删除',
message: '确定删除该文件吗?',
}).then(() => {
item.fileList.splice(fileIndex, 1)
showToast('已删除')
}).catch(() => {})
}
</script>
<style lang="less" scoped>
@ -626,6 +680,72 @@ const onPreview = (url, itemValue) => {
height: 100%;
}
.media-item-img {
width: 100%;
height: 100%;
}
.media-item--remove {
position: absolute;
top: 2px;
right: 2px;
font-size: 16px;
color: #fff;
background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
width: 18px;
height: 18px;
display: flex;
align-items: center;
justify-content: center;
}
&--uploading {
background-color: #f7f8fa;
display: flex;
align-items: center;
justify-content: center;
.uploading-overlay {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
.uploading-text {
font-size: 12px;
color: #323233;
}
}
}
&--failed {
background-color: #ee0a24;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
.failed-overlay {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
.failed-text {
font-size: 12px;
color: #fff;
font-weight: 500;
}
.failed-hint {
font-size: 10px;
color: rgba(255, 255, 255, 0.8);
}
}
}
&--video {
cursor: pointer;

4
zdxt-web-client/zdxt-efcode-enterprise-app/vite-env/.env.enterprise-dev-wmy

@ -1,9 +1,9 @@
# 企业端开发环境
VITE_PORT = 12001
# VITE_PROXY_TARGET = 'http://139.186.148.21:8080'
VITE_PROXY_TARGET = 'http://172.16.21.213:8080'
# VITE_PROXY_TARGET = 'http://172.16.21.213:8080'
# VITE_PROXY_TARGET = 'http://172.16.21.93:8080'
# VITE_PROXY_TARGET = 'http://172.16.20.72:8080'
VITE_PROXY_TARGET = 'http://172.16.20.72:8080'
VITE_BASE_URL = '/efcode20-ent-app/api'
VITE_CONTEXT_PATH = '/efcode20-ent-app/'
VITE_DEFAULT_LOGIN = '/enterprise/loginByPassword'

Loading…
Cancel
Save