Pārlūkot izejas kodu

完成多个页面的摄像头管理以及解决跨域问题

nahida 1 gadu atpakaļ
vecāks
revīzija
ff11b84659

+ 1 - 1
.env.development

@@ -1,4 +1,4 @@
 # 变量必须以 VITE_ 为前缀才能暴露给外部读取
 NODE_ENV = 'development'
-VITE_APP_BASE_API = '/api'
+VITE_APP_BASE_API = 'http://192.168.110.13:8080'
 VITE_MINIO_BASE_URL = 'http://192.168.110.30:9000/zksy-file'

+ 2 - 2
.env.production

@@ -1,4 +1,4 @@
 # 变量必须以 VITE_ 为前缀才能暴露给外部读取
 NODE_ENV = 'production'
-VITE_APP_BASE_API = 'https://gxq.huaihua.gov.cn/qyxyfjflserver'
-VITE_MINIO_BASE_URL = 'http://192.168.110.30:9000/zksy-file'
+VITE_APP_BASE_API = 'http://172.16.102.52:8080'
+VITE_MINIO_BASE_URL = 'http://172.16.102.52:9000/test'

+ 5 - 2
src/router/index.ts

@@ -13,7 +13,8 @@ const router = createRouter({
         {
           path: '',
           name: 'home',
-          component: () => import('@/views/HomeView.vue'),
+          // component: () => import('@/views/HomeView.vue'),
+          redirect: 'zhdpgl/yqzl/yqjs'
         }
       ],
     },
@@ -28,13 +29,15 @@ const router = createRouter({
     },
   ],
 })
+
 console.log(router.getRoutes())
+const modules = import.meta.glob('/src/views/**.vue');
 const dynamicRoutesList = useDynamicRoutes()
 dynamicRoutesList.forEach(item => {
   router.addRoute('layout', {
     path: item.path,
     name: item.name,
-    component: () => import(/* @vite-ignore */ `/src/${item.addr}`)
+    component: modules[`/src/${item.addr}`]
   })
 })
 console.log(router.getRoutes())

+ 29 - 10
src/stores/ParkSecurityStore.ts

@@ -3,12 +3,15 @@ import { clientDel, clientGet, clientPost } from '@/utils/request.ts'
 import type { ChannelInfo, ChannelInfoResponse, RtspUrl, RtspUrlResponse } from '@/views/zhdpgl/yqzl/yqaf.vue'
 import { ElLoading, ElMessage } from 'element-plus'
 import type { BaseResponse } from '@/utils/type.ts'
+import { getQueryEqMethod } from '@/utils/getQueryMethod.ts'
 
-interface ParkVisualVideoDto{
+interface ParkVisualVideoDto {
   url:string,
   token:string,
   area:number,
   channelId:string,
+  channelName:string,
+  belong:number,
   id?:string
 }
 
@@ -35,14 +38,22 @@ export const useParkSecurityStore = defineStore('parkSecurity', {
       }
       this.dataList = res.data.pageData;
     },
-    async getVideoChannel(list:Array<Array<string>>){
+    async setVideoChannel(list:Array<Array<string>>,belong:number){
       const loading = ElLoading.service({
         lock: true,
         text: '执行中......',
         fullscreen: true,
       })
       try {
-        const channelList = await clientGet<null, ParkVisualVideoResponse>('/park/ParkVisualVideo/getList')
+        const channelList = await clientGet<{conditionJson:string}, ParkVisualVideoResponse>('/park/ParkVisualVideo/getList',{
+          params:{
+            conditionJson: getQueryEqMethod([{
+              column: 'belong',
+              value: belong,
+              type: 'eq'
+            }])
+          }
+        })
         const ids = channelList.data.map(item => item.id)
         if (ids!.length > 0 || channelList) {
           await clientDel('/park/ParkVisualVideo/deleteBatchById', {
@@ -57,24 +68,32 @@ export const useParkSecurityStore = defineStore('parkSecurity', {
             const urlAndTokenObj: RtspUrl = await getUrlAndToken(w)
             const r: ParkVisualVideoDto = {
               channelId: w,
+              channelName: this.dataList.find(item => item.channelCode === w)?.channelName || '',
               area: i,
               url: urlAndTokenObj.url,
-              token: urlAndTokenObj.token
+              token: urlAndTokenObj.token,
+              belong: belong
             }
             toServerList.push(r)
           }
         }
         const res = await clientPost<ParkVisualVideoDto[], BaseResponse>('/park/ParkVisualVideo/saveBatch', toServerList)
-        if (res.code === 200) {
-          return true;
-        }
-        return false;
+        return res.code === 200;
+
       } finally {
         loading.close()
       }
     },
-    async getAllVideoChannel(){
-      const allList= await clientGet<null,ParkVisualVideoResponse>("/park/ParkVisualVideo/getList")
+    async getAllVideoChannel(belong:number){
+      const allList= await clientGet<null,ParkVisualVideoResponse>("/park/ParkVisualVideo/getList",{
+        params:{
+          conditionJson:getQueryEqMethod([{
+            column: 'belong',
+            value: belong,
+            type: 'eq'
+          }])
+        }
+      })
       if(allList.code !== 200){
         ElMessage.error(allList.msg)
         return;

+ 8 - 0
src/utils/getQueryMethod.ts

@@ -0,0 +1,8 @@
+interface QueryType {
+  column:string,
+  value:string | number |boolean,
+  type:'eq'|'like'
+}
+export const getQueryEqMethod = (queryList:QueryType[]) => {
+  return encodeURIComponent(JSON.stringify(queryList));
+}

+ 2 - 9
src/views/zhdpgl/yqzl/yqaf.vue

@@ -175,7 +175,7 @@ const handleConfirm = async ()=>{
         break;
     }
   }
-  const res = await parkSecurityStore.getVideoChannel(resList);
+  const res = await parkSecurityStore.setVideoChannel(resList,0);
   if(res){
     dialogVisible.value = false;
     ElMessage.success('保存成功')
@@ -184,7 +184,7 @@ const handleConfirm = async ()=>{
 
 const showDialogVisible = async ()=>{
   dialogVisible.value = true;
-  const videoChannel = await parkSecurityStore.getAllVideoChannel();
+  const videoChannel = await parkSecurityStore.getAllVideoChannel(0);
   if (videoChannel!.length > 0 || videoChannel) {
     form.value.select1 = videoChannel!.filter(item => item.area === 0).map(item => item.channelId);
     form.value.select2 = videoChannel!.filter(item => item.area === 1).map(item => item.channelId);
@@ -232,13 +232,6 @@ init()
           </el-tag>
         </template>
       </el-table-column>
-<!--      <el-table-column label="操作">-->
-<!--        <template #default="scope">-->
-<!--          <el-button type="primary" size="small" @click="checkCamera(scope.row)">-->
-<!--            预览-->
-<!--          </el-button>-->
-<!--        </template>-->
-<!--      </el-table-column>-->
     </el-table>
     <el-pagination
       v-model:current-page="currentPage"

+ 205 - 4
src/views/zhdpgl/zhaf/ssjk.vue

@@ -1,11 +1,212 @@
 <script setup lang="ts">
+import { ref, toRaw } from 'vue'
+import { ElMessage } from 'element-plus'
+import { clientPost } from '@/utils/request.ts'
+import type { BaseResponse } from '@/utils/type.ts'
+import { useParkSecurityStore } from '@/stores/ParkSecurityStore.ts'
 
+interface ChannelInfoParams {
+  access?: string;
+  channelCodeList?: string;
+  channelTypeList?: string;
+  deviceCategory?: string;
+  deviceCodeList?: string;
+  deviceType?: string;
+  includeSubOwnerCodeFlag?: string;
+  isOnline?: string;
+  isVirtual?: string;
+  ownerCode?: string;
+  pageNum?: number;
+  pageSize?: number;
+  sort?: string;
+  sortType?: string;
+  stat?: string;
+  unitTypeList?: string;
+}
+
+export interface ChannelInfo {
+  id: string;
+  deviceCode: string;
+  unitType: number;
+  unitSeq: number;
+  channelSeq: number;
+  channelCode: string;
+  channelSn: string;
+  channelName: string;
+  channelType: string;
+  cameraType: string;
+  ownerCode: string;
+  isOnline: number;
+  stat: number;
+  capability: string;
+  chExt: string;
+  isVirtual: boolean;
+  createTime: string;
+}
+
+export interface ChannelInfoResponse extends BaseResponse {
+  data: {
+    currentPage: number;
+    totalPage: number;
+    pageSize: number;
+    totalRows: number;
+    pageData: ChannelInfo[];
+  }
+}
+
+const tableData = ref<ChannelInfo[]>([])
+const loading = ref(false)
+const currentPage = ref(1)
+const pageSize = ref(10)
+const total = ref(0)
+const dialogVisible = ref(false)
+const parkSecurityStore = useParkSecurityStore()
+const form = ref({
+  select1: [] as string[]|undefined
+})
+
+const getData = async () => {
+  loading.value = true
+  try {
+    const params: ChannelInfoParams = {
+      pageNum: currentPage.value,
+      pageSize: pageSize.value,
+      unitTypeList: '1'
+    }
+    const paramsStr = new URLSearchParams(params as never).toString()
+    const res = await clientPost<ChannelInfoParams, ChannelInfoResponse>('/visualization/deviceInfo/getChannelPage?' + paramsStr)
+
+    if (res.code === 200) {
+      tableData.value = res.data.pageData
+      total.value = res.data.totalRows
+    } else {
+      ElMessage.error(res.msg || '获取数据失败')
+    }
+  } catch (error) {
+    console.error('Error fetching data:', error)
+    ElMessage.error('获取数据时发生错误')
+  } finally {
+    loading.value = false
+  }
+}
+
+const getCameraType = (q: string) => {
+  switch (q) {
+    case '1':
+      return '枪机'
+    case '2':
+      return '球机'
+    case '3':
+      return '半球'
+    case '5':
+      return '本地采集输入'
+    default:
+      return '其他'
+  }
+}
+
+const handleSizeChange = (val: number) => {
+  pageSize.value = val
+  getData()
+}
+
+const handleCurrentChange = (val: number) => {
+  currentPage.value = val
+  getData()
+}
+
+const handleConfirm = async ()=>{
+  const resList:[
+    select1:string[]
+  ] = [
+    []
+  ];
+  for (const k in form.value) {
+    switch (k) {
+      case 'select1':
+        resList[0] = toRaw(form.value.select1) as string[];
+        break;
+    }
+  }
+  const res = await parkSecurityStore.setVideoChannel(resList,1);
+  if(res){
+    dialogVisible.value = false;
+    ElMessage.success('保存成功')
+  }
+}
+
+const showDialogVisible = async ()=>{
+  dialogVisible.value = true;
+  const videoChannel = await parkSecurityStore.getAllVideoChannel(1);
+  if (videoChannel!.length > 0 || videoChannel) {
+    form.value.select1 = videoChannel!.filter(item => item.area === 0).map(item => item.channelId);
+  }
+}
+
+const init = () => {
+  getData()
+  parkSecurityStore.getSecurityList();
+}
+init()
 </script>
 
 <template>
- 实时监控
+  <div class="pt-20px">
+    <el-button type="primary" @click="showDialogVisible">配置大屏路径</el-button>
+    <el-table :data="tableData" style="width: 100%" v-loading="loading">
+      <el-table-column prop="id" label="id" width="100" />
+      <el-table-column prop="deviceCode" label="设备编码" width="100" />
+      <el-table-column prop="channelCode" label="通道编码" width="200" />
+      <el-table-column prop="channelName" label="通道名称" width="200" />
+      <el-table-column prop="channelType" label="通道类型">
+        <template #default="scope">
+          {{ scope.row.channelType === '1' ? '视频通道' : '未知' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="cameraType" label="摄像机类型">
+        <template #default="scope">
+          {{ getCameraType(scope.row.cameraType) }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="ownerCode" label="所属组织编码" />
+      <el-table-column prop="isOnline" label="在线状态">
+        <template #default="scope">
+          <el-tag :type="scope.row.isOnline === 1 ? 'success' : 'danger'">
+            {{ scope.row.isOnline === 1 ? '在线' : '离线' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="stat" label="状态">
+        <template #default="scope">
+          <el-tag :type="scope.row.stat === 1 ? 'success' : 'warning'">
+            {{ scope.row.stat === 1 ? '正常' : '异常' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-pagination
+      v-model:current-page="currentPage"
+      v-model:page-size="pageSize"
+      :page-sizes="[10, 20, 50, 100]"
+      :total="total"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+      layout="total, sizes, prev, pager, next, jumper"
+      class="mt-20px text-right"
+    />
+    <el-dialog v-model="dialogVisible" title="配置大屏路径">
+      <el-form-item label="实时监控">
+        <el-select v-model="form.select1" multiple :multiple-limit="8" placeholder="请选择">
+          <el-option v-for="(item,index) in parkSecurityStore.dataList" :key="index" :label="item.channelName" :value="item.channelCode"></el-option>
+        </el-select>
+      </el-form-item>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="dialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="handleConfirm">确定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
 </template>
 
-<style scoped>
-
-</style>

+ 248 - 5
src/views/zhdpgl/zhdg/dgzm.vue

@@ -1,11 +1,254 @@
 <script setup lang="ts">
+import { clientGet } from '@/utils/request.ts'
+import type { BaseResponse } from '@/utils/type.ts'
+import { ref } from 'vue'
+import { ElLoading, ElMessage } from 'element-plus'
+import { useDebounceFn } from '@vueuse/core'
+
+interface StatusParams {
+  cmd: number,
+  lightNums: string,
+  packageId: string
+}
+
+interface StatusResponse extends BaseResponse {
+  data: string
+}
+
+interface LightData {
+  light_num: string;       // 灯编号
+  brightness: number | boolean;      // 亮度
+  voltage: number;         // 电压
+  current: number;         // 电流
+  power: number;           // 功率
+  power_quantity: number;  // 电量
+  onlineStatus: number;    // 在线状态
+}
+
+const cardDatas = ref<LightData[]>([]);
+const linkDialogVisible = ref(false);
+// const downloadUrl = ref('public/LedOK Express.rar');
+
+const orignalParams = [
+  {
+    cmd: 6012,
+    lightNums: '40005289',
+    packageId: '1050'
+  },
+  {
+    cmd: 6012,
+    lightNums: '40005272',
+    packageId: '1050'
+  },
+  {
+    cmd: 6012,
+    lightNums: '40005274',
+    packageId: '1050'
+  },
+  {
+    cmd: 6012,
+    lightNums: '40005281',
+    packageId: '1050'
+  }
+]
+const getData = async () => {
+  let process = 0
+  const loading = ElLoading.service({
+    lock: true,
+    text: '正在加载数据' + process + '/' + orignalParams.length,
+    background: 'rgba(200, 200, 200, .8)'
+  })
+  const list = []
+  try {
+    for (let i = 0; i < orignalParams.length; i++) {
+      const res = await clientGet<StatusParams, StatusResponse>('/pole/instruct/issued/equipmentStatus', {
+        params: orignalParams[i]
+      })
+      list.push(res)
+      process = i + 1
+      loading.setText('正在加载数据' + process + '/' + orignalParams.length)
+    }
+
+    const resList: LightData[] = list.map(res => {
+      return JSON.parse(JSON.parse(res.data).params)
+    })
+    cardDatas.value = resList.flat(-1 >>> 1).map(item => {
+      item.brightness = item.brightness != 0
+      return item
+    })
+  } catch (error) {
+    console.error('数据获取失败:', error)
+  } finally {
+    loading.close()
+  }
+}
+const changeLight = useDebounceFn(async (light: LightData) => {
+  const loading = ElLoading.service({
+    lock: true,
+    text: '正在操作灯杆' + light.light_num,
+    background: 'rgba(200, 200, 200, .8)'
+  })
+  if (typeof light.brightness === 'number') {
+    ElMessage.warning('出现了错误')
+    return
+  }
+  try {
+    const newBrightness = light.brightness ? 0 : 100
+    await clientGet('/pole/instruct/issued/reportEnvironmentalData', {
+      params: {
+        cmd: 6011,
+        lightNums: light.light_num,
+        packageId: '1050',
+        brightness: newBrightness
+      }
+    })
+    const qq = await getSingleLightStatus({
+      cmd: 6012,
+      lightNums: light.light_num,
+      packageId: '1050'
+    })
+    if(qq == null){
+      ElMessage.error('获取状态失败,请重试')
+      return
+    }
+    qq.brightness = qq.brightness != 0
+    const updatedLightIndex = cardDatas.value.findIndex(card => card.light_num === light.light_num)
+    if (updatedLightIndex !== -1) {
+      cardDatas.value[updatedLightIndex] = (qq as LightData);
+    }
+    ElMessage.success(light.brightness ? '关灯' : '开灯')
+  } catch (error) {
+    console.error('开灯/关灯失败:', error)
+    ElMessage.error('操作失败,请重试')
+  }finally {
+    loading.close()
+  }
+},500);
+const getSingleLightStatus = async (params: StatusParams): Promise<LightData | null> => {
+  try {
+    const res = await clientGet<StatusParams, StatusResponse>('/pole/instruct/issued/equipmentStatus', {
+      params: params
+    });
+    const parsedData = JSON.parse(JSON.parse(res.data).params);
+    return parsedData[0];
+  } catch (error) {
+    console.error('获取单个灯杆状态失败:', error);
+    ElMessage.error('获取状态失败,请重试');
+    return null;
+  }
+};
+const updateCurrentData = async (light:LightData)=>{
+  const loading = ElLoading.service({
+    lock: true,
+    text: '正在获取当前灯杆数据',
+    background: 'rgba(200, 200, 200, .8)'
+  })
+  const res = await getSingleLightStatus({cmd:6012,lightNums:light.light_num,packageId:'1050'})
+  if(!res){
+    ElMessage.error('获取状态失败,请重试')
+    return
+  }
+  res.brightness = res.brightness != 0
+  const updatedLightIndex = cardDatas.value.findIndex(card => card.light_num === light.light_num);
+  if (updatedLightIndex !== -1) {
+    cardDatas.value[updatedLightIndex] = res as LightData;
+  }
+  loading.close()
+}
+const toOtherLink = (linkStr:'一键报警'|'灯杆显示屏')=>{
+  switch (linkStr){
+    case '一键报警':
+      window.open('https://172.16.102.27:8088/')
+      break;
+    case '灯杆显示屏':
+      linkDialogVisible.value = true
+      break;
+  }
+}
+const init = () => {
+  getData()
+}
+init()
 
 </script>
 
 <template>
- 今日报警列表
+  <div class="w-98% h-full">
+    <el-row :gutter="30" class="p-30px">
+      <el-col :span="24">
+        <el-row>
+          <el-col :span="6">
+            <el-button type="primary" @click="init">获取最新数据</el-button>
+          </el-col>
+          <el-col :span="4" :offset="14">
+            <el-button type="info" @click="toOtherLink('一键报警')">一键报警</el-button>
+            <el-button type="warning" @click="toOtherLink('灯杆显示屏')">灯杆显示屏</el-button>
+          </el-col>
+        </el-row>
+      </el-col>
+      <el-col :span="12" sm="24" v-for="(item,index) in cardDatas" :key="index" class="my-4">
+        <el-card>
+          <template #header>
+            <div class="flex gap-1 justify-between px-3">
+              <div class="flex gap-1">
+                <div class="w-20px h-20px">
+                  <img class="w-full h-full" src="/src/assets/svg/灯杆.svg"  alt="灯杆"/>
+                </div>
+                <div>灯杆信息</div>
+              </div>
+              <div>
+                <el-button @click="updateCurrentData(item)">更新当前灯杆数据</el-button>
+              </div>
+            </div>
+          </template>
+          <template #default>
+            <el-descriptions :title="'灯杆编号'+item.light_num" :column="2" border>
+              <el-descriptions-item
+                label="电压"
+                label-align="right"
+                align="center"
+              >
+                {{ item.voltage.toFixed(2) + 'V' }}
+              </el-descriptions-item>
+              <el-descriptions-item label="电流" label-align="right" align="center">
+                {{ item.current.toFixed(2) + 'A' }}
+              </el-descriptions-item>
+              <el-descriptions-item label="功率" label-align="right" align="center">
+                {{ item.power.toFixed(3) + 'kW' }}
+              </el-descriptions-item>
+              <el-descriptions-item label="电量" label-align="right" align="center">
+                {{ item.power_quantity.toFixed(2) + '度' }}
+              </el-descriptions-item>
+              <el-descriptions-item label="在线状态" label-align="right" align="center">
+                <el-tag :type="item.onlineStatus==1?'success':'danger'">
+                  <span v-if="item.onlineStatus==1">在线</span>
+                  <span v-else>离线</span>
+                </el-tag>
+              </el-descriptions-item>
+              <el-descriptions-item label="开关" label-align="right" align="center" class-name="flex gap-20px">
+                <el-text>当前
+                  <el-tag :type="item.brightness?'success':'danger'">{{ item.brightness ? '开' : '关' }}</el-tag>
+                </el-text>
+                <el-button :type="item.brightness?'info':'primary'" @click="changeLight(item)">
+                  {{ item.brightness ? '关闭' : '打开' }}
+                </el-button>
+              </el-descriptions-item>
+            </el-descriptions>
+          </template>
+        </el-card>
+      </el-col>
+    </el-row>
+    <el-dialog v-model="linkDialogVisible" title="请下载大屏显示器软件">
+      <template #default>
+        <div class="flex flex-col gap-10px">
+          <div>
+            <a :href="'/LedOK Express.rar'" download="LedOK_Express.rar">下载大屏显示器软件</a>
+          </div>
+        </div>
+      </template>
+      <template #footer>
+        <el-button @click="linkDialogVisible=false">关闭</el-button>
+      </template>
+    </el-dialog>
+  </div>
 </template>
-
-<style scoped>
-
-</style>

+ 213 - 4
src/views/zhdpgl/zhdg/dpfb.vue

@@ -1,11 +1,220 @@
 <script setup lang="ts">
+import { clientGet, clientPost, clientPut, clientDel } from '@/utils/request.ts'
+import { ref } from 'vue'
+import type { BaseResponse } from '@/utils/type.ts'
+import { ElMessage, ElDialog, ElInput, ElForm, ElFormItem, ElButton, ElUpload,type UploadFile,type UploadFiles, ElMessageBox } from 'element-plus'
 
+interface ParkStemScreen {
+  "id": string,
+  "type": string,
+  "createTime": string,
+  "updateTime": string,
+  "filePath": string[],
+}
+
+interface ParkStemScreenResponse extends BaseResponse {
+  data: ParkStemScreen[]
+}
+
+const minioUrl = import.meta.env.VITE_MINIO_BASE_URL
+
+const cardList = ref<ParkStemScreen[]>()
+
+const getList = async () => {
+  const res = await clientGet<null, ParkStemScreenResponse>('/park/parkStemScreen/getListWithImg');
+  if (res.code !== 200) {
+    ElMessage.error(res.msg)
+  }
+  cardList.value = res.data;
+}
+
+const init = () => {
+  getList()
+}
+init()
+
+// 新增功能相关状态和方法
+const addDialogVisible = ref(false)
+const newScreen = ref({
+  type: '',
+  filePath: [] as string[]
+})
+const addFileList = ref<UploadFile[]>([])
+
+const handleAddFileChange = (file: UploadFile, fileList: UploadFiles) => {
+  addFileList.value = fileList
+}
+
+const handleAdd = async () => {
+  if (addFileList.value.length === 0) {
+    ElMessage.error('请上传文件')
+    return
+  }
+
+  const formData = new FormData()
+  formData.append('type', newScreen.value.type)
+  addFileList.value.forEach(file => {
+    formData.append('files', file.raw!)
+  })
+
+  const res = await clientPost<FormData, BaseResponse>('/park/parkStemScreen/save', formData, {
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    }
+  });
+
+  if (res.code === 200) {
+    ElMessage.success('新增成功')
+    getList()
+    addDialogVisible.value = false
+    addFileList.value = []
+  } else {
+    ElMessage.error(res.msg)
+  }
+}
+
+// 修改功能相关状态和方法
+const editDialogVisible = ref(false)
+const editScreen = ref<ParkStemScreen>({
+  id: '',
+  type: '',
+  createTime: '',
+  updateTime: '',
+  filePath: [] as string[]
+})
+const editFileList = ref<UploadFile[]>([])
+
+const handleEditFileChange = (file: UploadFile, fileList: UploadFiles) => {
+  editFileList.value = fileList
+}
+
+const handleEdit = (item: ParkStemScreen) => {
+  editScreen.value = { ...item }
+  editFileList.value = []
+  editDialogVisible.value = true
+}
+
+const handleUpdate = async () => {
+  const formData = new FormData()
+  formData.append('id', editScreen.value.id)
+  formData.append('type', editScreen.value.type)
+
+  if (editFileList.value.length > 0) {
+    editFileList.value.forEach(file => {
+      formData.append('files', file.raw!)
+    })
+  }
+
+  const res = await clientPut<FormData, BaseResponse>('/park/parkStemScreen/updateById', formData, {
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    }
+  });
+
+  if (res.code === 200) {
+    ElMessage.success('修改成功')
+    getList()
+    editDialogVisible.value = false
+    editFileList.value = []
+  } else {
+    ElMessage.error(res.msg)
+  }
+}
+
+// 删除功能相关方法
+const handleDelete = async (id: string) => {
+  ElMessageBox.confirm('确定要删除此大屏图片吗?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  }).then(async () => {
+    const res = await clientDel<null,BaseResponse>(`/park/parkStemScreen/deleteById?id=${id}`);
+    if (res.code === 200) {
+      ElMessage.success('删除成功')
+      getList()
+    } else {
+      ElMessage.error(res.msg)
+    }
+  }).catch(() => {
+    ElMessage.info('已取消删除')
+  })
+}
 </script>
 
 <template>
- 今日报警列表
-</template>
+  <div>
+    <el-row class="p-20px" :gutter="20">
+      <el-col :span="24" class="my-20px">
+        <el-button type="primary" @click="addDialogVisible = true">新增大屏图片</el-button>
+      </el-col>
+      <el-col :lg="6" :sm="12" v-for="(item, index) in cardList" :key="index">
+        <el-card style="max-width: 480px">
+          <template #header>
+            <div class="flex justify-between">
+              <div>{{ item.type }}</div>
+              <div>
+                <el-button type="warning" @click="handleEdit(item)">修改</el-button>
+                <el-button type="danger" @click="handleDelete(item.id)" v-if="false">删除</el-button>
+              </div>
+            </div>
+          </template>
+          <img
+            :src="minioUrl + item.filePath[0]"
+            style="width: 100%"
+            alt="大屏图片"
+          />
+        </el-card>
+      </el-col>
+    </el-row>
 
-<style scoped>
+    <!-- 新增对话框 -->
+    <el-dialog v-model="addDialogVisible" title="新增大屏图片" destroy-on-close>
+      <el-form :model="newScreen" label-width="120px">
+        <el-form-item label="类型">
+          <el-input v-model="newScreen.type" />
+        </el-form-item>
+        <el-form-item label="文件上传">
+          <el-upload
+            action=""
+            :on-change="handleAddFileChange"
+            :auto-upload="false"
+            :file-list="addFileList"
+          >
+            <el-button type="primary">选取文件</el-button>
+          </el-upload>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="addDialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="handleAdd">确定</el-button>
+        </span>
+      </template>
+    </el-dialog>
 
-</style>
+    <!-- 修改对话框 -->
+    <el-dialog v-model="editDialogVisible" title="修改大屏图片" destroy-on-close>
+      <el-form :model="editScreen" label-width="120px">
+        <el-form-item label="类型">
+          <el-input v-model="editScreen.type" />
+        </el-form-item>
+        <el-form-item label="文件上传">
+          <el-upload
+            action=""
+            :on-change="handleEditFileChange"
+            :auto-upload="false"
+            :file-list="editFileList"
+          >
+            <el-button type="primary">选取文件</el-button>
+          </el-upload>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="editDialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="handleUpdate">确定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>

+ 0 - 11
src/views/zhdpgl/zhxf/bjtj.vue

@@ -9,14 +9,3 @@
 <style scoped>
 
 </style>
-<script setup lang="ts">
-
-</script>
-
-<template>
- 今日报警列表
-</template>
-
-<style scoped>
-
-</style>

+ 0 - 10
src/views/zhdpgl/zhxf/ssbjtj.vue

@@ -9,14 +9,4 @@
 <style scoped>
 
 </style>
-<script setup lang="ts">
-
-</script>
 
-<template>
- 实时报警统计
-</template>
-
-<style scoped>
-
-</style>

+ 205 - 4
src/views/zhdpgl/zhxf/ssjk.vue

@@ -1,11 +1,212 @@
 <script setup lang="ts">
+import { ref, toRaw } from 'vue'
+import { ElMessage } from 'element-plus'
+import { clientPost } from '@/utils/request.ts'
+import type { BaseResponse } from '@/utils/type.ts'
+import { useParkSecurityStore } from '@/stores/ParkSecurityStore.ts'
 
+interface ChannelInfoParams {
+  access?: string;
+  channelCodeList?: string;
+  channelTypeList?: string;
+  deviceCategory?: string;
+  deviceCodeList?: string;
+  deviceType?: string;
+  includeSubOwnerCodeFlag?: string;
+  isOnline?: string;
+  isVirtual?: string;
+  ownerCode?: string;
+  pageNum?: number;
+  pageSize?: number;
+  sort?: string;
+  sortType?: string;
+  stat?: string;
+  unitTypeList?: string;
+}
+
+export interface ChannelInfo {
+  id: string;
+  deviceCode: string;
+  unitType: number;
+  unitSeq: number;
+  channelSeq: number;
+  channelCode: string;
+  channelSn: string;
+  channelName: string;
+  channelType: string;
+  cameraType: string;
+  ownerCode: string;
+  isOnline: number;
+  stat: number;
+  capability: string;
+  chExt: string;
+  isVirtual: boolean;
+  createTime: string;
+}
+
+export interface ChannelInfoResponse extends BaseResponse {
+  data: {
+    currentPage: number;
+    totalPage: number;
+    pageSize: number;
+    totalRows: number;
+    pageData: ChannelInfo[];
+  }
+}
+
+const tableData = ref<ChannelInfo[]>([])
+const loading = ref(false)
+const currentPage = ref(1)
+const pageSize = ref(10)
+const total = ref(0)
+const dialogVisible = ref(false)
+const parkSecurityStore = useParkSecurityStore()
+const form = ref({
+  select1: [] as string[]|undefined
+})
+
+const getData = async () => {
+  loading.value = true
+  try {
+    const params: ChannelInfoParams = {
+      pageNum: currentPage.value,
+      pageSize: pageSize.value,
+      unitTypeList: '1'
+    }
+    const paramsStr = new URLSearchParams(params as never).toString()
+    const res = await clientPost<ChannelInfoParams, ChannelInfoResponse>('/visualization/deviceInfo/getChannelPage?' + paramsStr)
+
+    if (res.code === 200) {
+      tableData.value = res.data.pageData
+      total.value = res.data.totalRows
+    } else {
+      ElMessage.error(res.msg || '获取数据失败')
+    }
+  } catch (error) {
+    console.error('Error fetching data:', error)
+    ElMessage.error('获取数据时发生错误')
+  } finally {
+    loading.value = false
+  }
+}
+
+const getCameraType = (q: string) => {
+  switch (q) {
+    case '1':
+      return '枪机'
+    case '2':
+      return '球机'
+    case '3':
+      return '半球'
+    case '5':
+      return '本地采集输入'
+    default:
+      return '其他'
+  }
+}
+
+const handleSizeChange = (val: number) => {
+  pageSize.value = val
+  getData()
+}
+
+const handleCurrentChange = (val: number) => {
+  currentPage.value = val
+  getData()
+}
+
+const handleConfirm = async ()=>{
+  const resList:[
+    select1:string[]
+  ] = [
+    []
+  ];
+  for (const k in form.value) {
+    switch (k) {
+      case 'select1':
+        resList[0] = toRaw(form.value.select1) as string[];
+        break;
+    }
+  }
+  const res = await parkSecurityStore.setVideoChannel(resList,2);
+  if(res){
+    dialogVisible.value = false;
+    ElMessage.success('保存成功')
+  }
+}
+
+const showDialogVisible = async ()=>{
+  dialogVisible.value = true;
+  const videoChannel = await parkSecurityStore.getAllVideoChannel(2);
+  if (videoChannel!.length > 0 || videoChannel) {
+    form.value.select1 = videoChannel!.filter(item => item.area === 0).map(item => item.channelId);
+  }
+}
+
+const init = () => {
+  getData()
+  parkSecurityStore.getSecurityList();
+}
+init()
 </script>
 
 <template>
- 实时监控
+  <div class="pt-20px">
+    <el-button type="primary" @click="showDialogVisible">配置大屏路径</el-button>
+    <el-table :data="tableData" style="width: 100%" v-loading="loading">
+      <el-table-column prop="id" label="id" width="100" />
+      <el-table-column prop="deviceCode" label="设备编码" width="100" />
+      <el-table-column prop="channelCode" label="通道编码" width="200" />
+      <el-table-column prop="channelName" label="通道名称" width="200" />
+      <el-table-column prop="channelType" label="通道类型">
+        <template #default="scope">
+          {{ scope.row.channelType === '1' ? '视频通道' : '未知' }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="cameraType" label="摄像机类型">
+        <template #default="scope">
+          {{ getCameraType(scope.row.cameraType) }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="ownerCode" label="所属组织编码" />
+      <el-table-column prop="isOnline" label="在线状态">
+        <template #default="scope">
+          <el-tag :type="scope.row.isOnline === 1 ? 'success' : 'danger'">
+            {{ scope.row.isOnline === 1 ? '在线' : '离线' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="stat" label="状态">
+        <template #default="scope">
+          <el-tag :type="scope.row.stat === 1 ? 'success' : 'warning'">
+            {{ scope.row.stat === 1 ? '正常' : '异常' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-pagination
+      v-model:current-page="currentPage"
+      v-model:page-size="pageSize"
+      :page-sizes="[10, 20, 50, 100]"
+      :total="total"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+      layout="total, sizes, prev, pager, next, jumper"
+      class="mt-20px text-right"
+    />
+    <el-dialog v-model="dialogVisible" title="配置大屏路径">
+      <el-form-item label="实时监控">
+        <el-select v-model="form.select1" multiple :multiple-limit="4" placeholder="请选择">
+          <el-option v-for="(item,index) in parkSecurityStore.dataList" :key="index" :label="item.channelName" :value="item.channelCode"></el-option>
+        </el-select>
+      </el-form-item>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="dialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="handleConfirm">确定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
 </template>
 
-<style scoped>
-
-</style>

+ 10 - 10
vite.config.ts

@@ -28,14 +28,14 @@ export default defineConfig({
       '@': fileURLToPath(new URL('./src', import.meta.url))
     },
   },
-  server: {
-    proxy: {
-      '/api': {
-        // target: 'http://172.16.102.52:8080',
-        target: 'http://192.168.110.13:8080',
-        changeOrigin: true,
-        rewrite: (path) => path.replace(/^\/api/, '')
-      }
-    }
-  }
+  // server: {
+  //   proxy: {
+  //     '/api': {
+  //       // target: 'http://172.16.102.52:8080',
+  //        target: 'http://192.168.110.13:8080',
+  //       changeOrigin: true,
+  //       rewrite: (path) => path.replace(/^\/api/, '')
+  //     }
+  //   }
+  // }
 })