Просмотр исходного кода

Merge branch 'shareCamera' into 'master'

Share camera

See merge request tian-group/skyeye-admin-fe!50
孙宏耀 2 лет назад
Родитель
Сommit
6e87ee6945

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


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

@@ -1,5 +1,6 @@
 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 { default as TableActionIconsWords } from './src/components/TableActionIconsWords.vue';
 export * from './src/types/table';
 export * from './src/types/tableAction';

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

@@ -87,6 +87,7 @@
         v-bind="getBindValues"
         v-loading="tableLoading"
         @sort-change="sortChange"
+        :show-header="props.showHeader"
         :expand-row-keys="$props.expendRow"
       >
         <template v-for="item in getTableColumns" :key="item.key">

+ 31 - 0
src/components/Table/src/components/TableActionIconsWords.vue

@@ -0,0 +1,31 @@
+<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="item.type === 'icon'" :color="props.color" :size="props.size">
+            <component :is="item.icon" />
+          </el-icon>
+          <img v-if="item.type === 'img'" :src="item.icon" :style="{ width: `${props.size}px` }" />
+        </el-tooltip>
+        <div v-if="item.type === 'words'" style="color: rgb(17, 153, 232); cursor: pointer">{{
+          item.label
+        }}</div>
+      </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>

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

@@ -70,6 +70,10 @@ export const basicProps = {
     type: Boolean,
     default: true,
   },
+  showHeader: {
+    type: Boolean,
+    default: true,
+  },
   tableSetting: {
     type: Object as PropType<TableSetting>,
     default: () => ({

+ 186 - 144
src/views/cameras/overview/CamerasOverview.vue

@@ -2,18 +2,30 @@
   <div class="camera-page">
     <ConditionQuery />
     <div class="camera-list">
-      <BasicTable :columns="columns" :data-source="cameraItems" :row-key="(row) => row.code" :action-column="actionColumn"
-        :pagination="{ total: total, pageSize: size, hideOnSinglePage: !cameraItems.length }" :loading="loading"
+      <BasicTable
+        :columns="columns"
+        :data-source="cameraItems"
+        :row-key="(row) => row.code"
+        :action-column="actionColumn"
+        :pagination="{ total: total, pageSize: size, hideOnSinglePage: !cameraItems.length }"
+        :loading="loading"
         :tableSetting="{
           size: false,
           redo: false,
           fullscreen: false,
           striped: false,
           setting: false,
-        }" :striped="true" ref="tableRef" @order-change="orderByItem" @page-num-change="handlePageNumChange"
-        @page-size-change="handlePageSizeChange">
+        }"
+        :striped="true"
+        ref="tableRef"
+        @order-change="orderByItem"
+        @page-num-change="handlePageNumChange"
+        @page-size-change="handlePageSizeChange"
+      >
         <template #tableTitle>
           <el-button type="primary" :icon="Plus" @click="showAddPopover = true">添加</el-button>
+          <el-button color="#1890FF" @click="showSharedPopover = true" plain>共享相机</el-button>
+          <div class="add-tip" v-if="unAddlength > 0">{{ unAddlength }}</div>
         </template>
         <template #empty>
           <div class="empty-content flex flex-col items-center">
@@ -25,153 +37,183 @@
     </div>
     <AddCamera class="add-popover" v-model="showAddPopover" />
     <EditCamera class="add-popover" v-model="showEditPopover" :edit-data="editCameraData" />
+    <ShareCamera class="add-popover" v-model="addSharedPopover" :share-data="shareCameraData" />
+    <EditSharedCamera class="add-popover" v-model="showSharedPopover" />
   </div>
 </template>
 
 <script setup lang="ts">
-import { h, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
-import { BasicTable, TableActionIcons } from '@/components/Table';
-import { BasicColumn } from '@/components/Table';
-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';
-import { deleteCameraItem } from '@/api/camera/camera-overview';
-import { ElMessage, ElMessageBox } from 'element-plus';
-
-const cameraOverview = useCameraOverview();
-const { cameraItems, loading, total, page, size } = storeToRefs(cameraOverview);
-const { getCameraItems, openInterval, closeInterval } = cameraOverview;
-
-// 添加弹窗相关
-const showAddPopover = ref(false);
-
-const showEditPopover = ref(false);
-const editCameraData = ref<CameraIPItem | null>();
-
-//操作列
-const actionColumn: BasicColumn = reactive({
-  width: 150,
-  title: '操作',
-  prop: 'action',
-  key: 'action',
-  fixed: 'right',
-  render(record) {
-    return h(TableActionIcons as any, {
-      space: 20,
-      color: '#629bf9',
-      style: 'img',
-      size: 16,
-      actionIcons: [
-        {
-          label: '预览',
-          icon: previewIcon,
-          onClick: handlePreview.bind(null, record.row),
-        },
-        {
-          label: '编辑',
-          icon: editIcon,
-          onClick: handleEdit.bind(null, record.row),
-        },
-        {
-          label: '删除',
-          icon: deleteIcon,
-          onClick: handleDelete.bind(null, record.row),
-        },
-      ],
-    });
-  },
-});
-
-// 列排序操作
-const orderByItem = () => { };
-
-const handlePageNumChange = (pageNum) => {
-  page.value = pageNum;
-  getCameraItems();
-};
-
-const handlePageSizeChange = (pageSize) => {
-  size.value = pageSize;
-  getCameraItems();
-};
-
-const handlePreview = () => { };
-
-const handleDelete = (row) => {
-  ElMessageBox.confirm(`您想删除相机${row.code}`, '提示', {
-    confirmButtonText: '确定',
-    cancelButtonText: '取消',
-    type: 'warning',
-    draggable: true,
-  })
-    .then(() => {
-      deleteCameraItem({ cameraId: row.id }).then(() => {
-        ElMessage.success('删除成功');
-
-        getCameraItems();
+  import { h, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
+  import { BasicTable, TableActionIcons } from '@/components/Table';
+  import { BasicColumn } from '@/components/Table';
+  import { columns } from './overviewColumns';
+  import ConditionQuery from './components/ConditionQuery.vue';
+  import AddCamera from './components/CameraAddPopover.vue';
+  import ShareCamera from './components/CameraSharePopover.vue';
+  import EditCamera from './components/CameraEditPopover.vue';
+  import EditSharedCamera from './components/CameraSharedEdit.vue';
+  import emptyImg from '@/assets/images/table/table-empty.png';
+  import { Plus } from '@element-plus/icons-vue';
+  import shareIcon from '@/assets/images/table/table-share.png';
+  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, CameraShowItem } from './type';
+  import { deleteCameraItem } from '@/api/camera/camera-overview';
+  import { ElMessage, ElMessageBox } from 'element-plus';
+  import useCameraShared from './stores/useSharedCamera';
+
+  const CameraShared = useCameraShared();
+  const { unAddlength } = storeToRefs(CameraShared);
+
+  const cameraOverview = useCameraOverview();
+  const { cameraItems, loading, total, page, size } = storeToRefs(cameraOverview);
+  const { getCameraItems, openInterval, closeInterval } = cameraOverview;
+
+  // 添加弹窗相关
+  const showAddPopover = ref(false);
+
+  const showSharedPopover = ref(false);
+  const addSharedPopover = ref(false);
+
+  const showEditPopover = ref(false);
+  const editCameraData = ref<CameraIPItem | null>();
+  const shareCameraData = ref<CameraShowItem | null>();
+
+  //操作列
+  const actionColumn: BasicColumn = reactive({
+    width: 200,
+    title: '操作',
+    prop: 'action',
+    key: 'action',
+    fixed: 'right',
+    render(record) {
+      return h(TableActionIcons as any, {
+        space: 20,
+        color: '#629bf9',
+        style: 'img',
+        size: 16,
+        actionIcons: [
+          {
+            label: '分享',
+            icon: shareIcon,
+            onClick: handleShare.bind(null, record.row),
+          },
+          {
+            label: '预览',
+            icon: previewIcon,
+            onClick: handlePreview.bind(null, record.row),
+          },
+          {
+            label: '编辑',
+            icon: editIcon,
+            onClick: handleEdit.bind(null, record.row),
+          },
+          {
+            label: '删除',
+            icon: deleteIcon,
+            onClick: handleDelete.bind(null, record.row),
+          },
+        ],
       });
+    },
+  });
+
+  // 列排序操作
+  const orderByItem = () => {};
+
+  const handlePageNumChange = (pageNum) => {
+    page.value = pageNum;
+    getCameraItems();
+  };
+
+  const handlePageSizeChange = (pageSize) => {
+    size.value = pageSize;
+    getCameraItems();
+  };
+
+  const handleShare = (row) => {
+    addSharedPopover.value = true;
+    shareCameraData.value = row;
+  };
+
+  const handlePreview = (_row) => {};
+
+  const handleDelete = (row) => {
+    ElMessageBox.confirm(`您想删除相机${row.code}`, '提示', {
+      confirmButtonText: '确定',
+      cancelButtonText: '取消',
+      type: 'warning',
+      draggable: true,
     })
-    .catch(() => { });
-};
-
-const handleEdit = (row) => {
-  showEditPopover.value = true;
-  editCameraData.value = row;
-};
-
-onMounted(() => {
-  getCameraItems();
-  openInterval();
-});
-
-onBeforeUnmount(() => {
-  closeInterval();
-});
+      .then(() => {
+        deleteCameraItem({ cameraId: row.id }).then(() => {
+          ElMessage.success('删除成功');
+
+          getCameraItems();
+        });
+      })
+      .catch(() => {});
+  };
+
+  const handleEdit = (row) => {
+    showEditPopover.value = true;
+    editCameraData.value = row;
+  };
+
+  onMounted(() => {
+    getCameraItems();
+    openInterval();
+  });
+
+  onBeforeUnmount(() => {
+    closeInterval();
+  });
 </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;
-}
-
-.empty-img {
-  width: 396px;
-}
-
-.empty-text {
-  font-size: 22px;
-  color: #8e8e8e;
-  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;
-}
+  .camera-page {
+    position: relative;
+    height: calc(100vh - 64px - 12px);
+    background-color: #ffffff;
+  }
+
+  .camera-list {
+    padding: 0 21px;
+  }
+
+  .empty-content {
+    margin: auto;
+    padding: 125px 0;
+  }
+
+  .empty-img {
+    width: 396px;
+  }
+
+  .empty-text {
+    font-size: 22px;
+    color: #8e8e8e;
+    line-height: 30px;
+    text-align: center;
+  }
+
+  .add-tip {
+    position: absolute;
+    left: 187px;
+    top: 64px;
+    font-size: 16px;
+    color: red;
+  }
+  .add-popover {
+    position: absolute;
+    width: calc(100% - 21px);
+    height: 622px;
+    top: 0;
+    bottom: 0;
+    margin: auto;
+    z-index: 99;
+  }
 </style>

+ 94 - 0
src/views/cameras/overview/components/CameraEditshared.vue

@@ -0,0 +1,94 @@
+<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">
+      <EditSharedCamera
+        @cancel-execute="updateValue(false)"
+        @confirm-execute="onAddCamera"
+        :form-data="props.editData"
+      />
+    </div>
+  </el-card>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { CameraSharedItem, cameraAddType } from '../constant';
+  import { Close } from '@element-plus/icons-vue';
+  import EditSharedCamera from './EditSharedCamera.vue';
+
+  const props = defineProps<{ modelValue: boolean; editData: CameraSharedItem }>();
+
+  const emits = defineEmits(['update:modelValue', 'updateData']);
+
+  const addType = ref(cameraAddType[0].value);
+
+  const updateValue = (value) => {
+    addType.value = cameraAddType[0].value;
+    emits('update:modelValue', value);
+  };
+
+  const onAddCamera = (data) => {
+    updateValue(false);
+    emits('updateData', data);
+  };
+</script>
+
+<style scoped lang="scss">
+  .pop-card {
+    position: relative;
+    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;
+    cursor: pointer;
+
+    &-active {
+      background: #e2eefe;
+      border: 1px solid #1890ff;
+    }
+  }
+
+  .pop-content {
+    height: 566px;
+  }
+
+  :deep(.el-card__header) {
+    padding: 0;
+  }
+  :deep(.el-card__body) {
+    padding: 0;
+  }
+</style>

+ 86 - 0
src/views/cameras/overview/components/CameraSharePopover.vue

@@ -0,0 +1,86 @@
+<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">
+      <ShareCameraDetail :form-data="props.shareData" />
+    </div>
+  </el-card>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { cameraAddType } from '../constant';
+  import { Close } from '@element-plus/icons-vue';
+  import ShareCameraDetail from './ShareCameraDetail.vue';
+  import { CameraShowItem } from '../type';
+
+  const props = defineProps<{ modelValue: boolean; shareData?: CameraShowItem | null }>();
+
+  const emits = defineEmits(['update:modelValue']);
+
+  const addType = ref(cameraAddType[0].value);
+
+  const updateValue = (value) => {
+    addType.value = cameraAddType[0].value;
+    emits('update:modelValue', value);
+  };
+</script>
+
+<style scoped lang="scss">
+  .pop-card {
+    position: relative;
+    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;
+    cursor: pointer;
+
+    &-active {
+      background: #e2eefe;
+      border: 1px solid #1890ff;
+    }
+  }
+
+  .pop-content {
+    // height: 566px;
+  }
+
+  :deep(.el-card__header) {
+    padding: 0;
+  }
+  :deep(.el-card__body) {
+    padding: 0;
+  }
+</style>

+ 94 - 0
src/views/cameras/overview/components/CameraSharedEdit.vue

@@ -0,0 +1,94 @@
+<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 sharedCaremaType"
+            :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><SharedTable :addCameraType="addType" /></div>
+  </el-card>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { sharedCaremaType } from '../constant';
+  import { Close } from '@element-plus/icons-vue';
+  import SharedTable from './SharedTable.vue';
+
+  const props = defineProps<{ modelValue: boolean }>();
+
+  const emits = defineEmits(['update:modelValue']);
+
+  const addType = ref(sharedCaremaType[0].value);
+
+  const updateValue = (value) => {
+    addType.value = sharedCaremaType[0].value;
+    emits('update:modelValue', value);
+  };
+</script>
+
+<style scoped lang="scss">
+  .pop-card {
+    position: relative;
+    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;
+    cursor: pointer;
+
+    &-active {
+      background: #e2eefe;
+      border: 1px solid #1890ff;
+    }
+  }
+
+  .pop-content {
+    height: 566px;
+  }
+
+  :deep(.el-card__header) {
+    padding: 0;
+  }
+  :deep(.el-card__body) {
+    padding: 0;
+  }
+</style>

+ 101 - 0
src/views/cameras/overview/components/EditSharedCamera.vue

@@ -0,0 +1,101 @@
+<template>
+  <div>
+    <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 cameraSharedForm"
+          :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"
+            :type="item.prop === 'password' ? 'password' : ''"
+            :show-password="item.prop === 'password'"
+            :disabled="item.disabled"
+          />
+        </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, onBeforeMount, ref } from 'vue';
+  import { CameraSharedItem, cameraSharedForm } from '../constant';
+  import { cloneDeep } from 'lodash-es';
+
+  const props = defineProps<{ formData?: CameraSharedItem | null }>();
+
+  const emits = defineEmits(['cancel-execute', 'confirm-execute']);
+
+  const cameraIPData = ref<CameraSharedItem>({} as CameraSharedItem);
+
+  const rules = computed(() => {
+    const newRule = {};
+    cameraSharedForm.forEach((item) => {
+      if (item.required) {
+        newRule[item.prop] = item.rule;
+      }
+    });
+    return newRule;
+  });
+
+  const handleCancel = () => {
+    emits('cancel-execute');
+  };
+
+  const handleConfirm = () => {
+    const copyData = cloneDeep(cameraIPData.value);
+    emits('confirm-execute', copyData);
+  };
+
+  onBeforeMount(() => {
+    if (props.formData) {
+      cameraIPData.value = props.formData;
+    }
+  });
+</script>
+
+<style scoped>
+  .ip-form {
+    width: 768px;
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: space-between;
+    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;
+    padding: 0;
+  }
+  :deep(.el-form--inline .el-form-item) {
+    display: flex;
+    margin-right: 0;
+    margin-bottom: 28px;
+  }
+</style>

+ 233 - 0
src/views/cameras/overview/components/ShareCameraDetail.vue

@@ -0,0 +1,233 @@
+<template>
+  <div>
+    <div style="margin-top: 28px">
+      <el-form
+        class="ip-form"
+        :inline="true"
+        :model="shareData"
+        :rules="rules"
+        label-width="84px"
+        label-position="left"
+      >
+        <el-form-item label="企业账号:" prop="account" style="margin-right: 100px">
+          <el-input
+            v-model="shareData.account"
+            placeholder="请输入企业账号"
+            class="company-input"
+          />
+          <el-button
+            type="primary"
+            size="small"
+            plain
+            style="margin-left: 10px"
+            @click="verifyAccount"
+            >验证</el-button
+          >
+        </el-form-item>
+        <el-form-item label="权限设置:" prop="authority">
+          <el-radio-group v-model="shareData.authority" class="ml-4">
+            <el-radio v-for="item in authorityTypeSelect" :label="item.value" :key="item.value">{{
+              item.label
+            }}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item
+          label="企业名称:"
+          prop="name"
+          class="company-name"
+          style="margin-right: 160px"
+        >
+          <el-input
+            v-model="shareData.name"
+            placeholder="请输入企业账号"
+            readonly
+            style="width: 200px; height: 32px"
+          />
+          <div v-if="shareData.name" class="verify-success">账号验证成功</div>
+        </el-form-item>
+        <el-form-item label="分享时间:" prop="time">
+          <el-radio-group v-model="shareData.time" class="ml-4" @change="changeTime">
+            <el-radio v-for="item in timeTypeSelect" :label="item.value" :key="item.value"
+              >{{ item.label }}
+            </el-radio>
+          </el-radio-group>
+          <div
+            ><el-input
+              v-model="shareData.days"
+              placeholder="请输入"
+              :readonly="shareData.time === 'Indefinite'"
+              class="day-input"
+              @change="changeTimeDay"
+            />
+            <span>/天</span></div
+          >
+        </el-form-item>
+        <el-form-item label="备注:" prop="notes">
+          <el-input v-model="shareData.notes" placeholder="请输入备注" style="width: 765px" />
+        </el-form-item>
+        <el-button type="primary" @click="handleConfirm" class="confirm">确定</el-button>
+        <el-form-item label="分享企业:" prop="shareList" class="company-table">
+          <BasicTable
+            :columns="columns"
+            :data-source="shareData.shareList"
+            :row-key="(row) => row.code"
+            :action-column="actionColumn"
+            :showHeader="false"
+            :tableSetting="{
+              size: false,
+              redo: false,
+              fullscreen: false,
+              striped: false,
+              setting: false,
+            }"
+            :striped="true"
+            ref="tableRef"
+            style="height: 200px"
+          />
+        </el-form-item>
+      </el-form>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { h, reactive, ref } from 'vue';
+  import { BasicColumn, BasicTable, TableActionIcons } from '@/components/Table';
+  import { CameraShareItem, CameraShowItem } from '../type';
+  import editIcon from '@/assets/images/table/table-edit.png';
+  import deleteIcon from '@/assets/images/table/table-delete.png';
+  import { rules, authorityTypeSelect, timeTypeSelect, columns } from '../constant';
+
+  const props = defineProps<{ formData?: CameraShowItem | null }>();
+
+  const shareData = ref<CameraShareItem>({
+    account: undefined,
+    authority: 'look',
+    name: '',
+    time: 'Indefinite',
+    days: undefined,
+    notes: '',
+    shareList: [
+      {
+        name: 'C203',
+        account: 111777777777,
+        networkingState: 0,
+      },
+      {
+        name: 'C204',
+        account: 111888888888,
+        networkingState: 0,
+      },
+      {
+        name: 'C205',
+        account: 111999999999,
+        networkingState: 1,
+      },
+    ],
+  });
+
+  const handleEdit = (_row) => {};
+  const handleDelete = (_row) => {};
+
+  //操作列
+  const actionColumn: BasicColumn = reactive({
+    width: 200,
+    title: '操作',
+    prop: 'action',
+    key: 'action',
+    fixed: 'right',
+    render(record) {
+      return h(TableActionIcons as any, {
+        space: 20,
+        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 verifyAccount = () => {
+    shareData.value.name = 'C' + shareData.value.account;
+  };
+
+  const changeTime = () => {
+    shareData.value.days = undefined;
+  };
+
+  const changeTimeDay = () => {};
+
+  const handleConfirm = () => {
+    const addShareItem = {
+      name: shareData.value.name,
+      account: shareData.value.account!,
+      networkingState: props.formData?.networkingState,
+    };
+    shareData.value.shareList.push(addShareItem);
+  };
+</script>
+
+<style scoped>
+  .ip-form {
+    width: 900px;
+    display: flex;
+    flex-wrap: wrap;
+    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;
+  }
+
+  .company-input {
+    width: 200px;
+    height: 32px;
+  }
+
+  .day-input {
+    width: 64px;
+    height: 32px;
+    margin-left: 10px;
+    margin-right: 4px;
+  }
+  .verify-success {
+    position: absolute;
+    right: -100px;
+
+    /* margin-right: 100px; */
+    color: rgb(142, 215, 33);
+  }
+
+  .confirm {
+    position: relative;
+    left: 800px;
+  }
+
+  .company-table {
+    margin-top: 70px;
+    margin-left: -60px;
+  }
+
+  .company-name {
+    margin-right: 160px;
+    position: relative;
+  }
+</style>

+ 171 - 0
src/views/cameras/overview/components/SharedTable.vue

@@ -0,0 +1,171 @@
+<template>
+  <div class="camera-share">
+    <div class="flex items-center query-head">
+      <el-space alignment="center" :size="50">
+        <div style="display: flex">
+          <div>公司名称:</div>
+          <el-input
+            v-model="queryName"
+            clearable
+            placeholder="请输入公司名称"
+            class="query-content"
+          />
+        </div>
+        <div>
+          <span>公司账号:</span>
+          <el-input
+            v-model="queryAccount"
+            clearable
+            placeholder="请输入公司账号"
+            class="query-content"
+          />
+        </div>
+      </el-space>
+      <div class="flex-1 flex justify-end">
+        <el-button type="primary" @click="queryCameraItems"> 查询 </el-button>
+        <el-button @click="resetSearch"> 重置 </el-button>
+      </div>
+    </div>
+    <div class="camera-list">
+      <BasicTable
+        :columns="columns"
+        :data-source="props.addCameraType === 'complete' ? cameraCompleted : cameraIncompleted"
+        :row-key="(row) => row.cameraIp"
+        :action-column="actionColumn"
+        :tableSetting="{
+          size: false,
+          redo: false,
+          fullscreen: false,
+          striped: false,
+          setting: false,
+        }"
+        :striped="true"
+        ref="tableRef"
+      />
+    </div>
+    <CameraEditshared
+      class="add-popover"
+      v-model="shareEditedPopover"
+      :edit-data="handleEditData"
+      @update-data="updateData"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { h, reactive, ref } from 'vue';
+  import { columns } from '../sharedColumns';
+  import editIcon from '@/assets/images/table/table-edit.png';
+  import deleteIcon from '@/assets/images/table/table-delete.png';
+  import { CameraSharedItem } from '../constant';
+  import { BasicColumn, TableActionIconsWords, BasicTable } from '@/components/Table';
+  import CameraEditshared from '../components/CameraEditshared.vue';
+  import useCameraShared from '../stores/useSharedCamera';
+  import { storeToRefs } from 'pinia';
+
+  const CameraShared = useCameraShared();
+  const { queryName, queryAccount, cameraCompleted, cameraIncompleted } = storeToRefs(CameraShared);
+  const { handleAdd, handleDelete, resetSearch } = CameraShared;
+
+  const props = defineProps<{ addCameraType: string }>();
+
+  const shareEditedPopover = ref<boolean>(false);
+
+  const handleEditData = ref<CameraSharedItem>({} as CameraSharedItem);
+
+  const handleEdit = (row) => {
+    shareEditedPopover.value = true;
+    handleEditData.value = row;
+  };
+
+  const updateData = (data) => {
+    if (props.addCameraType === 'complete') {
+      const indexToEdit = cameraIncompleted.value.findIndex(
+        (item) => item.cameraIp === data.cameraIp,
+      );
+      cameraIncompleted.value[indexToEdit] = data;
+    } else {
+      const indexToEdit = cameraCompleted.value.findIndex(
+        (item) => item.cameraIp === data.cameraIp,
+      );
+      cameraCompleted.value[indexToEdit] = data;
+    }
+  };
+
+  //操作列
+  const actionColumn: BasicColumn = reactive({
+    width: 200,
+    title: '操作',
+    prop: 'action',
+    key: 'action',
+    fixed: 'right',
+    render(record) {
+      return h(TableActionIconsWords as any, {
+        space: 20,
+        color: '#629bf9',
+        style: 'img',
+        size: 16,
+        actionIcons: [
+          {
+            label: '添加',
+            type: 'words',
+            onClick: handleAdd.bind(null, record.row, props.addCameraType),
+          },
+          {
+            label: '编辑',
+            type: 'img',
+            icon: editIcon,
+            onClick: handleEdit.bind(null, record.row),
+          },
+          {
+            label: '删除',
+            type: 'img',
+            icon: deleteIcon,
+            onClick: handleDelete.bind(null, record.row, props.addCameraType),
+          },
+        ],
+      });
+    },
+  });
+
+  const queryCameraItems = () => {};
+</script>
+
+<style scoped>
+  .type-select {
+    width: 100px;
+  }
+
+  .query-content {
+    width: 160px;
+    margin-top: -6px;
+  }
+
+  .protocal-select {
+    width: 160px;
+  }
+
+  .query-head {
+    padding: 24px 57px 18px 21px;
+  }
+
+  .camera-share {
+    position: relative;
+    height: calc(100vh - 64px - 12px);
+    background-color: #ffffff;
+  }
+
+  .camera-list {
+    padding: 0 21px;
+  }
+
+  .add-popover {
+    position: absolute;
+    width: calc(100%);
+    height: 622px;
+    top: -57px;
+    left: -20px;
+    margin: auto;
+    z-index: 99;
+  }
+</style>

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

@@ -1,3 +1,9 @@
+import type { BasicColumn } from '@/components/Table';
+import { h } from 'vue';
+import connectedIcon from '@/assets/images/table/camera-netConnect.png';
+import unConnectedIcon from '@/assets/images/table/camera-netUnconnect.png';
+import { ElTag } from 'element-plus';
+
 export const queryTypeSelect = [
   {
     value: 'cameraIp',
@@ -48,6 +54,7 @@ type CameraAddFormItem = {
   required: boolean;
   rule?: any[];
   labelWidth?: string;
+  disabled?: boolean;
 };
 
 export const cameraIPAddForm: CameraAddFormItem[] = [
@@ -187,3 +194,267 @@ export const cameraRangeAddForm: CameraAddFormItem[] = [
     rule: [{ required: true, message: '请输入用户名密码', trigger: 'blur' }],
   },
 ];
+
+type CameraShareFormItem = {
+  label?: string;
+  prop: string;
+  placeholder?: string;
+  type?: 'input' | 'select' | 'tree-select' | 'radio';
+  option?: { value: any; label: any }[];
+  required?: boolean;
+  rule?: any[];
+  labelWidth?: string;
+};
+
+export const authorityTypeSelect = [
+  {
+    value: 'look',
+    label: '仅可查看',
+  },
+  {
+    value: 'operate',
+    label: '可远程控制',
+  },
+];
+
+export const timeTypeSelect = [
+  {
+    value: 'Indefinite',
+    label: '无限期',
+  },
+  {
+    value: 'definite',
+    label: '有限期',
+  },
+];
+
+export const cameraShareForm: CameraShareFormItem[] = [
+  {
+    label: '企业账号:',
+    prop: 'account',
+    placeholder: '请输入企业账号',
+    type: 'input',
+    required: true,
+    rule: [{ required: true, message: '请输入企业账号', trigger: 'blur' }],
+  },
+  {
+    label: '权限设置:',
+    prop: 'authority',
+    //placeholder: '请输入IP地址',
+    type: 'radio',
+    required: true,
+    option: authorityTypeSelect,
+    rule: [{ required: true, message: '', trigger: 'blur' }],
+  },
+  {
+    label: '企业名称:',
+    prop: 'name',
+    placeholder: '请输入企业名称',
+    type: 'input',
+    required: true,
+    rule: [{ required: true, message: '请输入企业名称', trigger: 'blur' }],
+  },
+  {
+    label: '分享时间:',
+    prop: 'time',
+    //placeholder: '请输入协议类型',
+    type: 'select',
+    required: true,
+    option: timeTypeSelect,
+    rule: [{ required: true, message: '', trigger: 'blur' }],
+  },
+  {
+    label: '备注:',
+    prop: 'notes',
+    placeholder: '请输入备注',
+    type: 'input',
+    required: false,
+    rule: [{ required: true, message: '请输入备注', trigger: 'blur' }],
+  },
+  {
+    label: '分享企业:',
+    prop: 'shareList',
+    // placeholder: '请输入场景名称',
+    // type: 'tree-select',
+    required: true,
+    rule: [{ required: true, message: '', trigger: 'blur' }],
+  },
+];
+
+export const rules = {
+  account: [{ required: true, message: '请输入企业账号', trigger: 'blur' }],
+  authority: [{ required: true, message: '', trigger: 'change' }],
+  name: [{ required: true, message: '请输入企业名称', trigger: 'change' }],
+  time: [{ required: true, message: '', trigger: 'change' }],
+  shareList: [{ required: true, message: '', trigger: 'blur' }],
+};
+
+export const columns: BasicColumn[] = [
+  {
+    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;',
+        },
+        {},
+      );
+    },
+    minWidth: 120,
+    align: 'center',
+    sortable: 'custom',
+  },
+  {
+    label: '名称',
+    prop: 'name',
+    minWidth: 200,
+  },
+  {
+    label: '账号',
+    prop: 'account',
+    minWidth: 200,
+  },
+  {
+    label: '联网状态',
+    prop: 'networkingState',
+    render(record) {
+      const updateNetworkingState = (row) => {
+        if (row.networkingState === 0) {
+          row.networkingState = 1;
+        } else {
+          row.networkingState = 0;
+        }
+      };
+      return h(
+        ElTag,
+        { onClick: () => updateNetworkingState(record.row), style: 'cursor: pointer;' },
+        {
+          default: () => {
+            return record.row.networkingState === 0 ? '连接' : '断开连接';
+          },
+        },
+      );
+    },
+    minWidth: 120,
+  },
+];
+
+export const sharedCaremaType = [
+  {
+    value: 'incomplete',
+    label: '待添加',
+  },
+  {
+    value: 'complete',
+    label: '已添加',
+  },
+];
+
+export const cameraSharedForm: CameraAddFormItem[] = [
+  {
+    label: '名称:',
+    prop: 'name',
+    placeholder: '请输入名称',
+    type: 'input',
+    required: true,
+    disabled: false,
+    rule: [{ required: true, message: '请输入名称', trigger: 'blur' }],
+  },
+  {
+    label: 'IP地址:',
+    prop: 'cameraIp',
+    placeholder: '请输入IP地址',
+    type: 'input',
+    required: true,
+    disabled: true,
+    rule: [{ required: true, message: '请输入IP地址', trigger: 'blur' }],
+  },
+  {
+    label: '端口:',
+    prop: 'cameraPort',
+    placeholder: '请输入端口号',
+    type: 'input',
+    required: true,
+    disabled: true,
+    rule: [{ required: true, message: '请输入端口号', trigger: 'blur' }],
+  },
+  {
+    label: '协议类型:',
+    prop: 'cameraType',
+    placeholder: '请输入协议类型',
+    type: 'input',
+    required: true,
+    disabled: true,
+    rule: [{ required: true, message: '请输入协议类型', trigger: 'blur' }],
+  },
+  {
+    label: '用户名:',
+    prop: 'username',
+    placeholder: '请输入用户名',
+    type: 'input',
+    required: true,
+    disabled: true,
+    rule: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
+  },
+  {
+    label: '场景:',
+    prop: 'workshopName',
+    placeholder: '请输入场景名称',
+    type: 'input',
+    required: true,
+    disabled: false,
+    rule: [{ required: true, message: '请输入场景名称', trigger: 'blur' }],
+  },
+  {
+    label: '密码:',
+    prop: 'password',
+    placeholder: '请输入用户名密码',
+    type: 'input',
+    required: true,
+    disabled: true,
+    rule: [{ required: true, message: '请输入用户名密码', trigger: 'blur' }],
+  },
+  {
+    label: '企业账号:',
+    prop: 'companyAccount',
+    placeholder: '请输入企业账号',
+    type: 'input',
+    disabled: true,
+    required: false,
+  },
+  {
+    label: '设备ID号:',
+    prop: 'code',
+    placeholder: '自定义ID,不能重复',
+    type: 'input',
+    required: true,
+    disabled: false,
+    rule: [{ required: true, message: '请输入设备ID号', trigger: 'blur' }],
+  },
+  {
+    label: '企业名称:',
+    prop: 'companyName',
+    placeholder: '请输入企业名称',
+    type: 'input',
+    disabled: true,
+    required: false,
+  },
+];
+
+export interface CameraSharedItem {
+  cameraIp: string; //ip地址
+  cameraType: string; //协议类型
+  cameraPort: string; //端口地址
+  companyAccount: number; //企业账号
+  companyName: string; //企业名称
+  code: string; //设备ID
+  workshopName: string; //车间场景
+  workspaceName: string; //工位场景
+  shareState: string; //是否添加
+  password?: string; //密码
+  username?: string; //用户名
+}

+ 62 - 0
src/views/cameras/overview/sharedColumns.ts

@@ -0,0 +1,62 @@
+import { h } from 'vue';
+import type { BasicColumn } from '@/components/Table';
+import { protocalTypeSelect } from './constant';
+
+export const columns: BasicColumn[] = [
+  {
+    label: '序号',
+    minWidth: 60,
+    type: 'index',
+    fixed: 'left',
+  },
+  {
+    label: 'IP地址',
+    prop: 'cameraIp',
+    minWidth: 140,
+  },
+  {
+    label: '协议类型',
+    prop: 'cameraType',
+    minWidth: 120,
+    render(record) {
+      return h(
+        'span',
+        {},
+        {
+          default: () =>
+            protocalTypeSelect.find((item) => item.value === record.row.cameraType)?.label,
+        },
+      );
+    },
+  },
+  {
+    label: '端口地址',
+    prop: 'cameraPort',
+    minWidth: 120,
+  },
+  {
+    label: '企业账号',
+    prop: 'companyAccount',
+    minWidth: 140,
+  },
+  {
+    label: '企业名称',
+    prop: 'companyName',
+    minWidth: 140,
+  },
+  {
+    label: '设备ID',
+    prop: 'code',
+    minWidth: 150,
+  },
+  {
+    label: '车间场景',
+    prop: 'workshopName',
+    minWidth: 140,
+  },
+  {
+    label: '工位场景',
+    prop: 'workspaceName',
+    minWidth: 140,
+  },
+];

+ 108 - 0
src/views/cameras/overview/stores/useSharedCamera.ts

@@ -0,0 +1,108 @@
+import { computed, ref } from 'vue';
+import { defineStore } from 'pinia';
+import { CameraSharedItem } from '../constant';
+import { ElMessage } from 'element-plus';
+
+export const useCameraShare = defineStore('camera-share', () => {
+  const cameraCompleted = ref<CameraSharedItem[]>([
+    {
+      cameraIp: '172.13.25.0000',
+      cameraType: 'haikang',
+      cameraPort: '11',
+      companyAccount: 111,
+      companyName: '1111',
+      code: 'C919-140',
+      workshopName: 'C919车间',
+      workspaceName: '东侧',
+      shareState: 'complete',
+      password: '11',
+      username: '22',
+    },
+  ]);
+
+  const cameraIncompleted = ref<CameraSharedItem[]>([
+    {
+      cameraIp: '172.13.25.0011',
+      cameraType: 'haikang',
+      cameraPort: '11',
+      companyAccount: 111,
+      companyName: '1111',
+      code: 'C919-141',
+      workshopName: 'C919车间',
+      workspaceName: '西侧',
+      shareState: 'incomplete',
+      password: '11',
+      username: '22',
+    },
+    {
+      cameraIp: '172.13.25.0009',
+      cameraType: 'haikang',
+      cameraPort: '11',
+      companyAccount: 111,
+      companyName: '1111',
+      code: 'C919-142',
+      workshopName: 'C919车间',
+      workspaceName: '北侧',
+      shareState: 'incomplete',
+      password: '11',
+      username: '22',
+    },
+  ]);
+
+  const queryName = ref<string>();
+  const queryAccount = ref<string>();
+
+  const unAddlength = computed(() => {
+    return cameraIncompleted.value.length;
+  });
+
+  const handleAdd = (row, addCameraType) => {
+    if (addCameraType === 'complete') return;
+    const currentData = cameraIncompleted.value.find((item) => (item.cameraIp = row.cameraIp));
+    const indexToRemove = cameraIncompleted.value.findIndex(
+      (item) => item.cameraIp === row.cameraIp,
+    );
+    if (indexToRemove !== -1) {
+      cameraIncompleted.value.splice(indexToRemove, 1);
+    }
+    if (currentData) {
+      cameraCompleted.value.push(currentData!);
+    }
+    ElMessage({
+      message: '添加成功',
+      type: 'success',
+    });
+  };
+
+  const handleDelete = (row, addCameraType) => {
+    if (addCameraType === 'complete') {
+      const indexToRemove = cameraCompleted.value.findIndex(
+        (item) => item.cameraIp === row.cameraIp,
+      );
+      cameraCompleted.value.splice(indexToRemove, 1);
+    } else {
+      const indexToRemove = cameraIncompleted.value.findIndex(
+        (item) => item.cameraIp === row.cameraIp,
+      );
+      cameraIncompleted.value.splice(indexToRemove, 1);
+    }
+  };
+
+  const resetSearch = () => {
+    queryName.value = '';
+    queryAccount.value = '';
+  };
+
+  return {
+    queryName,
+    queryAccount,
+    cameraCompleted,
+    cameraIncompleted,
+    unAddlength,
+    handleAdd,
+    handleDelete,
+    resetSearch,
+  };
+});
+
+export default useCameraShare;

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

@@ -56,3 +56,26 @@ export interface CameraRangeItem {
   /** 密码 */
   password: string;
 }
+
+interface CompanyShareItem {
+  name: string; //企业名字
+  account: number; //企业账号
+  networkingState?: number; //摄像机状态
+}
+
+export interface CameraShareItem {
+  /** 企业账号 */
+  account: number | undefined;
+  /** 权限设置 */
+  authority: string;
+  /** 企业名称 */
+  name: string;
+  /** 分享时间 */
+  time?: string;
+  /** 分享的有限期时间 */
+  days?: number;
+  /** 备注 */
+  notes?: string;
+  /** 密码 */
+  shareList: CompanyShareItem[];
+}