|
|
@@ -18,6 +18,31 @@
|
|
|
<el-button size="small" plain style="margin-left: 8px;" @click="onRefreshPointList" v-log="{ describe: { action: '点击刷新点位列表' } }">刷新</el-button>
|
|
|
</el-form-item>
|
|
|
|
|
|
+ <template v-if="!disablePointConfig">
|
|
|
+ <el-form-item v-for="item in cameraPropDefs" :key="item.key" :label="item.label">
|
|
|
+ <el-select
|
|
|
+ v-model="cameraForm[item.key]"
|
|
|
+ filterable
|
|
|
+ clearable
|
|
|
+ placeholder="不修改"
|
|
|
+ style="width: 108px;"
|
|
|
+ :loading="cameraPropLoading"
|
|
|
+ >
|
|
|
+ <el-option label="不修改" value="" />
|
|
|
+ <el-option v-for="opt in optionsFor(item.key)" :key="opt" :label="opt" :value="opt" />
|
|
|
+ </el-select>
|
|
|
+ <el-button
|
|
|
+ v-if="item.key === 'ISO'"
|
|
|
+ size="small"
|
|
|
+ plain
|
|
|
+ style="margin-left: 8px;"
|
|
|
+ :loading="cameraPropLoading"
|
|
|
+ @click="fetchCameraProperties"
|
|
|
+ v-log="{ describe: { action: '点击读取当前点位相机参数' } }"
|
|
|
+ >读取</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </template>
|
|
|
+
|
|
|
<!-- 多相机模式 - 仅拍照模式 -->
|
|
|
<!-- <el-form-item label="是否移动设备" v-if="isMultiCameraMode">
|
|
|
<div class="flex-row">
|
|
|
@@ -109,15 +134,16 @@
|
|
|
<div class="btn-row mar-top-20">
|
|
|
<div class="normal-btn" @click="refreshData" v-if="id" v-log="{ describe: { action: '点击刷新配置' } }">刷新</div>
|
|
|
<div class="normal-btn" @click="close" v-if="id" v-log="{ describe: { action: '点击关闭编辑行' } }">取消</div>
|
|
|
- <div class="normal-btn" v-loading="captureLoading" v-if="!id && editRowData.is_system" @click="testShoesFlip" v-log="{ describe: { action: '点击运行测试拍照' } }">运行</div>
|
|
|
- <div class="primary-btn" v-loading="captureLoading" @click="saveRow" v-log="{ describe: { action: '点击保存设备配置' } }">{{ id ? '保存并关闭' : '保存' }}</div>
|
|
|
+ <div class="normal-btn" @click="emit('onBackPreview')" v-if="!disablePointConfig && showingTestPhoto" v-log="{ describe: { action: '点击返回预览' } }">回预览</div>
|
|
|
+ <div class="normal-btn" v-loading="captureLoading" v-if="showRunButton" @click="onRunClick" v-log="{ describe: { action: '点击运行测试拍照' } }">拍照</div>
|
|
|
+ <div class="primary-btn" v-loading="captureLoading" @click="saveRow" v-log="{ describe: { action: '点击保存设备配置' } }">{{ id ? '保存关闭' : '保存' }}</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, defineProps, defineEmits , watch, onMounted, computed } from 'vue'
|
|
|
+import { ref, reactive, defineProps, defineEmits , watch, onMounted, computed } from 'vue'
|
|
|
import icpList from '@/utils/ipc';
|
|
|
import { digiCamControlWEB } from '@/utils/appconfig'
|
|
|
import {ElMessage} from "element-plus";
|
|
|
@@ -138,12 +164,149 @@ const isMultiCameraMode = computed(() => userInfoStore.isMultiCameraMode)
|
|
|
|
|
|
// 已配置相机的点位列表
|
|
|
const availablePoints = ref<string[]>(['A'])
|
|
|
+let cachedConfigs: any = null
|
|
|
+
|
|
|
+const cameraPropDefs = [
|
|
|
+ { key: 'ISO', label: 'ISO' },
|
|
|
+ { key: 'Aperture', label: '光圈' },
|
|
|
+ { key: 'ShutterSpeed', label: '快门' },
|
|
|
+ { key: 'Exposure', label: '曝光补偿' },
|
|
|
+]
|
|
|
+const cameraForm = reactive({
|
|
|
+ ISO: '',
|
|
|
+ Aperture: '',
|
|
|
+ ShutterSpeed: '',
|
|
|
+ Exposure: '',
|
|
|
+})
|
|
|
+const cameraRanges = reactive({
|
|
|
+ ISO: [] as string[],
|
|
|
+ Aperture: [] as string[],
|
|
|
+ ShutterSpeed: [] as string[],
|
|
|
+ Exposure: [] as string[],
|
|
|
+})
|
|
|
+const cameraPropLoading = ref(false)
|
|
|
+let cameraPropRequest = 0
|
|
|
+
|
|
|
+function isWritable(item) {
|
|
|
+ const flag = item?.CameraPropertyIsWriteable
|
|
|
+ if (flag == null) return true
|
|
|
+ return flag === true || flag === 1 || flag === '1' || flag === 'true' || flag === 'True'
|
|
|
+}
|
|
|
+
|
|
|
+function optionsFor(key) {
|
|
|
+ const range = cameraRanges[key] || []
|
|
|
+ const current = cameraForm[key]
|
|
|
+ if (current && !range.includes(current)) return [current, ...range]
|
|
|
+ return range
|
|
|
+}
|
|
|
+
|
|
|
+function parseCameraConfigs(raw) {
|
|
|
+ if (!raw) return {}
|
|
|
+ if (typeof raw === 'string') {
|
|
|
+ try {
|
|
|
+ const parsed = JSON.parse(raw)
|
|
|
+ return parsed && typeof parsed === 'object' ? parsed : {}
|
|
|
+ } catch (error) {
|
|
|
+ return {}
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (typeof raw === 'object') return raw
|
|
|
+ return {}
|
|
|
+}
|
|
|
+
|
|
|
+function applyCameraConfigs(raw) {
|
|
|
+ const saved = parseCameraConfigs(raw)
|
|
|
+ cameraPropDefs.forEach(({ key }) => {
|
|
|
+ cameraForm[key] = saved[key] != null && saved[key] !== '' ? String(saved[key]) : ''
|
|
|
+ })
|
|
|
+ return Object.keys(saved).length > 0
|
|
|
+}
|
|
|
+
|
|
|
+function getCameraConfigs() {
|
|
|
+ const payload = {}
|
|
|
+ cameraPropDefs.forEach(({ key }) => {
|
|
|
+ if (cameraForm[key] !== '' && cameraForm[key] != null) {
|
|
|
+ payload[key] = String(cameraForm[key])
|
|
|
+ }
|
|
|
+ })
|
|
|
+ return Object.keys(payload).length ? payload : undefined
|
|
|
+}
|
|
|
+
|
|
|
+function applySavedCamera(row, fallbackId) {
|
|
|
+ const nested = parseCameraConfigs(row?.camera_configs)
|
|
|
+ if (Object.keys(nested).length) {
|
|
|
+ applyCameraConfigs(nested)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const flat = {}
|
|
|
+ cameraPropDefs.forEach(({ key }) => {
|
|
|
+ if (row && row[key] != null && row[key] !== '') flat[key] = row[key]
|
|
|
+ })
|
|
|
+ if (Object.keys(flat).length) {
|
|
|
+ applyCameraConfigs(flat)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (row && Object.prototype.hasOwnProperty.call(row, 'camera_configs')) {
|
|
|
+ applyCameraConfigs(null)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (fallbackId) {
|
|
|
+ applyCameraConfigs(cachedConfigs?.camera_configs?.step_camera?.[String(fallbackId)])
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function fetchCameraProperties() {
|
|
|
+ if (!clientStore.isClient || props.disablePointConfig) return
|
|
|
+ const requestId = ++cameraPropRequest
|
|
|
+ const point = editRowData.value.point_name || 'A'
|
|
|
+ cameraPropLoading.value = true
|
|
|
+ const channel = icpList.socket.message + '_smart_shooter_get_camera_property'
|
|
|
+ const timer = setTimeout(() => {
|
|
|
+ if (requestId !== cameraPropRequest) return
|
|
|
+ cameraPropLoading.value = false
|
|
|
+ clientStore.ipc.removeAllListeners(channel)
|
|
|
+ ElMessage.warning('读取相机参数超时,请再点一次读取')
|
|
|
+ }, 8000)
|
|
|
+ clientStore.ipc.removeAllListeners(channel)
|
|
|
+ clientStore.ipc.on(channel, (_event, result) => {
|
|
|
+ if (requestId !== cameraPropRequest) return
|
|
|
+ clearTimeout(timer)
|
|
|
+ cameraPropLoading.value = false
|
|
|
+ clientStore.ipc.removeAllListeners(channel)
|
|
|
+ if (result?.code !== 0 || !Array.isArray(result.data)) {
|
|
|
+ ElMessage.warning(typeof result?.msg === 'string' ? result.msg : '未获取到当前点位的相机参数')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const dropped = []
|
|
|
+ cameraPropDefs.forEach(({ key, label }) => {
|
|
|
+ const item = result.data.find((prop) => prop?.CameraPropertyType === key)
|
|
|
+ const range = item && isWritable(item) && Array.isArray(item.CameraPropertyRange)
|
|
|
+ ? item.CameraPropertyRange.map((opt) => String(opt))
|
|
|
+ : []
|
|
|
+ cameraRanges[key] = range
|
|
|
+ if (cameraForm[key] && range.length && !range.includes(String(cameraForm[key]))) {
|
|
|
+ cameraForm[key] = ''
|
|
|
+ dropped.push(label)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ if (dropped.length) {
|
|
|
+ ElMessage.warning(`${dropped.join('、')}不在当前点位相机范围内,已清空`)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ socketStore.sendMessage({
|
|
|
+ type: 'smart_shooter_get_camera_property',
|
|
|
+ data: { point_name: point }
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
// 刷新点位列表(从 getAllUserConfigs API 获取已绑定相机的点位)
|
|
|
// 同时会校验当前选中的点位:若该点位已不再可用,自动回退到第一个可用点位
|
|
|
const refreshPointList = async () => {
|
|
|
try {
|
|
|
const result = await getAllUserConfigs({})
|
|
|
+ if (result.code == 0) {
|
|
|
+ cachedConfigs = result.data?.configs || {}
|
|
|
+ }
|
|
|
if (result.code == 0 && result.data?.configs?.camera_configs?.iso_config) {
|
|
|
const isoConfig = result.data.configs.camera_configs.iso_config
|
|
|
availablePoints.value = Object.keys(isoConfig).filter(key => {
|
|
|
@@ -186,6 +349,7 @@ const onRefreshPointList = async () => {
|
|
|
} else if (previousPoint !== editRowData.value.point_name && availablePoints.value[0]) {
|
|
|
emit('onPointChange', availablePoints.value[0])
|
|
|
}
|
|
|
+ fetchCameraProperties()
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -214,6 +378,10 @@ const props = defineProps({
|
|
|
disablePointConfig: {
|
|
|
type: Boolean,
|
|
|
default: false
|
|
|
+ },
|
|
|
+ showingTestPhoto: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
}
|
|
|
})
|
|
|
|
|
|
@@ -226,8 +394,9 @@ const maxFocalDistance = ref(8000); // 相机焦段距离最大值,默认8000
|
|
|
|
|
|
// 监听点位变化,切换预览
|
|
|
watch(() => editRowData.value.point_name, (newPoint, oldPoint) => {
|
|
|
- if (initStatus.value && newPoint && newPoint !== oldPoint) {
|
|
|
+ if (initStatus.value && newPoint && oldPoint && newPoint !== oldPoint) {
|
|
|
emit('onPointChange', newPoint)
|
|
|
+ fetchCameraProperties()
|
|
|
}
|
|
|
})
|
|
|
|
|
|
@@ -266,7 +435,6 @@ onMounted(async ()=>{
|
|
|
await refreshPointList()
|
|
|
if(props.addRowData.mode_type){
|
|
|
console.log(props.addRowData);
|
|
|
- initStatus.value = true
|
|
|
editRowData.value = props.addRowData;
|
|
|
// 多相机模式默认值
|
|
|
if (isMultiCameraMode.value) {
|
|
|
@@ -276,8 +444,13 @@ onMounted(async ()=>{
|
|
|
}
|
|
|
editRowData.value.is_move_device = editRowData.value.is_move_device ?? 1
|
|
|
}
|
|
|
+ if (props.addRowData.copy_from_id || props.addRowData.camera_configs) {
|
|
|
+ applySavedCamera(props.addRowData, props.addRowData.copy_from_id)
|
|
|
+ }
|
|
|
+ initStatus.value = true
|
|
|
testShoesFlip()
|
|
|
emit('onReady', editRowData.value.point_name || 'A')
|
|
|
+ fetchCameraProperties()
|
|
|
return
|
|
|
}
|
|
|
let fun = getDeviceConfigDetail;
|
|
|
@@ -308,9 +481,13 @@ onMounted(async ()=>{
|
|
|
}
|
|
|
editRowData.value.is_move_device = editRowData.value.is_move_device ?? 1
|
|
|
}
|
|
|
+ if (props.id) {
|
|
|
+ applySavedCamera(editRowData.value, props.id)
|
|
|
+ }
|
|
|
initStatus.value = true;
|
|
|
testShoesFlip()
|
|
|
emit('onReady', editRowData.value.point_name || 'A')
|
|
|
+ fetchCameraProperties()
|
|
|
}
|
|
|
|
|
|
})
|
|
|
@@ -359,6 +536,14 @@ async function changeNum(type, min, max) {
|
|
|
|
|
|
/*测试拍照*/
|
|
|
const captureLoading = ref(false)
|
|
|
+const showRunButton = computed(() => !props.disablePointConfig || (!props.id && editRowData.value.is_system))
|
|
|
+function onRunClick() {
|
|
|
+ if (!props.disablePointConfig) {
|
|
|
+ emit('onTestPhoto')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ testShoesFlip()
|
|
|
+}
|
|
|
function testShoesFlip(){
|
|
|
if (captureLoading.value) {
|
|
|
return
|
|
|
@@ -425,7 +610,7 @@ function testShoesFlip(){
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-const emit = defineEmits([ 'confirm','onClose','onRunMcuSingle','onReady','onPointChange']);
|
|
|
+const emit = defineEmits([ 'confirm','onClose','onRunMcuSingle','onReady','onPointChange','onTestPhoto','onBackPreview']);
|
|
|
const close = ()=>{
|
|
|
console.log('onClose')
|
|
|
emit('onClose')
|
|
|
@@ -454,6 +639,9 @@ const refreshData = async () => {
|
|
|
}
|
|
|
editRowData.value.is_move_device = editRowData.value.is_move_device ?? 1
|
|
|
}
|
|
|
+ await refreshPointList()
|
|
|
+ applySavedCamera(editRowData.value, props.id)
|
|
|
+ fetchCameraProperties()
|
|
|
ElMessage.success('刷新成功')
|
|
|
} else {
|
|
|
ElMessage.error('刷新失败')
|
|
|
@@ -493,6 +681,13 @@ const saveRow = async () => {
|
|
|
const saveData = {
|
|
|
...editRowData.value,
|
|
|
}
|
|
|
+ delete saveData.copy_from_id
|
|
|
+ cameraPropDefs.forEach(({ key }) => {
|
|
|
+ delete saveData[key]
|
|
|
+ })
|
|
|
+ if (!props.disablePointConfig) {
|
|
|
+ saveData.camera_configs = getCameraConfigs() || null
|
|
|
+ }
|
|
|
|
|
|
// 多相机模式添加点位信息
|
|
|
if (isMultiCameraMode.value) {
|
|
|
@@ -512,6 +707,7 @@ const saveRow = async () => {
|
|
|
// 暴露给父组件
|
|
|
defineExpose({
|
|
|
editRowData, // 父组件可通过 editData.value.editRowData 访问
|
|
|
+ getCameraConfigs,
|
|
|
});
|
|
|
</script>
|
|
|
<style>
|
|
|
@@ -519,6 +715,10 @@ defineExpose({
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.editrow_wrap {
|
|
|
+ width: 330px;
|
|
|
+ flex: 0 0 330px;
|
|
|
+ max-height: 100vh;
|
|
|
+ overflow-y: auto;
|
|
|
}
|
|
|
.config-type{
|
|
|
font-size: 16px;
|
|
|
@@ -606,30 +806,43 @@ defineExpose({
|
|
|
.btn-row{
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- justify-content: center;
|
|
|
- gap: 10px;
|
|
|
+ justify-content: stretch;
|
|
|
+ gap: 6px;
|
|
|
+ width: 100%;
|
|
|
+ padding: 0 8px;
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
|
.primary-btn{
|
|
|
- width: 100px;
|
|
|
+ flex: 1 1 0;
|
|
|
+ width: auto;
|
|
|
+ min-width: 0;
|
|
|
height: 30px;
|
|
|
+ padding: 0 2px;
|
|
|
background: linear-gradient( 135deg, #2FB0FF 0%, #B863FB 100%);
|
|
|
border-radius: 4px;
|
|
|
color: #fff;
|
|
|
- font-size: 14px;
|
|
|
+ font-size: 12px;
|
|
|
text-align: center;
|
|
|
line-height: 30px;
|
|
|
+ white-space: nowrap;
|
|
|
cursor: pointer;
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
|
.normal-btn{
|
|
|
- width: 100px;
|
|
|
+ flex: 1 1 0;
|
|
|
+ width: auto;
|
|
|
+ min-width: 0;
|
|
|
height: 30px;
|
|
|
+ padding: 0 2px;
|
|
|
background: #fff;
|
|
|
border: 1px solid #CCCCCC;
|
|
|
border-radius: 4px;
|
|
|
- font-size: 14px;
|
|
|
+ font-size: 12px;
|
|
|
text-align: center;
|
|
|
line-height: 30px;
|
|
|
+ white-space: nowrap;
|
|
|
cursor: pointer;
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
.mode-hint {
|