index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. <template>
  2. <div class="config">
  3. <el-menu default-active="2" class="custom-menu">
  4. <el-menu-item index="2">
  5. <el-icon><icon-menu /></el-icon>
  6. <template #title>视频灯检测</template>
  7. </el-menu-item>
  8. <!-- <el-menu-item index="1">
  9. <el-icon><MoreFilled /></el-icon>
  10. <template #title>电流电压检测</template>
  11. </el-menu-item> -->
  12. </el-menu>
  13. <div class="right-content">
  14. <el-steps style="margin-top: 10px;" class="step-content" finish-status="success" :space="200" :active="activeStep"
  15. simple>
  16. <el-step title="选择视频点位" />
  17. <el-step title="选择算法" />
  18. <el-step title="点位设备关联" />
  19. <el-step title="在线检测" />
  20. </el-steps>
  21. <div class="center-content">
  22. <div v-if="activeStep === 0" class="camera">
  23. <div class="camera-op">
  24. <el-select v-model="camera" placeholder="请选择" clearable style="width: 180px" size="small">
  25. <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
  26. </el-select>
  27. <el-button type="primary" size="small" @click="showCamera">获取画面</el-button>
  28. </div>
  29. <div class="camera-content">
  30. <img v-if="cameraImgShow" class="camera-img" src="@/assets/images/camera.png" />
  31. </div>
  32. </div>
  33. <div v-else-if="activeStep === 1" class="camera">
  34. <div class="camera-op">
  35. <el-select v-model="algorithm" placeholder="请选择算法" clearable style="width: 180px" size="small">
  36. <el-option v-for="item in algorithmOptions" :key="item.value" :label="item.label" :value="item.value" />
  37. </el-select>
  38. <el-button type="primary" size="small" @click="showCameraData">获取位置读数</el-button>
  39. </div>
  40. <div class="camera-content">
  41. <img v-if="!cameraDataShow" class="camera-img" src="@/assets/images/camera.png" />
  42. <img v-else class="camera-img" src="@/assets/images/camera-data.png" />
  43. </div>
  44. </div>
  45. <div v-else-if="activeStep === 2">
  46. <el-table :data="tableData">
  47. <el-table-column label="id" align="center" prop="id" width="80"/>
  48. <el-table-column label="位置" align="center" prop="position" />
  49. <el-table-column label="算法" align="center" show-overflow-tooltip width="150" >
  50. <template #default="{ row }">
  51. {{ algorithmOptions.filter(item => item.value == row.algorithmId)[0]?.label }}
  52. </template>
  53. </el-table-column>
  54. <el-table-column label="设备类型" align="center" prop="deviceType">
  55. <template #default="{ row }">
  56. {{ deviceTypeOptions.filter(item => item.id == row.deviceType)[0]?.name }}
  57. </template>
  58. </el-table-column>
  59. <el-table-column label="品牌型号" align="center" prop="brandXh">
  60. <template #default="{ row }">
  61. {{ deviceTypeDetailOptions.filter(item => item.id == row.brandXh)[0]?.ext1.brand }}{{ deviceTypeDetailOptions.filter(item => item.id == row.brandXh)[0]?.xh }}
  62. </template>
  63. </el-table-column>
  64. <el-table-column label="设备" align="center">
  65. <template #default="{ row }">
  66. {{ deviceOptions.filter(item => item.id == row.device)[0]?.name }}
  67. </template>
  68. </el-table-column>
  69. <el-table-column label="参数" align="center" prop="param" />
  70. <el-table-column label="状态" align="center" prop="status">
  71. <template #default="scope">
  72. <el-tag v-if="scope.row.status === '1'" type="info">未配</el-tag>
  73. <el-tag v-else type="success">已配</el-tag>
  74. </template>
  75. </el-table-column>
  76. <el-table-column label="操作" align="center" width="100" fixed="right">
  77. <template #default="{row,$index}">
  78. <el-button size="small" link type="primary" @click="showDetail(row)">详情</el-button>
  79. <el-button v-if="row.status == 1" size="small" link type="primary"
  80. @click="handleEdit(row,$index)">编辑</el-button>
  81. </template>
  82. </el-table-column>
  83. </el-table>
  84. </div>
  85. <div v-else-if="activeStep === 3">
  86. <el-table :data="tableData">
  87. <el-table-column label="id" align="center" prop="id" width="80"/>
  88. <el-table-column label="位置" align="center" prop="position" />
  89. <el-table-column label="算法" align="center" show-overflow-tooltip width="150" >
  90. <template #default="{ row }">
  91. {{ algorithmOptions.filter(item => item.value == row.algorithmId)[0]?.label }}
  92. </template>
  93. </el-table-column>
  94. <el-table-column label="设备类型" align="center" prop="deviceType">
  95. <template #default="{ row }">
  96. {{ deviceTypeOptions.filter(item => item.id == row.deviceType)[0]?.name }}
  97. </template>
  98. </el-table-column>
  99. <el-table-column label="品牌型号" align="center" prop="brandXh">
  100. <template #default="{ row }">
  101. {{ deviceTypeDetailOptions.filter(item => item.id == row.brandXh)[0]?.ext1.brand }}{{ deviceTypeDetailOptions.filter(item => item.id == row.brandXh)[0]?.xh }}
  102. </template>
  103. </el-table-column>
  104. <el-table-column label="设备" align="center">
  105. <template #default="{ row }">
  106. {{ deviceOptions.filter(item => item.id == row.device)[0]?.name }}
  107. </template>
  108. </el-table-column>
  109. <el-table-column label="参数" align="center" prop="param" />
  110. <el-table-column label="读数" align="center" prop="reading" />
  111. </el-table>
  112. </div>
  113. </div>
  114. <!-- 信息对话框 -->
  115. <el-dialog v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body>
  116. <el-form ref="addFormRef" :model="form" class="custom-form" label-width="80px" :disabled="detailDisabled">
  117. <el-row :gutter="20">
  118. <el-col :span="12">
  119. <el-form-item label="位置" prop="position"
  120. :rules="[{ required: true, message: '位置不能为空', trigger: 'blur' }]">
  121. <el-input v-model="form.position" placeholder="请输入位置" />
  122. </el-form-item>
  123. </el-col>
  124. <el-col :span="12">
  125. <el-form-item label="算法" prop="algorithmId"
  126. :rules="[{ required: true, message: '算法不能为空', trigger: 'change' }]">
  127. <el-select v-model="form.algorithmId" placeholder="请选择算法" clearable>
  128. <el-option v-for="item in algorithmOptions" :key="item.value" :label="item.label"
  129. :value="item.value" />
  130. </el-select>
  131. </el-form-item>
  132. </el-col>
  133. <el-col :span="12">
  134. <el-form-item label="设备类型" prop="deviceType"
  135. :rules="[{ required: true, message: '设备类型不能为空', trigger: 'change' }]">
  136. <el-select v-model="form.deviceType" clearable placeholder="请选择设备类型" @change="form.brandXh = ''">
  137. <el-option v-for="dict in deviceTypeOptions" :key="dict.id" :label="dict.name" :value="dict.id">
  138. </el-option>
  139. </el-select>
  140. </el-form-item>
  141. </el-col>
  142. <el-col :span="12">
  143. <el-form-item label="品牌型号" prop="brandXh"
  144. :rules="[{ required: true, message: '品牌型号不能为空', trigger: 'change' }]">
  145. <el-select v-model="form.brandXh" clearable placeholder="请选择品牌型号">
  146. <el-option
  147. v-for="dict in deviceTypeDetailOptions.filter(item => item.deviceTypeId === form.deviceType)"
  148. :key="dict.id" :label="`${dict.ext1.brand}${dict.xh}`" :value="dict.id">
  149. </el-option>
  150. </el-select>
  151. </el-form-item>
  152. </el-col>
  153. <el-col :span="12">
  154. <el-form-item label="设备" prop="device"
  155. :rules="[{ required: true, message: '设备不能为空', trigger: 'change' }]">
  156. <el-select v-model="form.device" placeholder="请选择设备" clearable>
  157. <el-option v-for="item in deviceOptions" :key="item.id" :label="item.name" :value="item.id" />
  158. </el-select>
  159. </el-form-item>
  160. </el-col>
  161. <el-col :span="12">
  162. <el-form-item label="参数" prop="param">
  163. <el-input v-model="form.param" placeholder="请输入" />
  164. </el-form-item>
  165. </el-col>
  166. <el-col :span="12">
  167. <el-form-item label="状态" prop="status"
  168. :rules="[{ required: true, message: '状态不能为空', trigger: 'change' }]">
  169. <el-select v-model="form.status" placeholder="请选择状态" clearable>
  170. <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
  171. </el-select>
  172. </el-form-item>
  173. </el-col>
  174. </el-row>
  175. </el-form>
  176. <template #footer>
  177. <div class="dialog-footer">
  178. <el-button :loading="buttonLoading" type="primary" @click="submitForm"> 确 定 </el-button>
  179. <el-button @click="cancel"> 取 消 </el-button>
  180. </div>
  181. </template>
  182. </el-dialog>
  183. <div class="bottom-btn">
  184. <el-button v-if="activeStep > 0" @click="lastStep">上一步</el-button>
  185. <el-button v-if="activeStep < 3" type="primary" @click="nextStep">下一步</el-button>
  186. <el-button v-else type="primary" @click="confirmClick">完成</el-button>
  187. </div>
  188. </div>
  189. </div>
  190. </template>
  191. <script setup lang="ts">
  192. import {
  193. Menu as IconMenu,
  194. } from '@element-plus/icons-vue'
  195. import { listDevice } from '@/api/deviceManage/device';
  196. import { listDeviceType, getDeviceTypeDetailList } from '@/api/deviceManage/deviceType';
  197. const { proxy } = getCurrentInstance() as ComponentInternalInstance;
  198. const addFormRef = ref<ElFormInstance>()
  199. const buttonLoading = ref(false);
  200. const form = ref({
  201. position: undefined,
  202. algorithmId: undefined,
  203. deviceType: undefined,
  204. brandXh: undefined,
  205. device: undefined,
  206. param: undefined,
  207. status: undefined,
  208. } as any);
  209. const dialog = ref({
  210. visible: false,
  211. title: '',
  212. });
  213. const camera = ref('')
  214. const algorithm = ref('')
  215. const activeStep = ref(0)
  216. const cameraImgShow = ref(false)
  217. const cameraDataShow = ref(false)
  218. const deviceTypeOptions = ref([])
  219. const deviceTypeDetailOptions = ref([])
  220. const deviceOptions = ref([])
  221. const curInd = ref(0)
  222. const detailDisabled = ref(false)
  223. const options = [
  224. {
  225. value: '2',
  226. label: '摄像头001',
  227. },
  228. {
  229. value: '2',
  230. label: '摄像头002',
  231. },
  232. {
  233. value: '3',
  234. label: '摄像头003',
  235. },
  236. ]
  237. const algorithmOptions = [
  238. {
  239. value: '1',
  240. label: '表针表计识别',
  241. },
  242. {
  243. value: '2',
  244. label: 'LED表计识别',
  245. },
  246. {
  247. value: '3',
  248. label: '指示灯状态识别',
  249. },
  250. ]
  251. const statusOptions = [
  252. {
  253. value: '1',
  254. label: '未配',
  255. },
  256. {
  257. value: '2',
  258. label: '已配',
  259. },
  260. ]
  261. const tableData = ref([])
  262. const showCamera = () => {
  263. if (!camera.value) return proxy?.$modal.msgError('请先选择摄像头')
  264. cameraImgShow.value = true
  265. }
  266. const showCameraData = () => {
  267. if (!algorithm.value) return proxy?.$modal.msgError('请先选择算法')
  268. cameraDataShow.value = true
  269. }
  270. const nextStep = () => {
  271. if (activeStep.value === 0) {
  272. if (!camera.value||!cameraImgShow.value) return proxy?.$modal.msgError('请先获取画面')
  273. }
  274. if (activeStep.value === 1) {
  275. if (!algorithm.value) return proxy?.$modal.msgError('请先选择算法')
  276. if (!cameraDataShow.value) return proxy?.$modal.msgError('请获取位置读数')
  277. const tmpArr = [];
  278. for (let i = 0; i < 3; i++) {
  279. tmpArr.push({
  280. id: `${i + 1}`,
  281. position: `L-000${i + 1}`,
  282. algorithmId: algorithm.value,
  283. deviceType: '',
  284. brandXh: '',
  285. device: '',
  286. param: '',
  287. status: '1'
  288. })
  289. }
  290. tableData.value = tmpArr
  291. }
  292. if (activeStep.value === 2) {
  293. if (tableData.value.some(item => item.status === '1')) return proxy?.$modal.msgError('请先配置设备')
  294. tableData.value = tableData.value.map(item => ({
  295. ...item,
  296. reading: '200V'
  297. }))
  298. }
  299. activeStep.value++
  300. }
  301. const lastStep = () => {
  302. activeStep.value--
  303. }
  304. const confirmClick = () => {
  305. }
  306. const showDetail = (row) => {
  307. detailDisabled.value = true
  308. dialog.value.visible = true;
  309. dialog.value.title = '详情';
  310. addFormRef.value?.resetFields();
  311. form.value = Object.assign(form.value, row);
  312. }
  313. const handleEdit = (row, index) => {
  314. detailDisabled.value = false
  315. dialog.value.visible = true;
  316. dialog.value.title = '编辑信息';
  317. addFormRef.value?.resetFields();
  318. curInd.value = index
  319. form.value = Object.assign(form.value, row);
  320. }
  321. const submitForm = () => {
  322. addFormRef.value.validate(async (valid) => {
  323. if (valid) {
  324. buttonLoading.value = true;
  325. setTimeout(() => {
  326. tableData.value[curInd.value] = Object.assign(tableData.value[curInd.value], form.value);
  327. buttonLoading.value = false;
  328. dialog.value.visible = false;
  329. }, 500);
  330. }
  331. });
  332. }
  333. const cancel = () => {
  334. dialog.value.visible = false;
  335. }
  336. const getDeviceTypeList = async () => {
  337. let deviceTypeList = []
  338. let deviceTypeDetailList = []
  339. await getDeviceTypeDetailList({}).then(({ code, rows }) => {
  340. if (code === 200) {
  341. deviceTypeDetailList = rows.map((item) => ({
  342. ...item,
  343. ext1: item.ext1 ? JSON.parse(item.ext1) : null
  344. }));
  345. }
  346. });
  347. await listDeviceType({}).then(({ code, rows }) => {
  348. if (code === 200) {
  349. deviceTypeList = (rows || []).map(item => ({
  350. ...item,
  351. deviceTypeDetailList: deviceTypeDetailList.filter(el => el.deviceTypeId === item.id)
  352. }))
  353. }
  354. })
  355. deviceTypeOptions.value = deviceTypeList
  356. deviceTypeDetailOptions.value = deviceTypeDetailList
  357. };
  358. const getDeviceList = () => {
  359. listDevice({}).then(({ code, rows }) => {
  360. if (code === 200) {
  361. deviceOptions.value = rows
  362. }
  363. })
  364. }
  365. onMounted(() => {
  366. getDeviceTypeList()
  367. getDeviceList()
  368. })
  369. </script>
  370. <style lang="scss" scoped>
  371. .config {
  372. padding: 0 10px;
  373. display: flex;
  374. justify-content: space-between;
  375. align-items: flex-start;
  376. height: calc(100vh - 84px);
  377. }
  378. .right-content {
  379. flex: 1;
  380. height: 100%;
  381. display: flex;
  382. flex-direction: column;
  383. .step-content {
  384. margin-left: 10px;
  385. }
  386. .bottom-btn {
  387. height: 60px;
  388. border-top: 1px solid #ccc;
  389. display: flex;
  390. justify-content: flex-end;
  391. align-items: center;
  392. }
  393. }
  394. .custom-menu {
  395. width: 200px;
  396. height: 100%;
  397. }
  398. .center-content {
  399. padding-left: 10px;
  400. margin-top: 10px;
  401. flex: 1;
  402. }
  403. .camera {
  404. .camera-op {
  405. display: flex;
  406. justify-content: flex-end;
  407. .el-button {
  408. margin-left: 5px;
  409. }
  410. }
  411. .camera-content {
  412. flex: 1;
  413. .camera-img {
  414. margin-top: 10px;
  415. width: 100%;
  416. height: 450px;
  417. }
  418. }
  419. }
  420. .custom-form{
  421. :deep(.el-input.is-disabled .el-input__wrapper) {
  422. background: initial;
  423. }
  424. :deep(.el-select__wrapper.is-disabled) {
  425. background: initial;
  426. }
  427. }
  428. </style>