|
@@ -0,0 +1,442 @@
|
|
|
+<template>
|
|
|
+ <div class="config">
|
|
|
+ <el-menu default-active="2" class="custom-menu">
|
|
|
+ <el-menu-item index="2">
|
|
|
+ <el-icon><icon-menu /></el-icon>
|
|
|
+ <template #title>视频灯检测</template>
|
|
|
+ </el-menu-item>
|
|
|
+ <!-- <el-menu-item index="1">
|
|
|
+ <el-icon><MoreFilled /></el-icon>
|
|
|
+ <template #title>电流电压检测</template>
|
|
|
+ </el-menu-item> -->
|
|
|
+
|
|
|
+ </el-menu>
|
|
|
+ <div class="right-content">
|
|
|
+ <el-steps style="margin-top: 10px;" class="step-content" finish-status="success" :space="200" :active="activeStep"
|
|
|
+ simple>
|
|
|
+ <el-step title="选择视频点位" />
|
|
|
+ <el-step title="选择算法" />
|
|
|
+ <el-step title="点位设备关联" />
|
|
|
+ <el-step title="在线检测" />
|
|
|
+ </el-steps>
|
|
|
+ <div class="center-content">
|
|
|
+ <div v-if="activeStep === 0" class="camera">
|
|
|
+ <div class="camera-op">
|
|
|
+ <el-select v-model="camera" placeholder="请选择" clearable style="width: 180px" size="small">
|
|
|
+ <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
+ </el-select>
|
|
|
+ <el-button type="primary" size="small" @click="showCamera">获取画面</el-button>
|
|
|
+ </div>
|
|
|
+ <div class="camera-content">
|
|
|
+ <img v-if="cameraImgShow" class="camera-img" src="@/assets/images/camera.png" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else-if="activeStep === 1" class="camera">
|
|
|
+ <div class="camera-op">
|
|
|
+ <el-select v-model="algorithm" placeholder="请选择算法" clearable style="width: 180px" size="small">
|
|
|
+ <el-option v-for="item in algorithmOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
+ </el-select>
|
|
|
+ <el-button type="primary" size="small" @click="showCameraData">获取位置读数</el-button>
|
|
|
+ </div>
|
|
|
+ <div class="camera-content">
|
|
|
+ <img v-if="!cameraDataShow" class="camera-img" src="@/assets/images/camera.png" />
|
|
|
+ <img v-else class="camera-img" src="@/assets/images/camera-data.png" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div v-else-if="activeStep === 2">
|
|
|
+ <el-table :data="tableData">
|
|
|
+ <el-table-column label="id" align="center" prop="id" width="80"/>
|
|
|
+ <el-table-column label="位置" align="center" prop="position" />
|
|
|
+ <el-table-column label="算法" align="center" show-overflow-tooltip width="150" >
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ algorithmOptions.filter(item => item.value == row.algorithmId)[0]?.label }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="设备类型" align="center" prop="deviceType">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ deviceTypeOptions.filter(item => item.id == row.deviceType)[0]?.name }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="品牌型号" align="center" prop="brandXh">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ deviceTypeDetailOptions.filter(item => item.id == row.brandXh)[0]?.ext1.brand }}{{ deviceTypeDetailOptions.filter(item => item.id == row.brandXh)[0]?.xh }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="设备" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ deviceOptions.filter(item => item.id == row.device)[0]?.name }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="参数" align="center" prop="param" />
|
|
|
+ <el-table-column label="状态" align="center" prop="status">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-tag v-if="scope.row.status === '1'" type="info">未配</el-tag>
|
|
|
+ <el-tag v-else type="success">已配</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" align="center" width="100" fixed="right">
|
|
|
+ <template #default="{row,$index}">
|
|
|
+ <el-button size="small" link type="primary" @click="showDetail(row)">详情</el-button>
|
|
|
+ <el-button v-if="row.status == 1" size="small" link type="primary"
|
|
|
+ @click="handleEdit(row,$index)">编辑</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ <div v-else-if="activeStep === 3">
|
|
|
+ <el-table :data="tableData">
|
|
|
+ <el-table-column label="id" align="center" prop="id" width="80"/>
|
|
|
+ <el-table-column label="位置" align="center" prop="position" />
|
|
|
+ <el-table-column label="算法" align="center" show-overflow-tooltip width="150" >
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ algorithmOptions.filter(item => item.value == row.algorithmId)[0]?.label }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="设备类型" align="center" prop="deviceType">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ deviceTypeOptions.filter(item => item.id == row.deviceType)[0]?.name }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="品牌型号" align="center" prop="brandXh">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ deviceTypeDetailOptions.filter(item => item.id == row.brandXh)[0]?.ext1.brand }}{{ deviceTypeDetailOptions.filter(item => item.id == row.brandXh)[0]?.xh }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="设备" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ {{ deviceOptions.filter(item => item.id == row.device)[0]?.name }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="参数" align="center" prop="param" />
|
|
|
+ <el-table-column label="读数" align="center" prop="reading" />
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 信息对话框 -->
|
|
|
+ <el-dialog v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body>
|
|
|
+ <el-form ref="addFormRef" :model="form" class="custom-form" label-width="80px" :disabled="detailDisabled">
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="位置" prop="position"
|
|
|
+ :rules="[{ required: true, message: '位置不能为空', trigger: 'blur' }]">
|
|
|
+ <el-input v-model="form.position" placeholder="请输入位置" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="算法" prop="algorithmId"
|
|
|
+ :rules="[{ required: true, message: '算法不能为空', trigger: 'change' }]">
|
|
|
+ <el-select v-model="form.algorithmId" placeholder="请选择算法" clearable>
|
|
|
+ <el-option v-for="item in algorithmOptions" :key="item.value" :label="item.label"
|
|
|
+ :value="item.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="设备类型" prop="deviceType"
|
|
|
+ :rules="[{ required: true, message: '设备类型不能为空', trigger: 'change' }]">
|
|
|
+ <el-select v-model="form.deviceType" clearable placeholder="请选择设备类型" @change="form.brandXh = ''">
|
|
|
+ <el-option v-for="dict in deviceTypeOptions" :key="dict.id" :label="dict.name" :value="dict.id">
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="品牌型号" prop="brandXh"
|
|
|
+ :rules="[{ required: true, message: '品牌型号不能为空', trigger: 'change' }]">
|
|
|
+ <el-select v-model="form.brandXh" clearable placeholder="请选择品牌型号">
|
|
|
+ <el-option
|
|
|
+ v-for="dict in deviceTypeDetailOptions.filter(item => item.deviceTypeId === form.deviceType)"
|
|
|
+ :key="dict.id" :label="`${dict.ext1.brand}${dict.xh}`" :value="dict.id">
|
|
|
+ </el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="设备" prop="device"
|
|
|
+ :rules="[{ required: true, message: '设备不能为空', trigger: 'change' }]">
|
|
|
+ <el-select v-model="form.device" placeholder="请选择设备" clearable>
|
|
|
+ <el-option v-for="item in deviceOptions" :key="item.id" :label="item.name" :value="item.id" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="参数" prop="param">
|
|
|
+ <el-input v-model="form.param" placeholder="请输入" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="状态" prop="status"
|
|
|
+ :rules="[{ required: true, message: '状态不能为空', trigger: 'change' }]">
|
|
|
+ <el-select v-model="form.status" placeholder="请选择状态" clearable>
|
|
|
+ <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ </el-form>
|
|
|
+ <template #footer>
|
|
|
+ <div class="dialog-footer">
|
|
|
+ <el-button :loading="buttonLoading" type="primary" @click="submitForm"> 确 定 </el-button>
|
|
|
+ <el-button @click="cancel"> 取 消 </el-button>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+ <div class="bottom-btn">
|
|
|
+ <el-button v-if="activeStep > 0" @click="lastStep">上一步</el-button>
|
|
|
+ <el-button v-if="activeStep < 3" type="primary" @click="nextStep">下一步</el-button>
|
|
|
+ <el-button v-else type="primary" @click="confirmClick">完成</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+<script setup lang="ts">
|
|
|
+import {
|
|
|
+ Menu as IconMenu,
|
|
|
+} from '@element-plus/icons-vue'
|
|
|
+import { listDevice } from '@/api/deviceManage/device';
|
|
|
+import { listDeviceType, getDeviceTypeDetailList } from '@/api/deviceManage/deviceType';
|
|
|
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
+const addFormRef = ref<ElFormInstance>()
|
|
|
+const buttonLoading = ref(false);
|
|
|
+const form = ref({
|
|
|
+ position: undefined,
|
|
|
+ algorithmId: undefined,
|
|
|
+ deviceType: undefined,
|
|
|
+ brandXh: undefined,
|
|
|
+ device: undefined,
|
|
|
+ param: undefined,
|
|
|
+ status: undefined,
|
|
|
+} as any);
|
|
|
+const dialog = ref({
|
|
|
+ visible: false,
|
|
|
+ title: '',
|
|
|
+});
|
|
|
+const camera = ref('')
|
|
|
+const algorithm = ref('')
|
|
|
+const activeStep = ref(0)
|
|
|
+const cameraImgShow = ref(false)
|
|
|
+const cameraDataShow = ref(false)
|
|
|
+const deviceTypeOptions = ref([])
|
|
|
+const deviceTypeDetailOptions = ref([])
|
|
|
+const deviceOptions = ref([])
|
|
|
+const curInd = ref(0)
|
|
|
+const detailDisabled = ref(false)
|
|
|
+const options = [
|
|
|
+ {
|
|
|
+ value: '2',
|
|
|
+ label: '摄像头001',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: '2',
|
|
|
+ label: '摄像头002',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: '3',
|
|
|
+ label: '摄像头003',
|
|
|
+ },
|
|
|
+]
|
|
|
+const algorithmOptions = [
|
|
|
+ {
|
|
|
+ value: '1',
|
|
|
+ label: '表针表计识别',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: '2',
|
|
|
+ label: 'LED表计识别',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: '3',
|
|
|
+ label: '指示灯状态识别',
|
|
|
+ },
|
|
|
+]
|
|
|
+const statusOptions = [
|
|
|
+ {
|
|
|
+ value: '1',
|
|
|
+ label: '未配',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: '2',
|
|
|
+ label: '已配',
|
|
|
+ },
|
|
|
+]
|
|
|
+const tableData = ref([])
|
|
|
+const showCamera = () => {
|
|
|
+ if (!camera.value) return proxy?.$modal.msgError('请先选择摄像头')
|
|
|
+ cameraImgShow.value = true
|
|
|
+}
|
|
|
+const showCameraData = () => {
|
|
|
+ if (!algorithm.value) return proxy?.$modal.msgError('请先选择算法')
|
|
|
+ cameraDataShow.value = true
|
|
|
+}
|
|
|
+const nextStep = () => {
|
|
|
+ if (activeStep.value === 0) {
|
|
|
+ if (!camera.value||!cameraImgShow.value) return proxy?.$modal.msgError('请先获取画面')
|
|
|
+ }
|
|
|
+ if (activeStep.value === 1) {
|
|
|
+ if (!algorithm.value) return proxy?.$modal.msgError('请先选择算法')
|
|
|
+ if (!cameraDataShow.value) return proxy?.$modal.msgError('请获取位置读数')
|
|
|
+ const tmpArr = [];
|
|
|
+ for (let i = 0; i < 3; i++) {
|
|
|
+ tmpArr.push({
|
|
|
+ id: `${i + 1}`,
|
|
|
+ position: `L-000${i + 1}`,
|
|
|
+ algorithmId: algorithm.value,
|
|
|
+ deviceType: '',
|
|
|
+ brandXh: '',
|
|
|
+ device: '',
|
|
|
+ param: '',
|
|
|
+ status: '1'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ tableData.value = tmpArr
|
|
|
+ }
|
|
|
+ if (activeStep.value === 2) {
|
|
|
+ if (tableData.value.some(item => item.status === '1')) return proxy?.$modal.msgError('请先配置设备')
|
|
|
+ tableData.value = tableData.value.map(item => ({
|
|
|
+ ...item,
|
|
|
+ reading: '200V'
|
|
|
+ }))
|
|
|
+ }
|
|
|
+ activeStep.value++
|
|
|
+}
|
|
|
+const lastStep = () => {
|
|
|
+ activeStep.value--
|
|
|
+}
|
|
|
+const confirmClick = () => {
|
|
|
+
|
|
|
+}
|
|
|
+const showDetail = (row) => {
|
|
|
+ detailDisabled.value = true
|
|
|
+ dialog.value.visible = true;
|
|
|
+ dialog.value.title = '详情';
|
|
|
+ addFormRef.value?.resetFields();
|
|
|
+ form.value = Object.assign(form.value, row);
|
|
|
+}
|
|
|
+const handleEdit = (row, index) => {
|
|
|
+ detailDisabled.value = false
|
|
|
+ dialog.value.visible = true;
|
|
|
+ dialog.value.title = '编辑信息';
|
|
|
+ addFormRef.value?.resetFields();
|
|
|
+ curInd.value = index
|
|
|
+ form.value = Object.assign(form.value, row);
|
|
|
+
|
|
|
+}
|
|
|
+const submitForm = () => {
|
|
|
+ addFormRef.value.validate(async (valid) => {
|
|
|
+ if (valid) {
|
|
|
+ buttonLoading.value = true;
|
|
|
+ setTimeout(() => {
|
|
|
+ tableData.value[curInd.value] = Object.assign(tableData.value[curInd.value], form.value);
|
|
|
+ buttonLoading.value = false;
|
|
|
+ dialog.value.visible = false;
|
|
|
+ }, 500);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+}
|
|
|
+const cancel = () => {
|
|
|
+ dialog.value.visible = false;
|
|
|
+}
|
|
|
+const getDeviceTypeList = async () => {
|
|
|
+ let deviceTypeList = []
|
|
|
+ let deviceTypeDetailList = []
|
|
|
+ await getDeviceTypeDetailList({}).then(({ code, rows }) => {
|
|
|
+ if (code === 200) {
|
|
|
+ deviceTypeDetailList = rows.map((item) => ({
|
|
|
+ ...item,
|
|
|
+ ext1: item.ext1 ? JSON.parse(item.ext1) : null
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ await listDeviceType({}).then(({ code, rows }) => {
|
|
|
+ if (code === 200) {
|
|
|
+ deviceTypeList = (rows || []).map(item => ({
|
|
|
+ ...item,
|
|
|
+ deviceTypeDetailList: deviceTypeDetailList.filter(el => el.deviceTypeId === item.id)
|
|
|
+ }))
|
|
|
+ }
|
|
|
+ })
|
|
|
+ deviceTypeOptions.value = deviceTypeList
|
|
|
+ deviceTypeDetailOptions.value = deviceTypeDetailList
|
|
|
+};
|
|
|
+const getDeviceList = () => {
|
|
|
+ listDevice({}).then(({ code, rows }) => {
|
|
|
+ if (code === 200) {
|
|
|
+ deviceOptions.value = rows
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+onMounted(() => {
|
|
|
+ getDeviceTypeList()
|
|
|
+ getDeviceList()
|
|
|
+})
|
|
|
+</script>
|
|
|
+<style lang="scss" scoped>
|
|
|
+.config {
|
|
|
+ padding: 0 10px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: flex-start;
|
|
|
+ height: calc(100vh - 84px);
|
|
|
+}
|
|
|
+
|
|
|
+.right-content {
|
|
|
+ flex: 1;
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+
|
|
|
+ .step-content {
|
|
|
+ margin-left: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .bottom-btn {
|
|
|
+ height: 60px;
|
|
|
+ border-top: 1px solid #ccc;
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.custom-menu {
|
|
|
+ width: 200px;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.center-content {
|
|
|
+ padding-left: 10px;
|
|
|
+ margin-top: 10px;
|
|
|
+ flex: 1;
|
|
|
+}
|
|
|
+
|
|
|
+.camera {
|
|
|
+ .camera-op {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+
|
|
|
+ .el-button {
|
|
|
+ margin-left: 5px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .camera-content {
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ .camera-img {
|
|
|
+ margin-top: 10px;
|
|
|
+ width: 100%;
|
|
|
+ height: 450px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+.custom-form{
|
|
|
+ :deep(.el-input.is-disabled .el-input__wrapper) {
|
|
|
+ background: initial;
|
|
|
+ }
|
|
|
+ :deep(.el-select__wrapper.is-disabled) {
|
|
|
+ background: initial;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|