Browse Source

拍照、视频

master
han\hanst 1 week ago
parent
commit
03021782c5
  1. 1
      src/api/longchuang/productionPlan.js
  2. 807
      src/views/modules/longtron/production-work-report.vue
  3. 4
      src/views/modules/longtron/screen-cable-cop-progress.vue
  4. 2
      src/views/modules/longtron/screen-machining-progress.vue
  5. 4
      src/views/modules/longtron/screen-renovation-progress.vue
  6. 4
      src/views/modules/longtron/screen-whole-lift-progress.vue

1
src/api/longchuang/productionPlan.js

@ -38,3 +38,4 @@ export const cancelReportLog = data => createAPI(`/longchuang/productionPlan/rep
// ================= 生产报工 ================= // ================= 生产报工 =================
export const getWorkReportOrderList = data => createAPI(`/longchuang/productionPlan/workReport/list`, 'post', data) export const getWorkReportOrderList = data => createAPI(`/longchuang/productionPlan/workReport/list`, 'post', data)
export const reportWorkNodeWithMedia = data => createAPI(`/longchuang/productionPlan/workReport/reportNodeWithMedia`, 'post', data)

807
src/views/modules/longtron/production-work-report.vue

@ -170,12 +170,14 @@
</div> </div>
<el-dialog <el-dialog
title="节点报工"
:title="reportDialogTitle"
:visible.sync="reportDialogVisible" :visible.sync="reportDialogVisible"
width="400px"
:width="requiresMediaUpload(reportData.orderType, reportData.nodeCode, reportData.nodeName) ? '80%' : '400px'"
:close-on-click-modal="false" :close-on-click-modal="false"
:before-close="handleReportDialogClose"
:custom-class="reportDialogClass"
v-drag> v-drag>
<el-form :model="reportData" label-position="top">
<el-form :model="reportData" label-position="top" class="report-dialog-form">
<el-row :gutter="12"> <el-row :gutter="12">
<el-col :span="12"> <el-col :span="12">
<el-form-item label="项目号"> <el-form-item label="项目号">
@ -188,21 +190,98 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="24">
<el-row :gutter="24" v-if="isQtyRequired(reportData.orderType)">
<el-col :span="24"> <el-col :span="24">
<el-form-item label="报工数量" required> <el-form-item label="报工数量" required>
<el-input v-model="reportData.reportQty" style="width: 100%"></el-input> <el-input v-model="reportData.reportQty" style="width: 100%"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="24" v-if="requiresMediaUpload(reportData.orderType, reportData.nodeCode, reportData.nodeName)">
<el-col :span="24">
<el-form-item label="报工影像(支持拍照和录像,可多次采集)">
<div class="media-layout">
<div class="media-layout-left">
<div class="camera-preview-wrap">
<video ref="cameraPreview" class="camera-preview" autoplay playsinline muted></video>
<div v-if="!cameraActive" class="camera-placeholder">摄像头未开启</div>
</div>
<div class="camera-action-bar">
<!-- <el-button size="mini" type="primary" plain icon="el-icon-video-camera" @click="startCamera" :disabled="reportLoading">
开启摄像头
</el-button>
<el-button size="mini" plain icon="el-icon-camera" @click="capturePhoto" :disabled="!cameraActive || reportLoading || recording">
拍照
</el-button>
<el-button
size="mini"
:type="recording ? 'danger' : 'warning'"
plain
icon="el-icon-video-play"
@click="toggleVideoRecording"
:disabled="!cameraActive || reportLoading">
{{ recording ? '停止录像' : '开始录像' }}
</el-button>-->
<el-button class="camera-btn" plain icon="el-icon-camera" @click="triggerSystemCamera('image')" :disabled="reportLoading">
拍照
</el-button>
<el-button class="video-btn" plain icon="el-icon-video-camera" @click="triggerSystemCamera('video')" :disabled="reportLoading">
录像
</el-button>
<!-- <el-button size="mini" plain icon="el-icon-switch-button" @click="stopCamera" :disabled="!cameraActive || reportLoading">
关闭摄像头
</el-button>-->
</div>
<input
ref="cameraImageInput"
class="camera-file-input"
type="file"
accept="image/*"
capture="environment"
multiple
@change="handleSystemCameraChange($event, 'image')">
<input
ref="cameraVideoInput"
class="camera-file-input"
type="file"
accept="video/*"
capture="environment"
multiple
@change="handleSystemCameraChange($event, 'video')">
</div>
<div class="media-layout-right">
<div class="capture-list-title">已采集 {{ capturedMediaList.length }} 个文件</div>
<div class="capture-list-panel">
<div v-if="capturedMediaList.length > 0" class="capture-list">
<div v-for="(item, index) in capturedMediaList" :key="item.uid" class="capture-item">
<img v-if="isImageFile(item)" :src="item.previewUrl" class="capture-thumb" alt="capture-image">
<video v-else class="capture-thumb" controls preload="metadata">
<source :src="item.previewUrl" :type="item.mimeType || 'video/webm'">
</video>
<div class="capture-meta">
<span class="capture-name">{{ item.name }}</span>
<el-tag size="mini" :type="item.kind === 'video' ? 'warning' : 'success'">
{{ item.kind === 'video' ? '视频' : '照片' }}
</el-tag>
</div>
<el-button type="danger" @click="removeCapturedMedia(index)">删除</el-button>
</div>
</div>
<div v-else class="empty-capture-list">
<i class="el-icon-camera"></i>
<p>暂无采集文件请先拍照或录像</p>
</div>
</div>
</div>
</div>
</el-form-item>
</el-col>
</el-row>
</el-form> </el-form>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button size="mini" class="reset-btn"
plain @click="reportDialogVisible = false">取消</el-button>
<el-button size="mini"
type="primary"
plain :loading="reportLoading" @click="submitNodeReport">
{{ reportLoading ? '提交中...' : '确认报工' }}
<el-button size="mini" class="reset-btn" plain @click="closeReportDialog">取消</el-button>
<el-button size="mini" type="primary" plain :loading="reportLoading" @click="submitNodeReport">
{{ reportLoading ? (requiresMediaUpload(reportData.orderType, reportData.nodeCode, reportData.nodeName) ? '上传中...' : '提交中...') : (requiresMediaUpload(reportData.orderType, reportData.nodeCode, reportData.nodeName) ? '上传并报工' : '确认报工') }}
</el-button> </el-button>
</div> </div>
</el-dialog> </el-dialog>
@ -239,11 +318,11 @@
</template> </template>
<script> <script>
import { getWorkReportOrderList, reportCableCopTaskNode, reportHomeLiftOrderNode, reportMachiningTaskNode, reportRenovationOrderNode } from '@/api/longchuang/productionPlan'
import { getWorkReportOrderList, reportCableCopTaskNode, reportHomeLiftOrderNode, reportMachiningTaskNode, reportRenovationOrderNode, reportWorkNodeWithMedia } from '@/api/longchuang/productionPlan'
export default { export default {
name: 'ProductionWorkReport', name: 'ProductionWorkReport',
data() {
data () {
return { return {
searchExpanded: ['0'], searchExpanded: ['0'],
searchData: { searchData: {
@ -272,22 +351,45 @@ export default {
reportQty: 1, reportQty: 1,
reportBy: '', reportBy: '',
remark: '' remark: ''
}
},
cameraActive: false,
cameraStream: null,
mediaRecorder: null,
recording: false,
recordedChunks: [],
captureIndex: 1,
capturedMediaList: []
} }
}, },
computed: { computed: {
totalOrders() {
totalOrders () {
return this.totalPage || 0 return this.totalPage || 0
}, },
pendingNodeCount() {
pendingNodeCount () {
return this.dataList.reduce((sum, item) => sum + (item.visibleNodeList || []).filter(node => node.status !== '已完成').length, 0) return this.dataList.reduce((sum, item) => sum + (item.visibleNodeList || []).filter(node => node.status !== '已完成').length, 0)
},
reportDialogTitle () {
return this.requiresMediaUpload(this.reportData.orderType, this.reportData.nodeCode, this.reportData.nodeName) ? '节点报工与影像上传' : '节点报工'
},
reportDialogClass () {
const classList = ['report-node-dialog']
if (this.requiresMediaUpload(this.reportData.orderType, this.reportData.nodeCode, this.reportData.nodeName)) {
classList.push('report-node-dialog--media')
}
return classList.join(' ')
} }
}, },
activated() {
activated () {
this.getDataList('Y') this.getDataList('Y')
}, },
deactivated () {
this.releaseMediaResources()
},
beforeDestroy () {
this.releaseMediaResources()
},
methods: { methods: {
getDataList(flag) {
getDataList (flag) {
if (flag === 'Y') { if (flag === 'Y') {
this.pageIndex = 1 this.pageIndex = 1
} }
@ -310,7 +412,7 @@ export default {
this.$message.error('查询失败') this.$message.error('查询失败')
}) })
}, },
normalizeOrderRow(row) {
normalizeOrderRow (row) {
const nodeList = row.nodeList || [] const nodeList = row.nodeList || []
const fallbackNodeDoneCount = nodeList.filter(item => item.status === '已完成').length const fallbackNodeDoneCount = nodeList.filter(item => item.status === '已完成').length
const fallbackCurrentNodeObj = nodeList.find(item => item.status !== '已完成') || {} const fallbackCurrentNodeObj = nodeList.find(item => item.status !== '已完成') || {}
@ -332,7 +434,7 @@ export default {
visiblePendingNodeList: nodeList.filter(item => item.status !== '已完成') visiblePendingNodeList: nodeList.filter(item => item.status !== '已完成')
} }
}, },
getOrderTypeName(orderType) {
getOrderTypeName (orderType) {
const map = { const map = {
HOME_LIFT: '家用电梯', HOME_LIFT: '家用电梯',
CABLE_COP: '线缆COP', CABLE_COP: '线缆COP',
@ -341,10 +443,10 @@ export default {
} }
return map[orderType] || orderType || '-' return map[orderType] || orderType || '-'
}, },
getModelFieldLabel(orderType) {
getModelFieldLabel (orderType) {
return orderType === 'MACHINING' ? '物料号' : '型号' return orderType === 'MACHINING' ? '物料号' : '型号'
}, },
resetQuery() {
resetQuery () {
this.searchData = { this.searchData = {
projectNo: '', projectNo: '',
orderType: '', orderType: '',
@ -356,7 +458,7 @@ export default {
} }
this.getDataList('Y') this.getDataList('Y')
}, },
getTypeTag(type) {
getTypeTag (type) {
const tagMap = { const tagMap = {
HOME_LIFT: 'primary', HOME_LIFT: 'primary',
CABLE_COP: 'warning', CABLE_COP: 'warning',
@ -365,7 +467,7 @@ export default {
} }
return tagMap[type] || 'info' return tagMap[type] || 'info'
}, },
getStatusClass(status) {
getStatusClass (status) {
const statusMap = { const statusMap = {
已排产: 'status-info', 已排产: 'status-info',
进行中: 'status-warning', 进行中: 'status-warning',
@ -373,7 +475,7 @@ export default {
} }
return statusMap[status] || 'status-info' return statusMap[status] || 'status-info'
}, },
getNodeStatusType(status) {
getNodeStatusType (status) {
const statusMap = { const statusMap = {
未开始: 'info', 未开始: 'info',
进行中: 'warning', 进行中: 'warning',
@ -381,7 +483,7 @@ export default {
} }
return statusMap[status] || 'info' return statusMap[status] || 'info'
}, },
getCardClass(item) {
getCardClass (item) {
if (item.status === '已完成') { if (item.status === '已完成') {
return 'card-done' return 'card-done'
} }
@ -390,7 +492,7 @@ export default {
} }
return 'card-plan' return 'card-plan'
}, },
canReportNode(order, node) {
canReportNode (order, node) {
if (!order || !node) { if (!order || !node) {
return false return false
} }
@ -399,14 +501,51 @@ export default {
} }
return !!order.currentNodeCode && order.currentNodeCode === node.nodeCode return !!order.currentNodeCode && order.currentNodeCode === node.nodeCode
}, },
handleReportNode(order, node) {
if (order && order.orderType === 'MACHINING') {
handleReportNode (order, node) {
if (!order || !node) {
return
}
if (order.orderType === 'MACHINING') {
this.openReportDialog(order, node, true) this.openReportDialog(order, node, true)
return return
} }
if (this.requiresMediaUpload(order.orderType, node.nodeCode, node.nodeName)) {
this.openReportDialog(order, node, false)
return
}
this.directReportNode(order, node) this.directReportNode(order, node)
}, },
directReportNode(order, node) {
isQtyRequired (orderType) {
return ['CABLE_COP', 'MACHINING'].includes(orderType)
},
requiresMediaUpload (orderType, nodeCode, nodeName) {
if (!orderType) {
return false
}
const requiredMap = {
HOME_LIFT: {
codeList: ['platformDebug', 'bgCeiling', 'doorAssy', 'pack'],
nameList: ['平台组装/调试', '背景墙/吊顶组装', '门组装', '打包']
},
RENOVATION: {
codeList: ['inspect', 'pack'],
nameList: ['检验', '打包']
}
}
const config = requiredMap[orderType]
if (!config) {
return false
}
if (nodeCode && config.codeList.includes(nodeCode)) {
return true
}
if (!nodeName) {
return false
}
const normalizedName = String(nodeName).replace(/\s+/g, '')
return config.nameList.some(name => name.replace(/\s+/g, '') === normalizedName)
},
directReportNode (order, node) {
this.reportData = { this.reportData = {
orderNo: order.orderNo, orderNo: order.orderNo,
orderType: order.orderType, orderType: order.orderType,
@ -419,7 +558,7 @@ export default {
} }
this.submitNodeReport() this.submitNodeReport()
}, },
openReportDialog(order, node, forceManualQty = false) {
openReportDialog (order, node, forceManualQty = false) {
this.reportData = { this.reportData = {
orderNo: order.orderNo, orderNo: order.orderNo,
orderType: order.orderType, orderType: order.orderType,
@ -431,19 +570,35 @@ export default {
remark: '' remark: ''
} }
this.reportDialogVisible = true this.reportDialogVisible = true
this.releaseMediaResources()
this.captureIndex = 1
},
closeReportDialog () {
this.reportDialogVisible = false
this.releaseMediaResources()
}, },
submitNodeReport() {
handleReportDialogClose (done) {
this.closeReportDialog()
if (typeof done === 'function') {
done()
}
},
submitNodeReport () {
if (!this.reportData.nodeCode) { if (!this.reportData.nodeCode) {
this.$message.warning('请选择报工节点') this.$message.warning('请选择报工节点')
return return
} }
if (['CABLE_COP', 'MACHINING'].includes(this.reportData.orderType)) {
if (this.isQtyRequired(this.reportData.orderType)) {
const qty = Number(this.reportData.reportQty) const qty = Number(this.reportData.reportQty)
if (!Number.isFinite(qty) || qty <= 0) { if (!Number.isFinite(qty) || qty <= 0) {
this.$message.warning('请填写大于0的实际生产数量') this.$message.warning('请填写大于0的实际生产数量')
return return
} }
} }
if (this.requiresMediaUpload(this.reportData.orderType, this.reportData.nodeCode, this.reportData.nodeName)) {
this.submitNodeReportWithMedia()
return
}
const apiMap = { const apiMap = {
HOME_LIFT: reportHomeLiftOrderNode, HOME_LIFT: reportHomeLiftOrderNode,
CABLE_COP: reportCableCopTaskNode, CABLE_COP: reportCableCopTaskNode,
@ -456,16 +611,11 @@ export default {
return return
} }
this.reportLoading = true this.reportLoading = true
apiFn({
orderNo: this.reportData.orderNo,
nodeCode: this.reportData.nodeCode,
reportQty: this.reportData.reportQty,
remark: this.reportData.remark
}).then(({ data }) => {
apiFn(this.buildNodeReportPayload()).then(({ data }) => {
this.reportLoading = false this.reportLoading = false
if (data && data.code === 0) { if (data && data.code === 0) {
this.$message.success(data.msg || '报工成功') this.$message.success(data.msg || '报工成功')
this.reportDialogVisible = false
this.closeReportDialog()
this.getDataList() this.getDataList()
} else { } else {
this.$message.error((data && data.msg) || '报工失败') this.$message.error((data && data.msg) || '报工失败')
@ -475,16 +625,301 @@ export default {
this.$message.error('报工失败') this.$message.error('报工失败')
}) })
}, },
openHistoryDialog(item) {
submitNodeReportWithMedia () {
if (!this.capturedMediaList.length) {
this.$message.warning('请先上传检验照片或视频')
return
}
const formData = new FormData()
const payload = this.buildNodeReportPayload()
formData.append('orderNo', payload.orderNo)
formData.append('orderType', this.reportData.orderType || '')
formData.append('nodeCode', payload.nodeCode)
formData.append('reportQty', String(payload.reportQty || 1))
formData.append('remark', payload.remark || '')
this.capturedMediaList.forEach(item => {
formData.append('file', item.raw, item.name)
})
this.reportLoading = true
reportWorkNodeWithMedia(formData).then(({ data }) => {
this.reportLoading = false
if (data && data.code === 0) {
this.$message.success(data.msg || '上传并报工成功')
this.closeReportDialog()
this.getDataList()
} else {
this.$message.error((data && data.msg) || '上传失败')
}
}).catch(() => {
this.reportLoading = false
this.$message.error('上传失败')
})
},
buildNodeReportPayload () {
const reportQty = this.isQtyRequired(this.reportData.orderType) ? this.reportData.reportQty : 1
return {
orderNo: this.reportData.orderNo,
nodeCode: this.reportData.nodeCode,
reportQty: reportQty,
remark: this.reportData.remark || ''
}
},
async startCamera () {
if (!this.requiresMediaUpload(this.reportData.orderType, this.reportData.nodeCode, this.reportData.nodeName)) {
return
}
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
this.$message.error('当前设备不支持摄像头调用,请使用“系统相机”')
return
}
this.stopCamera(true)
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: {
facingMode: { ideal: 'environment' },
width: { ideal: 1280 },
height: { ideal: 720 }
},
audio: false
})
this.cameraStream = stream
this.cameraActive = true
const videoEl = this.$refs.cameraPreview
if (videoEl) {
videoEl.srcObject = stream
const playPromise = videoEl.play()
if (playPromise && typeof playPromise.then === 'function') {
playPromise.catch(() => {})
}
}
} catch (e) {
this.cameraActive = false
this.$message.error('摄像头打开失败,请检查权限或改用“系统相机”')
}
},
stopCamera (discardRecording = false) {
if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
if (discardRecording) {
this.mediaRecorder.ondataavailable = null
this.mediaRecorder.onstop = null
this.mediaRecorder.stop()
this.resetRecordingState()
} else {
this.stopVideoRecording(false)
}
}
if (this.cameraStream && this.cameraStream.getTracks) {
this.cameraStream.getTracks().forEach(track => track.stop())
}
this.cameraStream = null
this.cameraActive = false
const videoEl = this.$refs.cameraPreview
if (videoEl) {
videoEl.srcObject = null
}
},
capturePhoto () {
if (!this.cameraActive || !this.cameraStream) {
this.$message.warning('请先开启摄像头')
return
}
const videoEl = this.$refs.cameraPreview
if (!videoEl || !videoEl.videoWidth || !videoEl.videoHeight) {
this.$message.warning('摄像头画面尚未准备完成')
return
}
const canvas = document.createElement('canvas')
canvas.width = videoEl.videoWidth
canvas.height = videoEl.videoHeight
const ctx = canvas.getContext('2d')
if (!ctx) {
this.$message.error('无法采集照片,请稍后重试')
return
}
ctx.drawImage(videoEl, 0, 0, canvas.width, canvas.height)
canvas.toBlob(blob => {
if (!blob) {
this.$message.error('照片生成失败,请重试')
return
}
this.appendCapturedFile(blob, 'image', 'image/jpeg')
}, 'image/jpeg', 0.92)
},
toggleVideoRecording () {
if (this.recording) {
this.stopVideoRecording(true)
} else {
this.startVideoRecording()
}
},
startVideoRecording () {
if (!this.cameraActive || !this.cameraStream) {
this.$message.warning('请先开启摄像头')
return
}
if (typeof MediaRecorder === 'undefined') {
this.$message.warning('当前浏览器不支持录像,请使用“系统相机”')
return
}
const recorderOptions = this.resolveMediaRecorderOptions()
this.recordedChunks = []
try {
this.mediaRecorder = new MediaRecorder(this.cameraStream, recorderOptions)
} catch (e) {
this.$message.warning('当前设备录像能力受限,请使用“系统相机”')
return
}
this.mediaRecorder.ondataavailable = event => {
if (event.data && event.data.size > 0) {
this.recordedChunks.push(event.data)
}
}
this.mediaRecorder.onstop = () => {
if (!this.recordedChunks.length) {
this.resetRecordingState()
return
}
const mimeType = this.mediaRecorder && this.mediaRecorder.mimeType ? this.mediaRecorder.mimeType : 'video/webm'
const blob = new Blob(this.recordedChunks, { type: mimeType })
this.appendCapturedFile(blob, 'video', mimeType)
this.resetRecordingState()
}
this.mediaRecorder.start()
this.recording = true
this.$message.success('开始录像')
},
stopVideoRecording (showMessage) {
if (!this.mediaRecorder || this.mediaRecorder.state === 'inactive') {
return
}
this.mediaRecorder.stop()
this.recording = false
if (showMessage) {
this.$message.success('录像已结束')
}
},
resolveMediaRecorderOptions () {
if (typeof MediaRecorder === 'undefined' || typeof MediaRecorder.isTypeSupported !== 'function') {
return {}
}
const mimeTypeList = [
'video/webm;codecs=vp9',
'video/webm;codecs=vp8',
'video/webm',
'video/mp4'
]
for (let i = 0; i < mimeTypeList.length; i++) {
const mimeType = mimeTypeList[i]
if (MediaRecorder.isTypeSupported(mimeType)) {
return { mimeType: mimeType }
}
}
return {}
},
appendCapturedFile (blob, kind, mimeType) {
const name = this.createCaptureName(kind, mimeType)
const file = new File([blob], name, { type: mimeType || blob.type || '' })
const previewUrl = URL.createObjectURL(file)
this.capturedMediaList.push({
uid: `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
name: name,
kind: kind,
mimeType: file.type,
raw: file,
previewUrl: previewUrl
})
},
createCaptureName (kind, mimeType) {
const extMap = {
'image/jpeg': 'jpg',
'image/png': 'png',
'video/webm': 'webm',
'video/mp4': 'mp4'
}
const fallbackExt = kind === 'video' ? 'webm' : 'jpg'
const extension = extMap[mimeType] || fallbackExt
const now = new Date()
const dateText = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}${String(now.getHours()).padStart(2, '0')}${String(now.getMinutes()).padStart(2, '0')}${String(now.getSeconds()).padStart(2, '0')}`
const nodeCode = this.reportData.nodeCode || 'node'
const seq = String(this.captureIndex++).padStart(3, '0')
return `${nodeCode}_${kind}_${dateText}_${seq}.${extension}`
},
removeCapturedMedia (index) {
const target = this.capturedMediaList[index]
if (target && target.previewUrl) {
URL.revokeObjectURL(target.previewUrl)
}
this.capturedMediaList.splice(index, 1)
},
resetCapturedMedia () {
this.capturedMediaList.forEach(item => {
if (item && item.previewUrl) {
URL.revokeObjectURL(item.previewUrl)
}
})
this.capturedMediaList = []
},
resetRecordingState () {
this.mediaRecorder = null
this.recording = false
this.recordedChunks = []
},
triggerSystemCamera (mode) {
const refName = mode === 'video' ? 'cameraVideoInput' : 'cameraImageInput'
const inputEl = this.$refs[refName]
if (!inputEl) {
return
}
inputEl.value = ''
inputEl.click()
},
handleSystemCameraChange (event, expectedKind) {
const files = (event && event.target && event.target.files) ? Array.from(event.target.files) : []
if (!files.length) {
return
}
files.forEach(file => {
const previewUrl = URL.createObjectURL(file)
const fileKind = expectedKind || (file.type && file.type.indexOf('video/') === 0 ? 'video' : 'image')
const fileName = file.name || this.createCaptureName(fileKind, file.type || '')
this.capturedMediaList.push({
uid: `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
name: fileName,
kind: fileKind,
mimeType: file.type || '',
raw: file,
previewUrl: previewUrl
})
})
if (event && event.target) {
event.target.value = ''
}
},
isImageFile (item) {
return !!(item && item.mimeType && item.mimeType.indexOf('image/') === 0)
},
releaseMediaResources () {
this.stopCamera(true)
this.resetCapturedMedia()
this.resetRecordingState()
const inputRefList = ['cameraImageInput', 'cameraVideoInput']
inputRefList.forEach(refName => {
const inputEl = this.$refs[refName]
if (inputEl) {
inputEl.value = ''
}
})
},
openHistoryDialog (item) {
this.historyOrder = item this.historyOrder = item
this.historyDialogVisible = true this.historyDialogVisible = true
}, },
sizeChangeHandle(val) {
sizeChangeHandle (val) {
this.pageSize = val this.pageSize = val
this.pageIndex = 1 this.pageIndex = 1
this.getDataList() this.getDataList()
}, },
currentChangeHandle(val) {
currentChangeHandle (val) {
this.pageIndex = val this.pageIndex = val
this.getDataList() this.getDataList()
} }
@ -622,8 +1057,9 @@ export default {
.cards-grid { .cards-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fill, minmax(390px, 1fr));
grid-template-columns: repeat(auto-fill, 320px);
gap: 15px; gap: 15px;
justify-content: start;
} }
.report-card { .report-card {
@ -831,6 +1267,210 @@ export default {
font-size: 42px; font-size: 42px;
} }
.media-capture-tip {
font-size: 12px;
color: #909399;
margin-bottom: 8px;
}
.report-container >>> .report-node-dialog--media {
margin-top: 4vh !important;
width: min(1080px, 96vw) !important;
max-width: calc(100vw - 12px);
height: 85vh;
max-height: 85vh;
display: flex;
flex-direction: column;
}
.report-container >>> .report-node-dialog--media .el-dialog__header,
.report-container >>> .report-node-dialog--media .el-dialog__footer {
flex: 0 0 auto;
padding-left: 16px;
padding-right: 16px;
}
.report-container >>> .report-node-dialog--media .el-dialog__body {
flex: 1 1 auto;
min-height: 0;
overflow: hidden;
padding: 8px 16px 10px;
display: flex;
flex-direction: column;
}
.report-dialog-form {
width: 100%;
}
.report-container >>> .report-node-dialog--media .report-dialog-form {
flex: 1 1 auto;
min-height: 0;
display: flex;
flex-direction: column;
}
.report-container >>> .report-node-dialog--media .report-dialog-form > .el-row:last-child {
flex: 1 1 auto;
min-height: 0;
margin-bottom: 0;
}
.report-container >>> .report-node-dialog--media .report-dialog-form .el-form-item {
margin-bottom: 8px;
}
.media-layout {
display: grid;
grid-template-columns: minmax(0, 2.1fr) minmax(300px, 1fr);
gap: 12px;
align-items: stretch;
height: 100%;
min-height: 0;
}
.media-layout-left {
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
}
.media-layout-right {
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
}
.camera-preview-wrap {
position: relative;
width: 100%;
flex: 1 1 auto;
min-height: 480px;
height: auto;
border: 1px solid #dcdfe6;
border-radius: 4px;
overflow: hidden;
background: #000;
}
.camera-preview {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.camera-placeholder {
position: absolute;
inset: 0;
color: #e5e9f2;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
letter-spacing: 0.5px;
}
.camera-action-bar {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-top: 8px;
}
.camera-action-bar .el-button {
margin-left: 0 !important;
}
.camera-file-input {
display: none;
}
.capture-list-title {
margin: 0 0 8px 0;
font-size: 12px;
color: #606266;
}
.capture-list-panel {
flex: 1 1 auto;
min-height: 480px;
border: 1px solid #ebeef5;
border-radius: 4px;
padding: 8px;
overflow: hidden;
background: #fafbfc;
}
.capture-list {
display: grid;
grid-template-columns: 1fr;
grid-auto-rows: 168px;
gap: 8px;
height: 100%;
align-content: start;
overflow-y: auto;
}
.capture-item {
height: 168px;
min-height: 168px;
display: flex;
flex-direction: column;
border: 1px solid #ebeef5;
border-radius: 4px;
overflow: hidden;
background: #fff;
}
.capture-thumb {
width: 100%;
height: 100px;
display: block;
object-fit: cover;
background: #000;
}
.capture-meta {
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
padding: 6px 8px 2px;
}
.capture-name {
font-size: 12px;
color: #606266;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.capture-remove {
margin-top: 2px;
padding: 4px 8px 2px 4px;
line-height: 1;
text-align: left;
}
.empty-capture-list {
text-align: center;
color: #909399;
padding: 36px 12px;
}
.empty-capture-list i {
font-size: 34px;
}
.empty-capture-list p {
margin: 8px 0 0 0;
font-size: 12px;
}
.history-table >>> .el-table__header-wrapper th, .history-table >>> .el-table__header-wrapper th,
.history-table >>> .el-table__header-wrapper .el-table__cell { .history-table >>> .el-table__header-wrapper .el-table__cell {
background-color: #F5F7FA !important; background-color: #F5F7FA !important;
@ -868,6 +1508,46 @@ export default {
.cards-grid { .cards-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.report-container >>> .report-node-dialog--media {
width: 98% !important;
margin-top: 1vh !important;
height: 95vh;
max-height: 95vh;
}
.report-container >>> .report-node-dialog--media .el-dialog__header,
.report-container >>> .report-node-dialog--media .el-dialog__footer {
padding-left: 10px;
padding-right: 10px;
}
.report-container >>> .report-node-dialog--media .el-dialog__body {
padding: 8px 10px;
}
.report-container >>> .report-node-dialog--media .report-dialog-form > .el-row:first-child .el-col {
width: 100%;
}
.media-layout {
grid-template-columns: 1fr;
gap: 8px;
}
.media-layout-right {
margin-top: 0;
}
.capture-list-panel {
min-height: 340px;
}
.camera-preview-wrap {
flex: 0 0 auto;
height: 400px;
min-height: 340px;
}
} }
.status-info { .status-info {
@ -892,4 +1572,45 @@ export default {
border-color: #909399; border-color: #909399;
color: #fff; color: #fff;
} }
/* 拍照按钮 */
.camera-btn {
font-size: 16px !important;
padding: 10px 16px !important;
border-radius: 6px;
color: #409EFF !important;
border-color: #409EFF !important;
background-color: #ecf5ff !important;
}
.camera-btn i {
font-size: 18px !important;
}
/* 悬停效果 */
.camera-btn:hover {
background-color: #409EFF !important;
color: #fff !important;
}
/* 录像按钮 */
.video-btn {
font-size: 16px !important;
padding: 10px 16px !important;
border-radius: 6px;
color: #F56C6C !important;
border-color: #F56C6C !important;
background-color: #fef0f0 !important;
}
.video-btn i {
font-size: 18px !important;
}
/* 悬停效果 */
.video-btn:hover {
background-color: #F56C6C !important;
color: #fff !important;
}
</style> </style>

4
src/views/modules/longtron/screen-cable-cop-progress.vue

@ -55,10 +55,10 @@ export default {
return { loading: false, boardTimerId: null,tableHeight: 640, currentTime: '', timerId: null, boardList: [], kpi: { total: 0, processing: 0, finished: 0, finishRate: 0 } } return { loading: false, boardTimerId: null,tableHeight: 640, currentTime: '', timerId: null, boardList: [], kpi: { total: 0, processing: 0, finished: 0, finishRate: 0 } }
}, },
mounted() { this.setTableHeight(); this.loadBoardData(); this.updateTime(); this.timerId = setInterval(this.updateTime, 1000); mounted() { this.setTableHeight(); this.loadBoardData(); this.updateTime(); this.timerId = setInterval(this.updateTime, 1000);
// 10
// 60
this.boardTimerId = setInterval(() => { this.boardTimerId = setInterval(() => {
this.loadBoardData() this.loadBoardData()
}, 10000)
}, 60000)
window.addEventListener('resize', this.setTableHeight) }, window.addEventListener('resize', this.setTableHeight) },
beforeDestroy() { if (this.timerId) clearInterval(this.timerId); beforeDestroy() { if (this.timerId) clearInterval(this.timerId);

2
src/views/modules/longtron/screen-machining-progress.vue

@ -53,7 +53,7 @@ export default {
this.timerId = setInterval(this.updateTime, 1000) this.timerId = setInterval(this.updateTime, 1000)
this.boardTimerId = setInterval(() => { this.boardTimerId = setInterval(() => {
this.loadBoardData() this.loadBoardData()
}, 10000)
}, 60000)
window.addEventListener('resize', this.setTableHeight) window.addEventListener('resize', this.setTableHeight)
}, },
beforeDestroy() { beforeDestroy() {

4
src/views/modules/longtron/screen-renovation-progress.vue

@ -46,10 +46,10 @@ export default {
return { loading: false, boardTimerId: null, tableHeight: 640, currentTime: '', timerId: null, boardList: [], kpi: { total: 0, processing: 0, finished: 0, finishRate: 0 } } return { loading: false, boardTimerId: null, tableHeight: 640, currentTime: '', timerId: null, boardList: [], kpi: { total: 0, processing: 0, finished: 0, finishRate: 0 } }
}, },
mounted() { this.setTableHeight(); this.loadBoardData(); this.updateTime(); this.timerId = setInterval(this.updateTime, 1000); mounted() { this.setTableHeight(); this.loadBoardData(); this.updateTime(); this.timerId = setInterval(this.updateTime, 1000);
// 10
// 60
this.boardTimerId = setInterval(() => { this.boardTimerId = setInterval(() => {
this.loadBoardData() this.loadBoardData()
}, 10000)
}, 60000)
window.addEventListener('resize', this.setTableHeight) }, window.addEventListener('resize', this.setTableHeight) },
beforeDestroy() { if (this.timerId) clearInterval(this.timerId); beforeDestroy() { if (this.timerId) clearInterval(this.timerId);

4
src/views/modules/longtron/screen-whole-lift-progress.vue

@ -65,10 +65,10 @@ export default {
this.updateTime() this.updateTime()
this.timerId = setInterval(this.updateTime, 1000) this.timerId = setInterval(this.updateTime, 1000)
// 10
// 60
this.boardTimerId = setInterval(() => { this.boardTimerId = setInterval(() => {
this.loadBoardData() this.loadBoardData()
}, 10000)
}, 60000)
window.addEventListener('resize', this.setTableHeight) window.addEventListener('resize', this.setTableHeight)
}, },

Loading…
Cancel
Save