Преглед на файлове

feat: 相机管理功能、样式

sunhongyao341504 преди 2 години
родител
ревизия
2f4051f80e

BIN
src/assets/images/table/camera-netConnect.png


BIN
src/assets/images/table/camera-netUnconnect.png


BIN
src/assets/images/table/table-delete.png


BIN
src/assets/images/table/table-edit.png


BIN
src/assets/images/table/table-preview.png


+ 1 - 0
src/components/Table/index.ts

@@ -1,4 +1,5 @@
 export { default as BasicTable } from './src/Table.vue';
 export { default as TableAction } from './src/components/TableAction.vue';
+export { default as TableActionIcons } from './src/components/TableActionIcons.vue';
 export * from './src/types/table';
 export * from './src/types/tableAction';

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

@@ -102,6 +102,7 @@
             :type="item.type"
             :minWidth="item.minWidth"
             :fixed="item.fixed"
+            :selectable="item.selectable"
           />
           <el-table-column v-else :prop="item.prop" :sortable="item.sortable" v-bind="item">
             <template #default="scope" v-if="item.render">
@@ -338,7 +339,7 @@
       rowKey: unref(getRowKey),
       data: tableData,
       size: unref(getTableSize),
-      stripe: unref(getStriped),
+      stripe: unref(getProps).striped || unref(getStriped),
       'max-height': getDeviceHeight.value,
     };
   });

+ 32 - 0
src/components/Table/src/components/TableActionIcons.vue

@@ -0,0 +1,32 @@
+<template>
+  <div class="flex items-center justify-center">
+    <el-space :size="space">
+      <div v-for="item in props.actionIcons" :key="item.label" @click="item.onClick">
+        <el-tooltip :content="item.label" effect="light">
+          <el-icon v-if="props.style === 'icon'" :color="props.color" :size="props.size">
+            <component :is="item.icon" />
+          </el-icon>
+          <img
+            v-if="props.style === 'img'"
+            :src="item.icon"
+            :style="{ width: `${props.size}px` }"
+          />
+        </el-tooltip>
+      </div>
+    </el-space>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ActionItem } from '@/components/Table';
+
+  const props = defineProps<{
+    space: number;
+    size: number;
+    color: string;
+    style: 'img' | 'icon';
+    actionIcons: ActionItem[];
+  }>();
+</script>
+
+<style scoped></style>

+ 2 - 0
src/components/Table/src/types/table.ts

@@ -25,6 +25,8 @@ export interface BasicColumn {
   render?: Function;
   type?: string;
   fixed?: string | boolean;
+  // selection列checkbox是否可选
+  selectable?: ((row: any, index: number) => boolean) | undefined;
 }
 
 export interface TableActionType {

+ 52 - 24
src/views/cameras/overview/CamerasOverview.vue

@@ -1,13 +1,11 @@
 <template>
-  <div>
-    <el-card :bordered="false" class="mb-3 proCard">
-      <ConditionQuery />
-    </el-card>
-    <el-card :bordered="false" class="proCard">
+  <div class="camera-page">
+    <ConditionQuery />
+    <div class="camera-list">
       <BasicTable
         :columns="columns"
         :data-source="cameraItems"
-        :row-key="(row) => row.id"
+        :row-key="(row) => row.name"
         :action-column="actionColumn"
         :pagination="false"
         :tableSetting="{
@@ -17,9 +15,13 @@
           striped: false,
           setting: false,
         }"
+        :striped="true"
         ref="tableRef"
         @order-change="orderByItem"
       >
+        <template #tableTitle>
+          <el-button type="primary" :icon="Plus" @click="showAddPopover = true">添加</el-button>
+        </template>
         <template #empty>
           <div class="empty-content flex flex-col items-center">
             <img :src="emptyImg" class="empty-img" />
@@ -27,22 +29,31 @@
           </div>
         </template>
       </BasicTable>
-    </el-card>
+    </div>
+    <AddCamera class="add-popover" v-model="showAddPopover" />
   </div>
 </template>
 
 <script setup lang="ts">
-  import { h, reactive } from 'vue';
-  import { BasicTable, TableAction } from '@/components/Table';
+  import { h, reactive, ref } from 'vue';
+  import { BasicTable, TableActionIcons } from '@/components/Table';
   import { BasicColumn } from '@/components/Table';
   import { getColumns } from './overviewColumns';
   import ConditionQuery from './components/ConditionQuery.vue';
+  import AddCamera from './components/CameraAddPopover.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';
+
+  // 添加弹窗相关
+  const showAddPopover = ref(true);
 
   const cameraItems = [
     {
       cameraIp: '10.10.10.10',
-      brand: '海康威视',
+      protocal: '海康威视',
       cameraPort: '8080',
       mac: '255.255.255.255',
       name: 'SHGD-XDJS-0001',
@@ -61,28 +72,27 @@
     key: 'action',
     fixed: 'right',
     render(record) {
-      return h(TableAction as any, {
-        style: 'button',
-        space: -10,
-        actions: [
+      return h(TableActionIcons as any, {
+        space: 20,
+        color: '#629bf9',
+        style: 'img',
+        size: 16,
+        actionIcons: [
           {
             label: '预览',
-            isConfirm: false,
-            text: true,
+            icon: previewIcon,
             onClick: handlePreview.bind(null, record.row),
           },
-          {
-            label: '删除',
-            isConfirm: false,
-            text: true,
-            onClick: handleDelete.bind(null, record.row),
-          },
           {
             label: '编辑',
-            isConfirm: false,
-            text: true,
+            icon: editIcon,
             onClick: handleEdit.bind(null, record.row),
           },
+          {
+            label: '删除',
+            icon: deleteIcon,
+            onClick: handleDelete.bind(null, record.row),
+          },
         ],
       });
     },
@@ -103,6 +113,14 @@
 </script>
 
 <style scoped>
+  .camera-page {
+    position: relative;
+    height: calc(100vh - 64px - 12px);
+    background-color: #ffffff;
+  }
+  .camera-list {
+    padding: 0 21px;
+  }
   .empty-content {
     margin: auto;
     padding: 125px 0;
@@ -118,4 +136,14 @@
     line-height: 30px;
     text-align: center;
   }
+
+  .add-popover {
+    position: absolute;
+    width: calc(100% - 21px);
+    height: 622px;
+    top: 0;
+    bottom: 0;
+    margin: auto;
+    z-index: 99;
+  }
 </style>

+ 78 - 0
src/views/cameras/overview/components/AddCameraByIP.vue

@@ -0,0 +1,78 @@
+<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"
+      >
+        <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-option
+            v-for="protocal in item.option"
+            :key="protocal.value"
+            :label="protocal.label"
+            :value="protocal.value"
+          />
+        </el-select>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { computed, ref } from 'vue';
+  import { CameraIPItem } from '../type';
+  import { cameraIPAddForm } from '../constant';
+
+  const cameraIPData = ref<CameraIPItem>({} as CameraIPItem);
+
+  const rules = computed(() => {
+    const newRule = {};
+    cameraIPAddForm.forEach((item) => {
+      if (item.required) {
+        newRule[item.prop] = item.rule;
+      }
+    });
+    return newRule;
+  });
+</script>
+
+<style scoped>
+  .ip-form {
+    width: 768px;
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: space-between;
+    align-content: space-around;
+  }
+
+  :deep(.el-form-item__label) {
+    font-size: 14px;
+    color: #363636;
+    padding: 0;
+  }
+  :deep(.el-form--inline .el-form-item) {
+    display: flex;
+    margin-right: 0;
+    margin-bottom: 28px;
+  }
+</style>

+ 260 - 0
src/views/cameras/overview/components/AddCameraByRange.vue

@@ -0,0 +1,260 @@
+<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"
+      >
+        <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-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>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { computed, h, reactive, ref } from 'vue';
+  import { CameraRangeItem } from '../type';
+  import { cameraRangeAddForm } from '../constant';
+  import SearchCamerasAction from './SearchCamerasAction.vue';
+  import { BasicTable } from '@/components/Table';
+  import { BasicColumn } from '@/components/Table';
+  import { columns } from '../searchRangeColumns';
+  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';
+
+  const cameraRangeData = ref<CameraRangeItem>({} as CameraRangeItem);
+
+  const rules = computed(() => {
+    const newRule = {};
+    cameraRangeAddForm.forEach((item) => {
+      if (item.required) {
+        newRule[item.prop] = item.rule;
+      }
+    });
+    return newRule;
+  });
+
+  //操作列
+  const actionColumn: BasicColumn = reactive({
+    width: 150,
+    title: '操作',
+    prop: 'action',
+    key: 'action',
+    fixed: 'right',
+    render(record) {
+      return h(SearchCamerasAction as any, {
+        activeColor: '#629bf9',
+        unactiveColor: 'rgba(0,0,0,0.4)',
+        tableTextActions: [
+          {
+            label: '添加',
+            disabled: false,
+            onclick: handleAdd.bind(null, record.row),
+          },
+        ],
+        tableIconActions: {
+          space: 10,
+          color: '#629bf9',
+          style: 'img',
+          size: 16,
+          actionIcons: [
+            {
+              label: '编辑',
+              icon: editIcon,
+              onClick: handleEdit.bind(null, record.row),
+            },
+            {
+              label: '删除',
+              icon: deleteIcon,
+              onClick: handleDelete.bind(null, record.row),
+            },
+          ],
+        },
+      });
+    },
+  });
+
+  const getRowClassName = (record) => {
+    return record.row.name !== 'SHGD-XDJS-0003' ? 'warm-row' : '';
+  };
+
+  const cameraItems = reactive([
+    {
+      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,
+    },
+    {
+      cameraIp: '10.10.10.10',
+      protocal: '海康威视',
+      cameraPort: '8080',
+      mac: '255.255.255.255',
+      name: 'SHGD-XDJS-0002',
+      workshopId: 'ARJ21部装车间',
+      workspaceId: '200工位',
+      networkState: 1,
+      status: 1,
+    },
+    {
+      cameraIp: '10.10.10.10',
+      protocal: '海康威视',
+      cameraPort: '8080',
+      mac: '255.255.255.255',
+      name: 'SHGD-XDJS-0003',
+      workshopId: 'ARJ21部装车间',
+      workspaceId: '200工位',
+      networkState: 1,
+      status: 1,
+    },
+    {
+      cameraIp: '10.10.10.10',
+      protocal: '海康威视',
+      cameraPort: '8080',
+      mac: '255.255.255.255',
+      name: 'SHGD-XDJS-0004',
+      workshopId: 'ARJ21部装车间',
+      workspaceId: '200工位',
+      networkState: 1,
+      status: 1,
+    },
+    {
+      cameraIp: '10.10.10.10',
+      protocal: '海康威视',
+      cameraPort: '8080',
+      mac: '255.255.255.255',
+      name: 'SHGD-XDJS-0005',
+      workshopId: 'ARJ21部装车间',
+      workspaceId: '200工位',
+      networkState: 1,
+      status: 1,
+    },
+  ]);
+
+  const searchRangeCamera = () => {};
+
+  // 列排序操作
+  const orderByItem = () => {};
+
+  const handleSelectionChange = (val: any[]) => {
+    console.log(val);
+  };
+
+  const handleAdd = () => {};
+
+  const handleDelete = () => {};
+
+  const handleEdit = () => {};
+</script>
+
+<style scoped>
+  .add-body {
+    width: 100%;
+  }
+
+  .range-form {
+    width: 1082px;
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: space-between;
+    align-content: space-around;
+    margin-bottom: -12px;
+  }
+
+  .btn-r {
+    margin-right: 24px;
+    margin-bottom: 16px;
+  }
+
+  .search-list {
+    width: 100%;
+    height: 290px;
+    padding: 0 28px;
+    margin-bottom: 60px;
+  }
+
+  :deep(.el-form-item__label) {
+    font-size: 14px;
+    color: #363636;
+    padding: 0;
+  }
+  :deep(.el-form--inline .el-form-item) {
+    display: flex;
+    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;
+  }
+</style>

+ 109 - 0
src/views/cameras/overview/components/CameraAddPopover.vue

@@ -0,0 +1,109 @@
+<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>
+        <div class="flex pop-head-tabs">
+          <div
+            v-for="item in cameraAddType"
+            :key="item.value"
+            class="flex justify-center items-center tab-item"
+            :class="{ 'tab-item-active': item.value === addType }"
+            @click="addType = item.value"
+          >
+            {{ item.label }}
+          </div>
+        </div>
+        <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 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>
+    </div>
+  </el-card>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { cameraAddType } from '../constant';
+  import { Close } from '@element-plus/icons-vue';
+  import IPAddCamera from './AddCameraByIP.vue';
+  import RangeAddCamera from './AddCameraByRange.vue';
+
+  const props = defineProps<{ modelValue: boolean }>();
+
+  const emits = defineEmits(['update:modelValue']);
+
+  const addType = ref(cameraAddType[1].value);
+
+  const updateValue = (value) => {
+    emits('update:modelValue', value);
+  };
+
+  const addCamera = () => {};
+</script>
+
+<style scoped lang="scss">
+  .pop-card {
+    margin-left: 21px !important;
+  }
+
+  .pop-head {
+    height: 56px;
+
+    &-name {
+      margin-left: 24px;
+      font-size: 16px;
+      font-weight: 500;
+      color: #252525;
+    }
+
+    &-tabs {
+      margin-top: 18px;
+
+      :first-child {
+        border-radius: 8px 0px 0px 0px;
+      }
+
+      :last-child {
+        border-radius: 0px 8px 0px 0px;
+      }
+    }
+  }
+
+  .tab-item {
+    width: 188px;
+    height: 38px;
+    background: #fafafa;
+    border: 1px solid #d9d9d9;
+
+    &-active {
+      background: #e2eefe;
+      border: 1px solid #1890ff;
+    }
+  }
+
+  .pop-content {
+    height: 566px;
+  }
+
+  .pop-footer {
+    position: absolute;
+    right: 24px;
+    bottom: 27px;
+    display: flex;
+    justify-content: flex-end;
+  }
+
+  :deep(.el-card__header) {
+    padding: 0;
+  }
+  :deep(.el-card__body) {
+    padding: 0;
+  }
+</style>

+ 62 - 21
src/views/cameras/overview/components/ConditionQuery.vue

@@ -1,31 +1,60 @@
 <template>
-  <div class="flex">
-    <el-space alignment="center" :size="20">
-      <span style="font-size: 16px">牌号</span>
-      <el-input
-        :style="{ width: '150px' }"
-        v-model="queryInteriorNs"
-        clearable
-        placeholder="请输入查询牌号"
-      />
+  <div class="flex items-center query-head">
+    <el-space alignment="center" :size="50">
+      <div>
+        <el-select v-model="queryType" placeholder="选择类型" class="type-select">
+          <el-option
+            v-for="item in queryTypeSelect"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+        <el-input
+          :style="{ width: '180px' }"
+          v-model="queryTypeContent"
+          clearable
+          placeholder="请输入查找内容"
+        />
+      </div>
+      <div>
+        <span>协议类型:</span>
+        <el-select v-model="queryProtocalType" placeholder="请选择协议类型" class="protocal-select">
+          <el-option
+            v-for="item in protocalTypeSelect"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+      </div>
+      <div>
+        <span>场景:</span>
+        <el-select v-model="queryWorkShop" placeholder="请选择场景" class="protocal-select">
+          <el-option
+            v-for="item in protocalTypeSelect"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+      </div>
     </el-space>
     <div class="flex-1 flex justify-end">
-      <el-button type="primary" @click="conditionSearch">
-        <template #icon>
-          <el-icon>
-            <SearchOutlined />
-          </el-icon>
-        </template>
-        查询
-      </el-button>
-      <el-button class="mr-20" @click="resetSearch"> 重置 </el-button>
-      <el-button class="mr-5" type="primary">添加</el-button>
+      <el-button type="primary" @click="conditionSearch"> 查询 </el-button>
+      <el-button @click="resetSearch"> 重置 </el-button>
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-  import { SearchOutlined } from '@vicons/antd';
+  import { ref } from 'vue';
+  import { queryTypeSelect, protocalTypeSelect } from '../constant';
+
+  const queryType = ref('');
+  const queryTypeContent = ref('');
+  const queryProtocalType = ref('');
+  const queryWorkShop = ref('');
 
   // 条件查询事件
   const conditionSearch = () => {};
@@ -34,4 +63,16 @@
   const resetSearch = () => {};
 </script>
 
-<style scoped></style>
+<style scoped>
+  .type-select {
+    width: 100px;
+  }
+
+  .protocal-select {
+    width: 160px;
+  }
+
+  .query-head {
+    padding: 24px 57px 18px 21px;
+  }
+</style>

+ 61 - 0
src/views/cameras/overview/components/SearchCamerasAction.vue

@@ -0,0 +1,61 @@
+<template>
+  <div class="flex items-center">
+    <el-space :size="space">
+      <span
+        v-for="(item, index) in props.tableTextActions"
+        :key="item.label"
+        @click="item.onClick"
+        :style="{
+          pointerEvents: disList[index] ? 'none' : 'auto',
+          textDecoration: 'underline',
+          fontSize: '14px',
+          width: '28px',
+          color: disList[index] ? props.unactiveColor : props.activeColor,
+        }"
+      >
+        {{ item.label }}
+      </span>
+    </el-space>
+    <div class="divide"></div>
+    <TableActionIcons v-bind="props.tableIconActions" />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { computed } from 'vue';
+  import { ActionItem } from '@/components/Table';
+  import { TableActionIcons } from '@/components/Table';
+
+  type TableIconActionProp = {
+    space: number;
+    size: number;
+    color: string;
+    style: 'img' | 'icon';
+    actionIcons: ActionItem[];
+  };
+
+  type TextBtnItem = {
+    label: string;
+    disabled: boolean;
+    onClick?: Fn;
+  };
+
+  const props = defineProps<{
+    space?: number;
+    activeColor: string;
+    unactiveColor: string;
+    tableTextActions: TextBtnItem[];
+    tableIconActions: TableIconActionProp;
+  }>();
+
+  const disList = computed(() => props.tableTextActions.map((item) => item.disabled));
+</script>
+
+<style scoped>
+  .divide {
+    width: 1px;
+    height: 14px;
+    background: #e9e9e9;
+    margin: 0 14px;
+  }
+</style>

+ 189 - 0
src/views/cameras/overview/constant.ts

@@ -0,0 +1,189 @@
+export const queryTypeSelect = [
+  {
+    value: 'cameraIp',
+    label: 'IP地址',
+  },
+  {
+    value: 'name',
+    label: '设备ID',
+  },
+];
+
+export const protocalTypeSelect = [
+  {
+    value: 'haikang',
+    label: '海康',
+  },
+  {
+    value: 'dahua',
+    label: '大华',
+  },
+  {
+    value: 'anxus',
+    label: '安迅士',
+  },
+  {
+    value: 'huawei',
+    label: '华为',
+  },
+];
+
+export const cameraAddType = [
+  {
+    value: 'ip',
+    label: 'IP/域名',
+  },
+  {
+    value: 'ipRange',
+    label: 'IP段',
+  },
+];
+
+type CameraAddFormItem = {
+  label: string;
+  prop: string;
+  placeholder: string;
+  type: 'input' | 'select';
+  option?: { value: any; label: any }[];
+  required: boolean;
+  rule?: any[];
+  labelWidth?: string;
+};
+
+export const cameraIPAddForm: CameraAddFormItem[] = [
+  {
+    label: '名称:',
+    prop: 'name',
+    placeholder: '请输入名称',
+    type: 'input',
+    required: true,
+    rule: [{ required: true, message: '请输入名称', trigger: 'blur' }],
+  },
+  {
+    label: 'IP地址:',
+    prop: 'cameraIp',
+    placeholder: '请输入IP地址',
+    type: 'input',
+    required: true,
+    rule: [{ required: true, message: '请输入IP地址', trigger: 'blur' }],
+  },
+  {
+    label: '端口:',
+    prop: 'cameraPort',
+    placeholder: '请输入端口号',
+    type: 'input',
+    required: true,
+    rule: [{ required: true, message: '请输入端口号', trigger: 'blur' }],
+  },
+  {
+    label: '协议类型:',
+    prop: 'protocal',
+    placeholder: '请输入协议类型',
+    type: 'select',
+    required: true,
+    option: protocalTypeSelect,
+    rule: [{ required: true, message: '请输入协议类型', trigger: 'blur' }],
+  },
+  {
+    label: '用户名:',
+    prop: 'user',
+    placeholder: '请输入用户名',
+    type: 'input',
+    required: true,
+    rule: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
+  },
+  {
+    label: '场景:',
+    prop: 'workshopId',
+    placeholder: '请输入场景名称',
+    type: 'input',
+    required: true,
+    rule: [{ required: true, message: '请输入场景名称', trigger: 'blur' }],
+  },
+  {
+    label: '密码:',
+    prop: 'password',
+    placeholder: '请输入用户名密码',
+    type: 'input',
+    required: true,
+    rule: [{ required: true, message: '请输入用户名密码', trigger: 'blur' }],
+  },
+  {
+    label: '工位负责人:',
+    prop: 'workspacer',
+    placeholder: '请输入工位负责人',
+    type: 'input',
+    required: false,
+  },
+  {
+    label: '设备ID号:',
+    prop: 'cameraId',
+    placeholder: '自定义ID,不能重复',
+    type: 'input',
+    required: true,
+    rule: [{ required: true, message: '请输入设备ID号', trigger: 'blur' }],
+  },
+  {
+    label: '备注:',
+    prop: 'remark',
+    placeholder: '请输入备注',
+    type: 'input',
+    required: false,
+  },
+];
+
+export const cameraRangeAddForm: CameraAddFormItem[] = [
+  {
+    label: '启用IP地址:',
+    prop: 'startIp',
+    placeholder: '请输入启用IP地址',
+    type: 'input',
+    labelWidth: '96px',
+    required: true,
+    rule: [{ required: true, message: '请输入启用IP地址', trigger: 'blur' }],
+  },
+  {
+    label: '协议类型:',
+    prop: 'cameraType',
+    placeholder: '请输入协议类型',
+    type: 'select',
+    required: true,
+    option: protocalTypeSelect,
+    rule: [{ required: true, message: '请输入协议类型', trigger: 'blur' }],
+  },
+  {
+    label: '用户名:',
+    prop: 'user',
+    placeholder: '请输入用户名',
+    type: 'input',
+    labelWidth: '60px',
+    required: false,
+    rule: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
+  },
+  {
+    label: '结束IP地址:',
+    prop: 'endId',
+    placeholder: '请输入结束IP地址',
+    type: 'input',
+    labelWidth: '96px',
+    required: true,
+    rule: [{ required: true, message: '请输入结束IP地址', trigger: 'blur' }],
+  },
+  {
+    label: '端口:',
+    prop: 'cameraPort',
+    placeholder: '请输入端口号',
+    type: 'input',
+    required: false,
+    rule: [{ required: true, message: '请输入端口号', trigger: 'blur' }],
+  },
+  {
+    label: '密码:',
+    prop: 'password',
+    placeholder: '请输入用户名密码',
+    type: 'input',
+    labelWidth: '60px',
+    required: false,
+    rule: [{ required: true, message: '请输入用户名密码', trigger: 'blur' }],
+  },
+];

+ 8 - 5
src/views/cameras/overview/overviewColumns.ts

@@ -1,8 +1,8 @@
 import { h } from 'vue';
 import { ElSwitch } from 'element-plus';
 import type { BasicColumn } from '@/components/Table';
-import connectedIcon from '@/assets/images/logo.png';
-import unConnectedIcon from '@/assets/images/login-pic.png';
+import connectedIcon from '@/assets/images/table/camera-netConnect.png';
+import unConnectedIcon from '@/assets/images/table/camera-netUnconnect.png';
 
 export const getColumns = (switchChange: () => unknown): BasicColumn[] => {
   return [
@@ -18,8 +18,8 @@ export const getColumns = (switchChange: () => unknown): BasicColumn[] => {
       minWidth: 80,
     },
     {
-      label: '品牌',
-      prop: 'brand',
+      label: '协议类型',
+      prop: 'protocal',
       minWidth: 60,
     },
     {
@@ -55,12 +55,14 @@ export const getColumns = (switchChange: () => unknown): BasicColumn[] => {
           'img',
           {
             src: record.row.networkState === 0 ? unConnectedIcon : connectedIcon,
-            style: 'width: 20px; height: 20px; object-fit: fill;',
+            style:
+              'width: 20px; height: 20px; object-fit: fill; display: inline-block; line-height: 20px; vertical-align: middle;',
           },
           {},
         );
       },
       minWidth: 80,
+      align: 'center',
       sortable: 'custom',
     },
     {
@@ -81,6 +83,7 @@ export const getColumns = (switchChange: () => unknown): BasicColumn[] => {
         );
       },
       minWidth: 100,
+      align: 'center',
       sortable: 'custom',
     },
   ];

+ 53 - 0
src/views/cameras/overview/searchRangeColumns.ts

@@ -0,0 +1,53 @@
+import type { BasicColumn } from '@/components/Table';
+
+export const columns: BasicColumn[] = [
+  {
+    minWidth: 30,
+    type: 'selection',
+    fixed: 'left',
+    selectable(row, _) {
+      return row.name === 'SHGD-XDJS-0003';
+    },
+  },
+  {
+    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,
+  },
+];

+ 21 - 0
src/views/cameras/overview/type.ts

@@ -0,0 +1,21 @@
+export interface CameraIPItem {
+  name: string;
+  cameraIp: string;
+  cameraPort: string;
+  protocal: string;
+  user: string;
+  workshopId: string;
+  password: string;
+  workspacer: string;
+  cameraId: string;
+  remark?: string;
+}
+
+export interface CameraRangeItem {
+  startIp: string;
+  endId: string;
+  cameraType: string;
+  port?: string;
+  user?: string;
+  password: string;
+}