Parcourir la source

feat: 相机列表数据匹配

sunhongyao341504 il y a 2 ans
Parent
commit
305fffdc6e

+ 120 - 0
mock/camera-config/camera-list.ts

@@ -0,0 +1,120 @@
+import { resultSuccess } from '../_util';
+
+const list = [
+  {
+    cameraIp: '10.10.10.10',
+    cameraMac: '255.255.255.255',
+    cameraPort: '8080',
+    cameraType: 'haikang',
+    createdAt: '',
+    id: 0,
+    integrationState: 0,
+    isDeleted: 0,
+    code: 'SHGD-XDJS-0001',
+    networkingState: 0,
+    nvrEndAt: '',
+    nvrPeriod: 0,
+    nvrStartAt: '',
+    remark: '',
+    resolution: 0,
+    status: 0,
+    updatedAt: '',
+    workshopName: 'ARJ21部装车间',
+    workspaceName: '200工位',
+  },
+  {
+    cameraIp: '10.10.10.10',
+    cameraMac: '255.255.255.255',
+    cameraPort: '8080',
+    cameraType: 'anxus',
+    createdAt: '',
+    id: 0,
+    integrationState: 0,
+    isDeleted: 0,
+    code: 'SHGD-XDJS-0002',
+    networkingState: 0,
+    nvrEndAt: '',
+    nvrPeriod: 0,
+    nvrStartAt: '',
+    remark: '',
+    resolution: 0,
+    status: 0,
+    updatedAt: '',
+    workshopName: 'ARJ21部装车间',
+    workspaceName: '200工位',
+  },
+  {
+    cameraIp: '10.10.10.10',
+    cameraMac: '255.255.255.255',
+    cameraPort: '8080',
+    cameraType: 'haikang',
+    createdAt: '',
+    id: 0,
+    integrationState: 0,
+    isDeleted: 0,
+    code: 'SHGD-XDJS-0003',
+    networkingState: 1,
+    nvrEndAt: '',
+    nvrPeriod: 0,
+    nvrStartAt: '',
+    remark: '',
+    resolution: 0,
+    status: 0,
+    updatedAt: '',
+    workshopName: 'ARJ21部装车间',
+    workspaceName: '200工位',
+  },
+  {
+    cameraIp: '10.10.10.10',
+    cameraMac: '255.255.255.255',
+    cameraPort: '8080',
+    cameraType: 'haikang',
+    createdAt: '',
+    id: 0,
+    integrationState: 0,
+    isDeleted: 0,
+    code: 'SHGD-XDJS-0004',
+    networkingState: 0,
+    nvrEndAt: '',
+    nvrPeriod: 0,
+    nvrStartAt: '',
+    remark: '',
+    resolution: 0,
+    status: 0,
+    updatedAt: '',
+    workshopName: 'ARJ21部装车间',
+    workspaceName: '200工位',
+  },
+  {
+    cameraIp: '10.10.10.10',
+    cameraMac: '255.255.255.255',
+    cameraPort: '8080',
+    cameraType: 'dahua',
+    createdAt: '',
+    id: 0,
+    integrationState: 0,
+    isDeleted: 0,
+    code: 'SHGD-XDJS-0005',
+    networkingState: 0,
+    nvrEndAt: '',
+    nvrPeriod: 0,
+    nvrStartAt: '',
+    remark: '',
+    resolution: 0,
+    status: 0,
+    updatedAt: '',
+    workshopName: 'ARJ21部装车间',
+    workspaceName: '200工位',
+  },
+];
+
+export default [
+  {
+    url: '/cameraConfig/getCamera',
+    timeout: 1000,
+    method: 'get',
+    response: () => {
+      return resultSuccess(list);
+    },
+  },
+];

+ 5 - 4
package.json

@@ -32,12 +32,12 @@
   },
   "dependencies": {
     "@element-plus/icons-vue": "2.0.9",
-    "@types/fabric": "^5.3.6",
+    "@types/fabric": "5.3.6",
     "@vicons/antd": "0.12.0",
     "@vicons/ionicons5": "0.12.0",
     "@vueup/vue-quill": "1.0.0-beta.8",
     "@vueuse/core": "8.9.4",
-    "@vueuse/router": "^10.6.1",
+    "@vueuse/router": "10.6.1",
     "@wangeditor/editor": "5.1.23",
     "@wangeditor/editor-for-vue": "5.1.12",
     "axios": "0.27.2",
@@ -56,8 +56,9 @@
     "print-js": "1.6.0",
     "qrcode": "1.5.1",
     "qs": "6.11.0",
-    "url-join": "^5.0.0",
+    "url-join": "5.0.0",
     "vue": "3.3.4",
+    "vue-hooks-plus": "1.8.6",
     "vue-router": "4.1.2",
     "vue-types": "4.1.1",
     "vuedraggable": "4.1.0",
@@ -145,4 +146,4 @@
       ]
     }
   }
-}
+}

+ 69 - 3
pnpm-lock.yaml

@@ -9,7 +9,7 @@ dependencies:
     specifier: 2.0.9
     version: 2.0.9(vue@3.3.4)
   '@types/fabric':
-    specifier: ^5.3.6
+    specifier: 5.3.6
     version: 5.3.6
   '@vicons/antd':
     specifier: 0.12.0
@@ -24,7 +24,7 @@ dependencies:
     specifier: 8.9.4
     version: 8.9.4(vue@3.3.4)
   '@vueuse/router':
-    specifier: ^10.6.1
+    specifier: 10.6.1
     version: 10.6.1(vue-router@4.1.2)(vue@3.3.4)
   '@wangeditor/editor':
     specifier: 5.1.23
@@ -81,11 +81,14 @@ dependencies:
     specifier: 6.11.0
     version: 6.11.0
   url-join:
-    specifier: ^5.0.0
+    specifier: 5.0.0
     version: 5.0.0
   vue:
     specifier: 3.3.4
     version: 3.3.4
+  vue-hooks-plus:
+    specifier: ^1.8.6
+    version: 1.8.6(vue@3.3.4)
   vue-router:
     specifier: 4.1.2
     version: 4.1.2(vue@3.3.4)
@@ -1045,6 +1048,10 @@ packages:
     resolution: {integrity: sha512-kow8REgIIG42atN9vAaIdpEqVzj6WzV9m0PII8oce+an4Lc3eyfQF32/FbabbGmfWuF7TceTdd+gh74kOrXkPw==}
     dev: true
 
+  /@types/js-cookie@3.0.6:
+    resolution: {integrity: sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==}
+    dev: false
+
   /@types/json-schema@7.0.11:
     resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
     dev: true
@@ -1392,6 +1399,10 @@ packages:
     resolution: {integrity: sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ==}
     dev: false
 
+  /@vue/devtools-api@6.5.1:
+    resolution: {integrity: sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==}
+    dev: false
+
   /@vue/reactivity-transform@3.3.4:
     resolution: {integrity: sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==}
     dependencies:
@@ -2691,6 +2702,11 @@ packages:
     dev: false
     optional: true
 
+  /decode-uri-component@0.2.2:
+    resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==}
+    engines: {node: '>=0.10'}
+    dev: false
+
   /decompress-response@4.2.1:
     resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==}
     engines: {node: '>=8'}
@@ -3818,6 +3834,11 @@ packages:
       to-regex-range: 5.0.1
     dev: true
 
+  /filter-obj@1.1.0:
+    resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==}
+    engines: {node: '>=0.10.0'}
+    dev: false
+
   /finalhandler@1.1.2:
     resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==}
     engines: {node: '>= 0.8'}
@@ -4611,6 +4632,11 @@ packages:
     hasBin: true
     dev: true
 
+  /js-cookie@3.0.5:
+    resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
+    engines: {node: '>=14'}
+    dev: false
+
   /js-tokens@4.0.0:
     resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
     dev: true
@@ -5767,6 +5793,16 @@ packages:
       side-channel: 1.0.4
     dev: false
 
+  /query-string@7.1.3:
+    resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==}
+    engines: {node: '>=6'}
+    dependencies:
+      decode-uri-component: 0.2.2
+      filter-obj: 1.1.0
+      split-on-first: 1.1.0
+      strict-uri-encode: 2.0.0
+    dev: false
+
   /querystringify@2.2.0:
     resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
     requiresBuild: true
@@ -6026,6 +6062,11 @@ packages:
     dev: false
     optional: true
 
+  /screenfull@5.2.0:
+    resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==}
+    engines: {node: '>=0.10.0'}
+    dev: false
+
   /scroll-into-view-if-needed@2.2.31:
     resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==}
     dependencies:
@@ -6210,6 +6251,11 @@ packages:
     resolution: {integrity: sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==}
     dev: true
 
+  /split-on-first@1.1.0:
+    resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==}
+    engines: {node: '>=6'}
+    dev: false
+
   /split2@3.2.2:
     resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==}
     dependencies:
@@ -6232,6 +6278,11 @@ packages:
     engines: {node: '>= 0.6'}
     dev: true
 
+  /strict-uri-encode@2.0.0:
+    resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==}
+    engines: {node: '>=4'}
+    dev: false
+
   /string-argv@0.3.1:
     resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==}
     engines: {node: '>=0.6.19'}
@@ -7004,6 +7055,21 @@ packages:
       - supports-color
     dev: true
 
+  /vue-hooks-plus@1.8.6(vue@3.3.4):
+    resolution: {integrity: sha512-RQfEJkTXYMmHztgAHLhLBc/8n90qz6Pk2xV8WPNDiu+cn9ClwxdjPHvfj6J01BJ8G+tULXEBxD8XO9F1cf0A0w==}
+    peerDependencies:
+      vue: ^3.2.25
+    dependencies:
+      '@types/js-cookie': 3.0.6
+      '@vue/devtools-api': 6.5.1
+      js-cookie: 3.0.5
+      lodash: 4.17.21
+      qs: 6.11.0
+      query-string: 7.1.3
+      screenfull: 5.2.0
+      vue: 3.3.4
+    dev: false
+
   /vue-router@4.1.2(vue@3.3.4):
     resolution: {integrity: sha512-5BP1qXFncVRwgV/XnqzsKApdMjQPqWIpoUBdL1ynz8HyLxIX/UDAx7Ql2BjmA5CXT/p61JfZvkpiFWFpaqcfag==}
     peerDependencies:

+ 65 - 0
src/api/camera/camera-overview.ts

@@ -0,0 +1,65 @@
+import { http } from '@/utils/http/axios';
+import { CameraIPItem, CameraShowItem, CameraRangeItem } from '@/views/cameras/overview/type';
+
+export type CameraQueryParams = {
+  /** 相机协议类型 */
+  cameraType?: string;
+  /** 设备ID */
+  deviceId?: string;
+  /** 相机IP */
+  ip?: string;
+  /** 场景 */
+  sceneId?: string;
+};
+
+export const getCameraList = (params: CameraQueryParams) => {
+  return http.request<CameraShowItem[]>(
+    {
+      url: '/cameraConfig/getCamera',
+      method: 'get',
+      params,
+    },
+    {
+      urlPrefix: '',
+    },
+  );
+};
+
+export const addCameraItem = (data: CameraIPItem) => {
+  return http.request(
+    {
+      url: '/cameraConfig/saveCamera',
+      method: 'post',
+      data,
+    },
+    {
+      urlPrefix: '',
+    },
+  );
+};
+
+export const getCamerasByIPRange = (params: CameraRangeItem) => {
+  return http.request<CameraShowItem[]>(
+    {
+      url: '/cameraConfig/getCameraByIpSegment',
+      method: 'get',
+      params,
+    },
+    {
+      urlPrefix: '',
+    },
+  );
+};
+
+export const updateCameraItem = (data: Partial<CameraIPItem>) => {
+  return http.request(
+    {
+      url: '/cameraConfig/updateCamera',
+      method: 'put',
+      data,
+    },
+    {
+      urlPrefix: '',
+    },
+  );
+};

+ 3 - 1
src/components/Table/src/Table.vue

@@ -85,7 +85,7 @@
       <el-table
         ref="tableElRef"
         v-bind="getBindValues"
-        v-loading="getLoading"
+        v-loading="tableLoading"
         @sort-change="sortChange"
       >
         <template v-for="item in getTableColumns" :key="item.key">
@@ -209,6 +209,8 @@
   const { getAppTheme, getDarkTheme } = useDesignSetting();
 
   const { getLoading, setLoading } = useLoading(getProps);
+  // loading修改
+  const tableLoading = computed(() => props.loading);
 
   const { getPaginationInfo, setPagination } = usePagination(getProps);
 

+ 4 - 0
src/components/Table/src/props.ts

@@ -92,4 +92,8 @@ export const basicProps = {
     type: Boolean,
     default: false,
   },
+  loading: {
+    type: Boolean,
+    default: false,
+  },
 };

+ 23 - 21
src/views/cameras/overview/CamerasOverview.vue

@@ -5,9 +5,10 @@
       <BasicTable
         :columns="columns"
         :data-source="cameraItems"
-        :row-key="(row) => row.name"
+        :row-key="(row) => row.code"
         :action-column="actionColumn"
         :pagination="false"
+        :loading="loading"
         :tableSetting="{
           size: false,
           redo: false,
@@ -31,38 +32,36 @@
       </BasicTable>
     </div>
     <AddCamera class="add-popover" v-model="showAddPopover" />
+    <EditCamera class="add-popover" v-model="showEditPopover" :edit-data="editCameraData" />
   </div>
 </template>
 
 <script setup lang="ts">
-  import { h, reactive, ref } from 'vue';
+  import { h, onMounted, reactive, ref } from 'vue';
   import { BasicTable, TableActionIcons } from '@/components/Table';
   import { BasicColumn } from '@/components/Table';
-  import { getColumns } from './overviewColumns';
+  import { columns } from './overviewColumns';
   import ConditionQuery from './components/ConditionQuery.vue';
   import AddCamera from './components/CameraAddPopover.vue';
+  import EditCamera from './components/CameraEditPopover.vue';
   import emptyImg from '@/assets/images/table/table-empty.png';
   import { Plus } from '@element-plus/icons-vue';
   import previewIcon from '@/assets/images/table/table-preview.png';
   import editIcon from '@/assets/images/table/table-edit.png';
   import deleteIcon from '@/assets/images/table/table-delete.png';
+  import useCameraOverview from './stores/useCameraOverview';
+  import { storeToRefs } from 'pinia';
+  import { CameraIPItem } from './type';
+
+  const cameraOverview = useCameraOverview();
+  const { cameraItems, loading } = storeToRefs(cameraOverview);
+  const { getCameraItems } = cameraOverview;
 
   // 添加弹窗相关
   const showAddPopover = ref(false);
 
-  const cameraItems = [
-    {
-      cameraIp: '10.10.10.10',
-      protocal: '海康威视',
-      cameraPort: '8080',
-      mac: '255.255.255.255',
-      name: 'SHGD-XDJS-0001',
-      workshopId: 'ARJ21部装车间',
-      workspaceId: '200工位',
-      networkState: 1,
-      status: 1,
-    },
-  ];
+  const showEditPopover = ref(false);
+  const editCameraData = ref<CameraIPItem | null>();
 
   //操作列
   const actionColumn: BasicColumn = reactive({
@@ -98,10 +97,6 @@
     },
   });
 
-  // el-switch的change事件
-  const switchChange = () => {};
-  const columns = getColumns(switchChange);
-
   // 列排序操作
   const orderByItem = () => {};
 
@@ -109,7 +104,14 @@
 
   const handleDelete = () => {};
 
-  const handleEdit = () => {};
+  const handleEdit = (row) => {
+    showEditPopover.value = true;
+    editCameraData.value = row;
+  };
+
+  onMounted(() => {
+    getCameraItems();
+  });
 </script>
 
 <style scoped>

+ 68 - 34
src/views/cameras/overview/components/AddCameraByIP.vue

@@ -1,48 +1,60 @@
 <template>
-  <div style="margin-bottom: 120px">
-    <el-form
-      class="ip-form"
-      :inline="true"
-      :model="cameraIPData"
-      :rules="rules"
-      label-width="84px"
-      label-position="left"
-    >
-      <el-form-item
-        v-for="item in cameraIPAddForm"
-        :key="item.prop"
-        :label="item.label"
-        :prop="item.prop"
+  <div>
+    <div style="margin-bottom: 120px">
+      <el-form
+        class="ip-form"
+        :inline="true"
+        :model="cameraIPData"
+        :rules="rules"
+        label-width="84px"
+        label-position="left"
       >
-        <el-input
-          v-if="item.type === 'input'"
-          v-model="cameraIPData[item.prop]"
-          :placeholder="item.placeholder"
-          style="width: 200px"
-        />
-        <el-select
-          v-if="item.type === 'select'"
-          v-model="cameraIPData[item.prop]"
-          :placeholder="item.placeholder"
-          style="width: 200px"
+        <el-form-item
+          v-for="item in cameraIPAddForm"
+          :key="item.prop"
+          :label="item.label"
+          :prop="item.prop"
         >
-          <el-option
-            v-for="protocal in item.option"
-            :key="protocal.value"
-            :label="protocal.label"
-            :value="protocal.value"
+          <el-input
+            v-if="item.type === 'input'"
+            v-model="cameraIPData[item.prop]"
+            :placeholder="item.placeholder"
+            style="width: 200px"
+            :type="item.prop === 'password' ? 'password' : ''"
+            :show-password="item.prop === 'password'"
           />
-        </el-select>
-      </el-form-item>
-    </el-form>
+          <el-select
+            v-if="item.type === 'select'"
+            v-model="cameraIPData[item.prop]"
+            :placeholder="item.placeholder"
+            style="width: 200px"
+          >
+            <el-option
+              v-for="protocal in item.option"
+              :key="protocal.value"
+              :label="protocal.label"
+              :value="protocal.value"
+            />
+          </el-select>
+        </el-form-item>
+      </el-form>
+    </div>
+    <span class="pop-footer">
+      <el-button @click="handleCancel">取消</el-button>
+      <el-button type="primary" @click="handleConfirm">确定</el-button>
+    </span>
   </div>
 </template>
 
 <script setup lang="ts">
-  import { computed, ref } from 'vue';
+  import { computed, onBeforeMount, ref } from 'vue';
   import { CameraIPItem } from '../type';
   import { cameraIPAddForm } from '../constant';
 
+  const props = defineProps<{ formData?: CameraIPItem | null }>();
+
+  const emits = defineEmits(['cancel-execute', 'confirm-execute']);
+
   const cameraIPData = ref<CameraIPItem>({} as CameraIPItem);
 
   const rules = computed(() => {
@@ -54,6 +66,20 @@
     });
     return newRule;
   });
+
+  const handleCancel = () => {
+    emits('cancel-execute');
+  };
+
+  const handleConfirm = () => {
+    emits('confirm-execute', cameraIPData.value);
+  };
+
+  onBeforeMount(() => {
+    if (props.formData) {
+      cameraIPData.value = props.formData;
+    }
+  });
 </script>
 
 <style scoped>
@@ -65,6 +91,14 @@
     align-content: space-around;
   }
 
+  .pop-footer {
+    position: absolute;
+    right: 24px;
+    bottom: 27px;
+    display: flex;
+    justify-content: flex-end;
+  }
+
   :deep(.el-form-item__label) {
     font-size: 14px;
     color: #363636;

+ 100 - 75
src/views/cameras/overview/components/AddCameraByRange.vue

@@ -1,78 +1,85 @@
 <template>
-  <div class="flex flex-col items-center add-body">
-    <el-form
-      class="range-form"
-      :inline="true"
-      :model="cameraRangeData"
-      :rules="rules"
-      label-width="84px"
-      label-position="left"
-    >
-      <el-form-item
-        v-for="item in cameraRangeAddForm"
-        :key="item.prop"
-        :label="item.label"
-        :prop="item.prop"
-        :label-width="item.labelWidth"
+  <div style="width: 100%">
+    <div class="flex flex-col items-center add-body">
+      <el-form
+        class="range-form"
+        :inline="true"
+        :model="cameraRangeData"
+        :rules="rules"
+        label-width="84px"
+        label-position="left"
       >
-        <el-input
-          v-if="item.type === 'input'"
-          v-model="cameraRangeData[item.prop]"
-          :placeholder="item.placeholder"
-          style="width: 200px"
-        />
-        <el-select
-          v-if="item.type === 'select'"
-          v-model="cameraRangeData[item.prop]"
-          :placeholder="item.placeholder"
-          style="width: 200px"
+        <el-form-item
+          v-for="item in cameraRangeAddForm"
+          :key="item.prop"
+          :label="item.label"
+          :prop="item.prop"
+          :label-width="item.labelWidth"
         >
-          <el-option
-            v-for="protocal in item.option"
-            :key="protocal.value"
-            :label="protocal.label"
-            :value="protocal.value"
+          <el-input
+            v-if="item.type === 'input'"
+            v-model="cameraRangeData[item.prop]"
+            :placeholder="item.placeholder"
+            style="width: 200px"
           />
-        </el-select>
-      </el-form-item>
-    </el-form>
-    <div class="flex justify-end" style="width: 100%">
-      <el-button type="primary" class="btn-r" @click="searchRangeCamera">搜索</el-button>
-    </div>
-    <div class="search-list">
-      <BasicTable
-        style="height: 274px"
-        :columns="columns"
-        :data-source="cameraItems"
-        :row-key="(row) => row.name"
-        :action-column="actionColumn"
-        :pagination="false"
-        :tableSetting="{
-          size: false,
-          redo: false,
-          fullscreen: false,
-          striped: false,
-          setting: false,
-        }"
-        :row-class-name="getRowClassName"
-        ref="tableRef"
-        @order-change="orderByItem"
-        @selection-change="handleSelectionChange"
-      >
-        <template #empty>
-          <div class="empty-content flex flex-col items-center">
-            <img :src="emptyImg" class="empty-img" />
-            <span class="empty-text">目前无内容,请先添加相机</span>
-          </div>
-        </template>
-      </BasicTable>
+          <el-select
+            v-if="item.type === 'select'"
+            v-model="cameraRangeData[item.prop]"
+            :placeholder="item.placeholder"
+            style="width: 200px"
+          >
+            <el-option
+              v-for="protocal in item.option"
+              :key="protocal.value"
+              :label="protocal.label"
+              :value="protocal.value"
+            />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <div class="flex justify-end" style="width: 100%">
+        <el-button type="primary" class="btn-r" @click="searchRangeCamera">搜索</el-button>
+      </div>
+      <div class="search-list">
+        <BasicTable
+          style="height: 274px"
+          :columns="columns"
+          :data-source="cameraItems"
+          :row-key="(row) => row.name"
+          :action-column="actionColumn"
+          :pagination="false"
+          :tableSetting="{
+            size: false,
+            redo: false,
+            fullscreen: false,
+            striped: false,
+            setting: false,
+          }"
+          :row-class-name="getRowClassName"
+          ref="tableRef"
+          @order-change="orderByItem"
+          @selection-change="handleSelectionChange"
+        >
+          <template #empty>
+            <div class="empty-content flex flex-col items-center">
+              <img :src="emptyImg" class="empty-img" />
+              <span class="empty-text">目前无内容,请先添加相机</span>
+            </div>
+          </template>
+        </BasicTable>
+      </div>
+      <EditCamera v-model="showEditPop" style="z-index: 9" :edit-data="editRow" />
     </div>
+    <span class="pop-footer">
+      <el-button @click="handleCancel">取消</el-button>
+      <el-button type="primary" @click="handleConfirm">确定</el-button>
+    </span>
   </div>
 </template>
 
 <script setup lang="ts">
   import { computed, h, reactive, ref } from 'vue';
-  import { CameraRangeItem } from '../type';
+  import { CameraRangeItem, CameraIPItem } from '../type';
   import { cameraRangeAddForm } from '../constant';
   import SearchCamerasAction from './SearchCamerasAction.vue';
   import { BasicTable } from '@/components/Table';
@@ -81,9 +88,15 @@
   import emptyImg from '@/assets/images/table/table-empty.png';
   import editIcon from '@/assets/images/table/table-edit.png';
   import deleteIcon from '@/assets/images/table/table-delete.png';
+  import EditCamera from './CameraEditPopover.vue';
+
+  const emits = defineEmits(['cancel-execute', 'confirm-execute']);
 
   const cameraRangeData = ref<CameraRangeItem>({} as CameraRangeItem);
 
+  const showEditPop = ref(false);
+  const editRow = ref<CameraIPItem | null>(null);
+
   const rules = computed(() => {
     const newRule = {};
     cameraRangeAddForm.forEach((item) => {
@@ -209,7 +222,16 @@
 
   const handleDelete = () => {};
 
-  const handleEdit = () => {};
+  const handleEdit = (row) => {
+    showEditPop.value = true;
+    editRow.value = row;
+  };
+
+  const handleCancel = () => {
+    emits('cancel-execute');
+  };
+
+  const handleConfirm = () => {};
 </script>
 
 <style scoped>
@@ -228,14 +250,21 @@
 
   .btn-r {
     margin-right: 24px;
-    margin-bottom: 16px;
   }
 
   .search-list {
     width: 100%;
-    height: 290px;
+    height: 300px;
     padding: 0 28px;
-    margin-bottom: 60px;
+    margin-bottom: 40px;
+  }
+
+  .pop-footer {
+    position: absolute;
+    right: 24px;
+    bottom: 27px;
+    display: flex;
+    justify-content: flex-end;
   }
 
   :deep(.el-form-item__label) {
@@ -248,13 +277,9 @@
     margin-right: 0;
     margin-bottom: 28px;
   }
-  :deep(.el-table__body-wrapper tr td.el-table-fixed-column--left) {
-    background: unset;
-  }
-  :deep(.el-table__body-wrapper tr td.el-table-fixed-column--right) {
-    background: unset;
-  }
   :deep(.el-table .warm-row) {
     background: #f9e6e5 !important;
+    --el-table-row-hover-bg-color: none;
+    --el-bg-color: none;
   }
 </style>

+ 16 - 15
src/views/cameras/overview/components/CameraAddPopover.vue

@@ -18,12 +18,12 @@
       </div>
     </template>
     <div class="pop-content flex justify-center items-center">
-      <IPAddCamera v-if="addType === 'ip'" />
-      <RangeAddCamera v-if="addType === 'ipRange'" />
-      <span class="pop-footer">
-        <el-button @click="updateValue(false)">取消</el-button>
-        <el-button type="primary" @click="addCamera">确定</el-button>
-      </span>
+      <IPAddCamera
+        v-if="addType === 'ip'"
+        @cancel-execute="updateValue(false)"
+        @confirm-execute="onAddCamera"
+      />
+      <RangeAddCamera v-if="addType === 'ipRange'" @cancel-execute="updateValue(false)" />
     </div>
   </el-card>
 </template>
@@ -34,22 +34,30 @@
   import { Close } from '@element-plus/icons-vue';
   import IPAddCamera from './AddCameraByIP.vue';
   import RangeAddCamera from './AddCameraByRange.vue';
+  import useCameraOverview from '../stores/useCameraOverview';
 
   const props = defineProps<{ modelValue: boolean }>();
 
   const emits = defineEmits(['update:modelValue']);
 
+  const cameraOverview = useCameraOverview();
+  const { addCamera } = cameraOverview;
+
   const addType = ref(cameraAddType[0].value);
 
   const updateValue = (value) => {
+    addType.value = cameraAddType[0].value;
     emits('update:modelValue', value);
   };
 
-  const addCamera = () => {};
+  const onAddCamera = (data) => {
+    addCamera(data);
+  };
 </script>
 
 <style scoped lang="scss">
   .pop-card {
+    position: relative;
     margin-left: 21px !important;
   }
 
@@ -81,6 +89,7 @@
     height: 38px;
     background: #fafafa;
     border: 1px solid #d9d9d9;
+    cursor: pointer;
 
     &-active {
       background: #e2eefe;
@@ -92,14 +101,6 @@
     height: 566px;
   }
 
-  .pop-footer {
-    position: absolute;
-    right: 24px;
-    bottom: 27px;
-    display: flex;
-    justify-content: flex-end;
-  }
-
   :deep(.el-card__header) {
     padding: 0;
   }

+ 79 - 0
src/views/cameras/overview/components/CameraEditPopover.vue

@@ -0,0 +1,79 @@
+<template>
+  <el-card v-if="props.modelValue" class="pop-card">
+    <template #header>
+      <div class="flex justify-between items-center pop-head">
+        <span class="pop-head-name">编辑相机</span>
+        <el-icon :size="16" class="mr-3" @click="updateValue(false)"><Close /></el-icon>
+      </div>
+    </template>
+    <div class="pop-content flex justify-center items-center">
+      <IPAddCamera
+        :form-data="props.editData"
+        @cancel-execute="updateValue(false)"
+        @confirm-execute="onEditCamera"
+      />
+    </div>
+  </el-card>
+</template>
+
+<script setup lang="ts">
+  import { Close } from '@element-plus/icons-vue';
+  import IPAddCamera from './AddCameraByIP.vue';
+  import { CameraIPItem } from '../type';
+  import useCameraOverview from '../stores/useCameraOverview';
+
+  const props = defineProps<{
+    modelValue: boolean;
+    editData?: CameraIPItem | null;
+    confirmEvent?: (data) => any;
+  }>();
+
+  const emits = defineEmits(['update:modelValue']);
+
+  const cameraOverview = useCameraOverview();
+  const { editCamera } = cameraOverview;
+
+  const updateValue = (value) => {
+    emits('update:modelValue', value);
+  };
+
+  const onEditCamera = (data) => {
+    if (!props.confirmEvent) {
+      editCamera(data);
+    } else {
+      props.confirmEvent(data);
+    }
+  };
+</script>
+
+<style scoped lang="scss">
+  .pop-card {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    left: 0;
+    top: 0;
+  }
+
+  .pop-head {
+    height: 56px;
+
+    &-name {
+      margin-left: 24px;
+      font-size: 16px;
+      font-weight: 500;
+      color: #252525;
+    }
+  }
+
+  .pop-content {
+    height: 566px;
+  }
+
+  :deep(.el-card__header) {
+    padding: 0;
+  }
+  :deep(.el-card__body) {
+    padding: 0;
+  }
+</style>

+ 14 - 11
src/views/cameras/overview/components/ConditionQuery.vue

@@ -19,7 +19,7 @@
       </div>
       <div>
         <span>协议类型:</span>
-        <el-select v-model="queryProtocalType" placeholder="请选择协议类型" class="protocal-select">
+        <el-select v-model="queryCameraType" placeholder="请选择协议类型" class="protocal-select">
           <el-option
             v-for="item in protocalTypeSelect"
             :key="item.value"
@@ -41,26 +41,29 @@
       </div>
     </el-space>
     <div class="flex-1 flex justify-end">
-      <el-button type="primary" @click="conditionSearch"> 查询 </el-button>
+      <el-button type="primary" @click="getCameraItems"> 查询 </el-button>
       <el-button @click="resetSearch"> 重置 </el-button>
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-  import { ref } from 'vue';
+  import { storeToRefs } from 'pinia';
   import { queryTypeSelect, protocalTypeSelect } from '../constant';
+  import useCameraOverview from '../stores/useCameraOverview';
 
-  const queryType = ref('');
-  const queryTypeContent = ref('');
-  const queryProtocalType = ref('');
-  const queryWorkShop = ref('');
-
-  // 条件查询事件
-  const conditionSearch = () => {};
+  const cameraOverview = useCameraOverview();
+  const { queryType, queryTypeContent, queryCameraType, queryWorkShop } =
+    storeToRefs(cameraOverview);
+  const { getCameraItems } = cameraOverview;
 
   // 重置查询条件
-  const resetSearch = () => {};
+  const resetSearch = () => {
+    queryType.value = '';
+    queryTypeContent.value = '';
+    queryCameraType.value = '';
+    queryWorkShop.value = '';
+  };
 </script>
 
 <style scoped>

+ 6 - 6
src/views/cameras/overview/constant.ts

@@ -77,7 +77,7 @@ export const cameraIPAddForm: CameraAddFormItem[] = [
   },
   {
     label: '协议类型:',
-    prop: 'protocal',
+    prop: 'cameraType',
     placeholder: '请输入协议类型',
     type: 'select',
     required: true,
@@ -86,7 +86,7 @@ export const cameraIPAddForm: CameraAddFormItem[] = [
   },
   {
     label: '用户名:',
-    prop: 'user',
+    prop: 'userName',
     placeholder: '请输入用户名',
     type: 'input',
     required: true,
@@ -110,14 +110,14 @@ export const cameraIPAddForm: CameraAddFormItem[] = [
   },
   {
     label: '工位负责人:',
-    prop: 'workspacer',
+    prop: 'principal',
     placeholder: '请输入工位负责人',
     type: 'input',
     required: false,
   },
   {
     label: '设备ID号:',
-    prop: 'cameraId',
+    prop: 'code',
     placeholder: '自定义ID,不能重复',
     type: 'input',
     required: true,
@@ -162,7 +162,7 @@ export const cameraRangeAddForm: CameraAddFormItem[] = [
   },
   {
     label: '结束IP地址:',
-    prop: 'endId',
+    prop: 'endIp',
     placeholder: '请输入结束IP地址',
     type: 'input',
     labelWidth: '96px',
@@ -171,7 +171,7 @@ export const cameraRangeAddForm: CameraAddFormItem[] = [
   },
   {
     label: '端口:',
-    prop: 'cameraPort',
+    prop: 'port',
     placeholder: '请输入端口号',
     type: 'input',
     required: false,

+ 104 - 80
src/views/cameras/overview/overviewColumns.ts

@@ -3,88 +3,112 @@ import { ElSwitch } from 'element-plus';
 import type { BasicColumn } from '@/components/Table';
 import connectedIcon from '@/assets/images/table/camera-netConnect.png';
 import unConnectedIcon from '@/assets/images/table/camera-netUnconnect.png';
+import { protocalTypeSelect } from './constant';
+import useCameraOverview from './stores/useCameraOverview';
+import { CameraShowItem } from './type';
 
-export const getColumns = (switchChange: () => unknown): BasicColumn[] => {
-  return [
-    {
-      label: '序号',
-      minWidth: 40,
-      type: 'index',
-      fixed: 'left',
-    },
-    {
-      label: 'IP地址',
-      prop: 'cameraIp',
-      minWidth: 80,
-    },
-    {
-      label: '协议类型',
-      prop: 'protocal',
-      minWidth: 60,
-    },
-    {
-      label: '端口地址',
-      prop: 'cameraPort',
-      minWidth: 60,
-    },
-    {
-      label: 'MAC地址',
-      prop: 'mac',
-      minWidth: 100,
-    },
-    {
-      label: '设备ID',
-      prop: 'name',
-      minWidth: 110,
-    },
-    {
-      label: '车间场景',
-      prop: 'workshopId',
-      minWidth: 100,
-    },
-    {
-      label: '工位场景',
-      prop: 'workspaceId',
-      minWidth: 60,
+const cameraOverview = useCameraOverview();
+const { editCamera } = cameraOverview;
+
+export const columns: BasicColumn[] = [
+  {
+    label: '序号',
+    minWidth: 60,
+    type: 'index',
+    fixed: 'left',
+  },
+  {
+    label: 'IP地址',
+    prop: 'cameraIp',
+    minWidth: 140,
+  },
+  {
+    label: '协议类型',
+    prop: 'cameraType',
+    minWidth: 80,
+    render(record) {
+      return h(
+        'span',
+        {},
+        {
+          default: () =>
+            protocalTypeSelect.find((item) => item.value === record.row.cameraType)?.label,
+        },
+      );
     },
-    {
-      label: '联网状态',
-      prop: 'networkState',
-      render(record) {
-        return h(
-          'img',
-          {
-            src: record.row.networkState === 0 ? unConnectedIcon : connectedIcon,
-            style:
-              'width: 20px; height: 20px; object-fit: fill; display: inline-block; line-height: 20px; vertical-align: middle;',
-          },
-          {},
-        );
-      },
-      minWidth: 80,
-      align: 'center',
-      sortable: 'custom',
+  },
+  {
+    label: '端口地址',
+    prop: 'cameraPort',
+    minWidth: 80,
+  },
+  {
+    label: 'MAC地址',
+    prop: 'cameraMac',
+    minWidth: 140,
+  },
+  {
+    label: '设备ID',
+    prop: 'code',
+    minWidth: 150,
+  },
+  {
+    label: '车间场景',
+    prop: 'workshopName',
+    minWidth: 140,
+  },
+  {
+    label: '工位场景',
+    prop: 'workspaceName',
+    minWidth: 80,
+  },
+  {
+    label: '联网状态',
+    prop: 'networkingState',
+    render(record) {
+      return h(
+        'img',
+        {
+          src: record.row.networkingState === 0 ? connectedIcon : unConnectedIcon,
+          style:
+            'width: 20px; height: 20px; object-fit: fill; display: inline-block; line-height: 20px; vertical-align: middle;',
+        },
+        {},
+      );
     },
-    {
-      label: '是否进入平台',
-      prop: 'status',
-      render(record) {
-        return h(
-          ElSwitch,
-          {
-            modelValue: record.row.status,
-            onChange: () => {
-              switchChange();
-            },
-            activeValue: 1,
-            inactiveValue: 0,
+    minWidth: 120,
+    align: 'center',
+    sortable: 'custom',
+  },
+  {
+    label: '是否进入平台',
+    prop: 'status',
+    render(record) {
+      return h(
+        ElSwitch,
+        {
+          modelValue: record.row.status,
+          onChange: (val) => {
+            record.row.status = val;
+            const upData: CameraShowItem = {} as CameraShowItem;
+            upData.code = record.row.code;
+            upData.id = record.row.id;
+            upData.status = record.row.status;
+            editCamera(upData);
           },
-          {},
-        );
-      },
-      minWidth: 100,
-      align: 'center',
-      sortable: 'custom',
+          activeValue: 0,
+          inactiveValue: 1,
+        },
+        {},
+      );
     },
-  ];
-};
+    minWidth: 140,
+    align: 'center',
+    sortable: 'custom',
+  },
+  {
+    label: '备注',
+    prop: 'remark',
+    minWidth: 60,
+  },
+];

+ 77 - 0
src/views/cameras/overview/stores/useCameraOverview.ts

@@ -0,0 +1,77 @@
+import { ref } from 'vue';
+import { defineStore } from 'pinia';
+import {
+  CameraQueryParams,
+  getCameraList,
+  addCameraItem,
+  updateCameraItem,
+} from '@/api/camera/camera-overview';
+import { CameraIPItem, CameraShowItem } from '../type';
+import { useRequest } from 'vue-hooks-plus';
+
+export const useCameraOverview = defineStore('camera-overview', () => {
+  const queryType = ref('');
+  const queryTypeContent = ref('');
+  const queryCameraType = ref('');
+  const queryWorkShop = ref('');
+
+  const cameraItems = ref<CameraShowItem[]>([]);
+
+  // 条件查询事件
+  const conditionSearch = () => {
+    const params: CameraQueryParams = {};
+    if (queryType.value) {
+      switch (queryType.value) {
+        case 'cameraIp':
+          params.ip = queryTypeContent.value;
+          break;
+
+        case 'name':
+          params.deviceId = queryTypeContent.value;
+          break;
+      }
+    }
+    if (queryCameraType.value) {
+      params.cameraType = queryCameraType.value;
+    }
+    if (queryWorkShop.value) {
+      params.sceneId = queryWorkShop.value;
+    }
+    return getCameraList(params).then((res) => {
+      return res;
+    });
+  };
+
+  const { loading, run: getCameraItems } = useRequest(conditionSearch, {
+    manual: true,
+    onSuccess: (res) => {
+      cameraItems.value = res;
+    },
+  });
+
+  const addCamera = (data: CameraIPItem) => {
+    addCameraItem(data).then(() => {
+      getCameraItems();
+    });
+  };
+
+  const editCamera = (data: Partial<CameraShowItem>) => {
+    updateCameraItem(data).then(() => {
+      getCameraItems();
+    });
+  };
+
+  return {
+    queryType,
+    queryTypeContent,
+    queryCameraType,
+    queryWorkShop,
+    cameraItems,
+    loading,
+    getCameraItems,
+    addCamera,
+    editCamera,
+  };
+});
+
+export default useCameraOverview;

+ 40 - 9
src/views/cameras/overview/type.ts

@@ -1,21 +1,52 @@
 export interface CameraIPItem {
-  name: string;
+  /** 相机IP */
   cameraIp: string;
+  /**	相机协议类型 */
+  cameraType: string;
+  /** 相机端口 */
   cameraPort: string;
-  protocal: string;
-  user: string;
-  workshopId: string;
-  password: string;
-  workspacer: string;
-  cameraId: string;
+  /** 相机ID */
+  code: string;
+  /** 工位场景Id */
+  workspaceId: string;
+  /** 工位负责人 */
+  principal?: string;
+  /** 描述 */
   remark?: string;
+  /** 相机名称 */
+  name: string;
+  /** id */
+  id?: number;
+  /** 用户名 */
+  userName?: string;
+  /** 密码 */
+  password?: string;
+}
+
+export interface CameraShowItem extends CameraIPItem {
+  /** 相机MAC地址 */
+  cameraMac: string;
+  /** 车间场景名称 */
+  workshopName: string;
+  /** 工位场景名称 */
+  workspaceName: string;
+  /** 联网状态: 0-启用, 1-禁用 */
+  networkingState: number;
+  /** 状态: 0-启用, 1-禁用 */
+  status: number;
 }
 
 export interface CameraRangeItem {
+  /** 启用IP地址 */
   startIp: string;
-  endId: string;
+  /** 结束IP地址 */
+  endIp: string;
+  /** 协议类型 */
   cameraType: string;
+  /** 相机端口 */
   port?: string;
-  user?: string;
+  /** 用户名 */
+  userName?: string;
+  /** 密码 */
   password: string;
 }