Bladeren bron

Merge branch 'dev-bxy' into 'dev'

feat: 完成平台管理-院区监控区域管理菜单

See merge request product-group-fe/sfy-safety-group/sfy-safety!253
毕欣怡 6 maanden geleden
bovenliggende
commit
810076704b

+ 13 - 2
src/api/security-confidentiality-position/index.ts

@@ -35,12 +35,12 @@ export const getConfidentialityPositionList = (data: GetPositionListParams) => {
 };
 
 /**
- * @description: 新增或编辑治安重点部位/保密要害部位
+ * @description: 新增或编辑治安重点部位/保密要害部位/院区重点监控区域
  */
 export interface AddOrUpdatePositionInfoParams {
   id?: number; // 分组id
   groupName: string; // 分组名称
-  type?: number; // 类型:3-治安重点部位,4-保密要害部位
+  type?: number; // 类型:3-治安重点部位,4-保密要害部位,5-院区重点监控区域
   cameraIdList?: number[]; // 相机id列表
 }
 export const addOrUpdatePositionInfo = (data: AddOrUpdatePositionInfoParams) => {
@@ -205,3 +205,14 @@ export const getRecentDaysCaptureRecordCount = (day: number) => {
     method: 'get',
   });
 };
+
+/**
+ * @description: 院区监控区域管理--查询院区监控区域列表
+ */
+export const getSurveillanceAreaList = (data: GetPositionListParams) => {
+  return http.request({
+    url: '/cameraGroup/queryHospitalKeyAreas',
+    method: 'post',
+    data,
+  });
+};

+ 339 - 3
src/views/system/surveillance/PageSurveillance.vue

@@ -1,7 +1,343 @@
 <template>
-  <div>院区重点监控区域</div>
+  <div class="safety-platform-container">
+    <div class="safety-platform-container__header">
+      <div class="breadcrumb-title">院区重点监控区域</div>
+    </div>
+    <div class="safety-platform-container__main">
+      <div class="search-table-container">
+        <header class="disaster-precaution__header">
+          <el-button
+            v-if="surveillanceAreaManagePermission"
+            class="search-table-container--button"
+            type="primary"
+            :icon="Plus"
+            @click="handleAddSurveillanceArea"
+          >
+            新建重点监控区域
+          </el-button>
+          <BasicSearch
+            :searchConfig="SURVEILLANCE_AREA_LIST_SEARCH_CONFIG"
+            :searchData="searchData"
+            :custom-reset="true"
+            @update:search-data="handleSearch"
+            @custom-reset="handleReset"
+          >
+            <template #surveillanceArea>
+              <el-input
+                v-model="searchKeyword"
+                :placeholder="`请输入${curSearchTypeLabel}进行搜索`"
+                clearable
+                @input="handleSearch"
+                @keyup.enter="handleSearch"
+                style="width: 380px"
+              >
+                <template #prefix>
+                  <el-icon color="#1777ff"><Search /></el-icon>
+                </template>
+                <template #prepend>
+                  <el-select
+                    v-model="searchSelectedType"
+                    placeholder="选择搜索项"
+                    @change="handleSelectedTypeChange"
+                    style="width: 100px"
+                  >
+                    <el-option
+                      v-for="item in surveillanceAreaQueryOptions"
+                      :key="item.value"
+                      :label="item.label"
+                      :value="item.value"
+                    />
+                  </el-select>
+                </template>
+              </el-input>
+            </template>
+          </BasicSearch>
+        </header>
+        <BasicTable ref="basicTableRef" :tableData="tableData" :tableConfig="tableConfig">
+          <template #cameraName="scope">
+            <div class="camera-name-container">
+              <div v-for="item in scope.row.children" :key="item.id">
+                <ThumbnailClick
+                  :imageUrl="item.pushStreamDTO.imageUrl"
+                  :code="item.code"
+                  position="right"
+                  @mouse-enter="handleMouseEnter"
+                  @mouse-leave="handleMouseLeave"
+                >
+                  <div
+                    :class="{ active: activeCameraCode === item.code && activePositionId === scope.row.id }"
+                    @click="handleCameraClick(scope.row.id, item.code)"
+                    >{{ item.name }}</div
+                  >
+                </ThumbnailClick>
+              </div>
+            </div>
+          </template>
+          <template #action="{ row, index }">
+            <div class="action-container--div">
+              <ActionButton text="上移" @click="handleUpOne(row, index)" v-if="index > 0" />
+              <ActionButton text="下移" @click="handleDownOne(row, index)" v-if="index < tableData.length - 1" />
+              <ActionButton text="编辑" @click="handleEditSurveillanceArea(row)" />
+              <ActionButton
+                text="删除"
+                :popconfirm="{
+                  title: '是否删除该重点监控区域?',
+                }"
+                @confirm="handleDeleteSurveillanceArea(row.id)"
+              />
+            </div>
+          </template>
+        </BasicTable>
+      </div>
+    </div>
+    <UpdatePositionMonitorCamera
+      v-if="updatePositionMonitorCameraVisible"
+      @confirm="handleConfirmPositionMonitorCamera"
+      @close="handleClosePositionMonitorCamera"
+    />
+  </div>
 </template>
 
-<script setup lang="ts"></script>
+<script setup lang="ts">
+  import { ref, onMounted, reactive, computed } from 'vue';
+  import { ElMessage } from 'element-plus';
+  import { Plus, Search } from '@element-plus/icons-vue';
+  import { storeToRefs } from 'pinia';
+  import BasicSearch from '@/components/BasicSearch.vue';
+  import BasicTable from '@/components/BasicTable.vue';
+  import ActionButton from '@/components/ActionButton.vue';
+  import UpdatePositionMonitorCamera from '@/components/position-monitor-camera-edit/UpdatePositionMonitorCamera.vue';
+  import ThumbnailClick from '@/components/thumbnail/ThumbnailClick.vue';
+  import useTableConfig from '@/hooks/useTableConfigHook';
+  import { useUserInfoHook } from '@/hooks/useUserInfoHook';
+  import { usePositionMonitorCameraEdit } from '@/store/modules/usePositionMonitorCameraEdit';
+  import {
+    FIELDTYPE,
+    FIELD_CONTENT,
+    POSITION_TYPE,
+    surveillanceAreaQueryOptions,
+    SURVEILLANCE_MANAGEMENT_PERMISSIONS,
+  } from './constant';
+  import {
+    SURVEILLANCE_AREA_LIST_TABLE_MAX_HEIGHT_DEFAULT,
+    SURVEILLANCE_AREA_LIST_TABLE_MAX_HEIGHT_PERMISSION,
+    SURVEILLANCE_AREA_LIST_TABLE_OPTIONS,
+    SURVEILLANCE_AREA_LIST_TABLE_COLUMNS,
+    SURVEILLANCE_AREA_LIST_SEARCH_CONFIG,
+  } from './config';
+  import {
+    GetPositionListParams,
+    PositionMonitorCameraListRes,
+    getSurveillanceAreaList,
+    AddOrUpdatePositionInfoParams,
+    addOrUpdatePositionInfo,
+    updateCameraGroupOrder,
+  } from '@/api/security-confidentiality-position';
+  import { deleteCameraGroupApi } from '@/api/nine-square-grid';
 
-<style scoped></style>
+  const { tableConfig } = useTableConfig(SURVEILLANCE_AREA_LIST_TABLE_COLUMNS, SURVEILLANCE_AREA_LIST_TABLE_OPTIONS);
+
+  const { permissions } = useUserInfoHook();
+  const surveillanceAreaManagePermission = ref<boolean>(false);
+
+  const positionMonitorCameraEdit = usePositionMonitorCameraEdit();
+  const {
+    titleOfUpdatePositionMonitorCamera,
+    dataOfPosition,
+    idOfPosition,
+    nameOfPosition,
+    selectedCameraIdsOfPosition,
+  } = storeToRefs(positionMonitorCameraEdit);
+  const { initDataOfPosition, resetPositionMonitorCameraEdit } = positionMonitorCameraEdit;
+
+  const searchData = reactive<GetPositionListParams>({
+    groupName: '',
+    cameraName: '',
+  });
+
+  const searchSelectedType = ref(FIELDTYPE.POSITION_NAME);
+  const searchKeyword = ref('');
+  const curSearchTypeLabel = computed(() => {
+    const option = surveillanceAreaQueryOptions.find((item) => item.value === searchSelectedType.value);
+    return option ? option.label : FIELD_CONTENT[searchSelectedType.value];
+  });
+
+  const tableData = ref<PositionMonitorCameraListRes[]>([]);
+
+  const updatePositionMonitorCameraVisible = ref(false);
+
+  const handleAddSurveillanceArea = () => {
+    titleOfUpdatePositionMonitorCamera.value = '新建重点监控区域';
+    dataOfPosition.value = undefined;
+    initDataOfPosition();
+    updatePositionMonitorCameraVisible.value = true;
+  };
+
+  const handleSearch = () => {
+    if (searchSelectedType.value === FIELDTYPE.POSITION_NAME) searchData.groupName = searchKeyword.value;
+    else searchData.cameraName = searchKeyword.value;
+    getTableData();
+  };
+
+  const handleReset = () => {
+    searchKeyword.value = '';
+    searchSelectedType.value = FIELDTYPE.POSITION_NAME;
+    searchData.cameraName = '';
+    searchData.groupName = '';
+    getTableData();
+  };
+
+  const handleSelectedTypeChange = () => {
+    searchKeyword.value = '';
+  };
+
+  const handleUpOne = (currentRow: PositionMonitorCameraListRes, currentIndex: number) => {
+    // 获取上一行的数据
+    const prevRow = tableData.value[currentIndex - 1];
+    // 构造交换数据数组
+    const swapData = [
+      { id: prevRow.id, orderNum: currentRow.orderNum }, // 上一行使用当前行的orderNum
+      { id: currentRow.id, orderNum: prevRow.orderNum }, // 当前行使用上一行的orderNum
+    ];
+
+    updateCameraGroupOrder(swapData)
+      .then(() => {
+        ElMessage.success('上移成功');
+        getTableData();
+      })
+      .catch(() => {
+        ElMessage.error('上移失败');
+      });
+  };
+
+  const handleDownOne = (currentRow: PositionMonitorCameraListRes, currentIndex: number) => {
+    // 获取下一行的数据
+    const nextRow = tableData.value[currentIndex + 1];
+    // 构造交换数据数组
+    const swapData = [
+      { id: nextRow.id, orderNum: currentRow.orderNum }, // 下一行使用当前行的orderNum
+      { id: currentRow.id, orderNum: nextRow.orderNum }, // 当前行使用下一行的orderNum
+    ];
+
+    updateCameraGroupOrder(swapData)
+      .then(() => {
+        ElMessage.success('下移成功');
+        getTableData();
+      })
+      .catch(() => {
+        ElMessage.error('下移失败');
+      });
+  };
+
+  const handleEditSurveillanceArea = (row: PositionMonitorCameraListRes) => {
+    titleOfUpdatePositionMonitorCamera.value = '编辑重点监控区域';
+    dataOfPosition.value = row;
+    initDataOfPosition();
+    updatePositionMonitorCameraVisible.value = true;
+  };
+
+  const handleDeleteSurveillanceArea = (id: number) => {
+    deleteCameraGroupApi(id).then(() => {
+      ElMessage.success('重点监控区域删除成功');
+      getTableData();
+    });
+  };
+
+  const handleConfirmPositionMonitorCamera = () => {
+    const params: AddOrUpdatePositionInfoParams = {
+      id: idOfPosition.value ?? undefined,
+      groupName: nameOfPosition.value,
+      type: POSITION_TYPE.SURVEILLANCE_AREA,
+      cameraIdList: selectedCameraIdsOfPosition.value.map((item) => item.id),
+    };
+    addOrUpdatePositionInfo(params).then(() => {
+      ElMessage.success(idOfPosition.value ? '重点监控区域编辑成功' : '重点监控区域新建成功');
+      getTableData();
+    });
+    updatePositionMonitorCameraVisible.value = false;
+    resetPositionMonitorCameraEdit();
+  };
+
+  const handleClosePositionMonitorCamera = () => {
+    ElMessage.info('取消操作');
+    updatePositionMonitorCameraVisible.value = false;
+    resetPositionMonitorCameraEdit();
+  };
+
+  const getTableData = () => {
+    tableConfig.loading = true;
+    getSurveillanceAreaList(searchData).then((res) => {
+      tableData.value = res;
+    });
+    tableConfig.loading = false;
+  };
+
+  // 当前选中的重点监控区域id和相机code
+  const activePositionId = ref(0);
+  const activeCameraCode = ref('');
+  const handleCameraClick = (id: number, code: string) => {
+    activePositionId.value = id;
+    activeCameraCode.value = code;
+  };
+  const handleMouseEnter = (code: string) => {
+    activeCameraCode.value = code;
+  };
+  const handleMouseLeave = () => {
+    activeCameraCode.value = '';
+  };
+
+  // 动态生成表格列配置
+  const getTableColumns = () => {
+    if (surveillanceAreaManagePermission.value) {
+      return SURVEILLANCE_AREA_LIST_TABLE_COLUMNS;
+    } else {
+      // 过滤掉操作列
+      return SURVEILLANCE_AREA_LIST_TABLE_COLUMNS.filter(
+        (column) => column.prop !== 'action' && column.type !== 'selection',
+      );
+    }
+  };
+
+  onMounted(() => {
+    getTableData();
+    surveillanceAreaManagePermission.value = Boolean(
+      permissions.find(
+        (item: { code: string }) => item.code === SURVEILLANCE_MANAGEMENT_PERMISSIONS.SURVEILLANCE_MANAGEMENT,
+      ),
+    );
+    tableConfig.maxHeight = surveillanceAreaManagePermission.value
+      ? SURVEILLANCE_AREA_LIST_TABLE_MAX_HEIGHT_PERMISSION
+      : SURVEILLANCE_AREA_LIST_TABLE_MAX_HEIGHT_DEFAULT;
+    tableConfig.columns = getTableColumns();
+  });
+</script>
+
+<style scoped lang="scss">
+  @use '@/styles/page-main-layout.scss' as *;
+  @use '@/styles/page-details-layout.scss' as *;
+  @use '@/styles/basic-table-action.scss' as *;
+
+  .action-container--div {
+    justify-content: flex-start;
+  }
+
+  .camera-name-container {
+    display: flex;
+    gap: 10px;
+    align-items: flex-start;
+    flex-direction: column;
+  }
+
+  .camera-name-container .thumb-nail {
+    color: #1777ff;
+    cursor: pointer;
+  }
+
+  .camera-name-container .thumb-nail:hover {
+    color: #94c1ff;
+  }
+
+  .camera-name-container .active {
+    color: #003f97;
+  }
+</style>

+ 15 - 0
src/views/system/surveillance/config/index.ts

@@ -0,0 +1,15 @@
+import {
+  SURVEILLANCE_AREA_LIST_TABLE_MAX_HEIGHT_DEFAULT,
+  SURVEILLANCE_AREA_LIST_TABLE_MAX_HEIGHT_PERMISSION,
+  SURVEILLANCE_AREA_LIST_TABLE_OPTIONS,
+  SURVEILLANCE_AREA_LIST_TABLE_COLUMNS,
+} from './table';
+import { SURVEILLANCE_AREA_LIST_SEARCH_CONFIG } from './search';
+
+export {
+  SURVEILLANCE_AREA_LIST_TABLE_MAX_HEIGHT_DEFAULT,
+  SURVEILLANCE_AREA_LIST_TABLE_MAX_HEIGHT_PERMISSION,
+  SURVEILLANCE_AREA_LIST_TABLE_OPTIONS,
+  SURVEILLANCE_AREA_LIST_TABLE_COLUMNS,
+  SURVEILLANCE_AREA_LIST_SEARCH_CONFIG,
+};

+ 9 - 0
src/views/system/surveillance/config/search.ts

@@ -0,0 +1,9 @@
+import type { SearchConfig } from '@/types/basic-search';
+
+export const SURVEILLANCE_AREA_LIST_SEARCH_CONFIG: SearchConfig[] = [
+  {
+    label: '',
+    prop: 'surveillanceArea',
+    slot: 'surveillanceArea',
+  },
+];

+ 56 - 0
src/views/system/surveillance/config/table.ts

@@ -0,0 +1,56 @@
+/**
+ * 治安重点部位表格配置
+ */
+import type { TableColumnProps } from '@/types/basic-table';
+
+export const SURVEILLANCE_AREA_LIST_TABLE_MAX_HEIGHT_DEFAULT = 'calc(70vh - 30px)';
+export const SURVEILLANCE_AREA_LIST_TABLE_MAX_HEIGHT_PERMISSION = 'calc(70vh - 80px)';
+
+// 基础表格样式配置
+const TABLE_OPTIONS = {
+  emptyText: '暂无数据',
+  loading: true,
+};
+
+// 应急处置表格样式配置
+export const SURVEILLANCE_AREA_LIST_TABLE_OPTIONS = {
+  ...TABLE_OPTIONS,
+};
+
+// 应急处置表格列配置
+export const SURVEILLANCE_AREA_LIST_TABLE_COLUMNS: TableColumnProps[] = [
+  {
+    label: '序号',
+    prop: 'index',
+    width: '80px',
+    type: 'index',
+    align: 'center',
+  },
+  {
+    label: '监控区域名称',
+    prop: 'groupName',
+    // align: 'center',
+    minWidth: '120px',
+  },
+  {
+    label: '关联相机',
+    prop: 'cameraName',
+    slot: 'cameraName',
+    // align: 'center',
+    minWidth: '180px',
+  },
+  {
+    label: '创建时间',
+    prop: 'createdAt',
+    // align: 'center',
+    width: '200px',
+  },
+  {
+    prop: 'action',
+    label: '操作',
+    // align: 'center',
+    slot: 'action',
+    fixed: 'right',
+    width: '230px',
+  },
+];

+ 37 - 0
src/views/system/surveillance/constant/index.ts

@@ -0,0 +1,37 @@
+/**
+ * @description: 院区监控区域管理 常量对应字段
+ */
+
+// 查询字段对应:1-区域名称,2-相机名称
+export enum FIELDTYPE {
+  POSITION_NAME = 'groupName',
+  CAMERA_NAME = 'cameraName',
+}
+
+export const FIELD_CONTENT = {
+  [FIELDTYPE.POSITION_NAME]: '区域名称',
+  [FIELDTYPE.CAMERA_NAME]: '相机名称',
+};
+
+export const surveillanceAreaQueryOptions = [
+  {
+    label: FIELD_CONTENT[FIELDTYPE.POSITION_NAME],
+    value: FIELDTYPE.POSITION_NAME,
+  },
+  {
+    label: FIELD_CONTENT[FIELDTYPE.CAMERA_NAME],
+    value: FIELDTYPE.CAMERA_NAME,
+  },
+];
+
+// 新增或编辑治安重点部位/保密要害部位type:3-治安重点部位,4-保密要害部位,5-院区重点监控区域
+export enum POSITION_TYPE {
+  SECURITY_POSITION = 3,
+  CONFIDENTIALITY_POSITION = 4,
+  SURVEILLANCE_AREA = 5,
+}
+
+export const SURVEILLANCE_MANAGEMENT_PERMISSIONS = {
+  // 院区监控区域管理权限
+  SURVEILLANCE_MANAGEMENT: 'campus_surveillance_area_module:area_management',
+};