zhudie %!s(int64=2) %!d(string=hai) anos
pai
achega
d9216b0fee

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


+ 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">

+ 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: () => ({

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

@@ -2,18 +2,29 @@
   <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>
         </template>
         <template #empty>
           <div class="empty-content flex flex-col items-center">
@@ -25,153 +36,170 @@
     </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" />
   </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 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';
+
+  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-popover {
+    position: absolute;
+    width: calc(100% - 21px);
+    height: 622px;
+    top: 0;
+    bottom: 0;
+    margin: auto;
+    z-index: 99;
+  }
 </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>

+ 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: 111,
+        networkingState: 0,
+      },
+      {
+        name: 'C204',
+        account: 111,
+        networkingState: 0,
+      },
+      {
+        name: 'C205',
+        account: 111,
+        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 = 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>

+ 149 - 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',
@@ -187,3 +193,146 @@ 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: 'blur' }],
+  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) {
+      return h(
+        ElTag,
+        {
+          //  record.row.networkingState
+        },
+        {
+          default: () => {
+            return record.row.networkingState === 0 ? '连接' : '断开连接';
+          },
+        },
+      );
+    },
+    minWidth: 120,
+  },
+];

+ 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[];
+}