SecurityPosition.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. <template>
  2. <div class="safety-platform-container">
  3. <div class="safety-platform-container__header">
  4. <div class="breadcrumb-title">治安重点部位</div>
  5. </div>
  6. <div class="safety-platform-container__main">
  7. <div class="search-table-container">
  8. <header class="disaster-precaution__header">
  9. <el-button
  10. v-if="securityPositionManagePermission"
  11. class="search-table-container--button"
  12. type="primary"
  13. :icon="Plus"
  14. @click="handleAddSecurityPosition"
  15. >
  16. 新建重点监控部位
  17. </el-button>
  18. <BasicSearch
  19. :searchConfig="SECURITY_POSITION_LIST_SEARCH_CONFIG"
  20. :searchData="searchData"
  21. :custom-reset="true"
  22. @update:search-data="handleSearch"
  23. @custom-reset="handleReset"
  24. >
  25. <template #securityPosition>
  26. <el-input
  27. v-model="searchKeyword"
  28. :placeholder="`请输入${curSearchTypeLabel}进行搜索`"
  29. clearable
  30. @input="handleSearch"
  31. @keyup.enter="handleSearch"
  32. style="width: 380px"
  33. >
  34. <template #prefix>
  35. <el-icon color="#1777ff"><Search /></el-icon>
  36. </template>
  37. <template #prepend>
  38. <el-select
  39. v-model="searchSelectedType"
  40. placeholder="选择搜索项"
  41. @change="handleSelectedTypeChange"
  42. style="width: 100px"
  43. >
  44. <el-option
  45. v-for="item in securityPositionQueryOptions"
  46. :key="item.value"
  47. :label="item.label"
  48. :value="item.value"
  49. />
  50. </el-select>
  51. </template>
  52. </el-input>
  53. </template>
  54. </BasicSearch>
  55. </header>
  56. <BasicTable ref="basicTableRef" :tableData="tableData" :tableConfig="tableConfig">
  57. <template #cameraName="scope">
  58. <div class="camera-name-container">
  59. <div v-for="item in scope.row.children" :key="item.id">
  60. <ThumbnailClick
  61. :imageUrl="item.pushStreamDTO.imageUrl"
  62. :code="item.code"
  63. position="right"
  64. @mouse-enter="handleMouseEnter"
  65. @mouse-leave="handleMouseLeave"
  66. >
  67. <div
  68. :class="{ active: activeCameraCode === item.code && activePositionId === scope.row.id }"
  69. @click="handleCameraClick(scope.row.id, item.code)"
  70. >{{ item.name }}</div
  71. >
  72. </ThumbnailClick>
  73. </div>
  74. </div>
  75. </template>
  76. <template #action="{ row, index }">
  77. <div class="action-container--div">
  78. <ActionButton text="上移" @click="handleUpOne(row, index)" v-if="index > 0" />
  79. <ActionButton text="下移" @click="handleDownOne(row, index)" v-if="index < tableData.length - 1" />
  80. <ActionButton text="编辑" @click="handleEditSecurityPosition(row)" />
  81. <ActionButton
  82. text="删除"
  83. :popconfirm="{
  84. title: '是否删除该治安重点部位?',
  85. }"
  86. @confirm="handleDeleteSecurityPosition(row.id)"
  87. />
  88. </div>
  89. </template>
  90. </BasicTable>
  91. </div>
  92. </div>
  93. <UpdatePositionMonitorCamera
  94. v-if="updatePositionMonitorCameraVisible"
  95. @confirm="handleConfirmPositionMonitorCamera"
  96. @close="handleClosePositionMonitorCamera"
  97. />
  98. </div>
  99. </template>
  100. <script setup lang="ts">
  101. import { ref, onMounted, reactive, computed } from 'vue';
  102. import { ElMessage } from 'element-plus';
  103. import { Plus, Search } from '@element-plus/icons-vue';
  104. import { storeToRefs } from 'pinia';
  105. import BasicSearch from '@/components/BasicSearch.vue';
  106. import BasicTable from '@/components/BasicTable.vue';
  107. import ActionButton from '@/components/ActionButton.vue';
  108. import UpdatePositionMonitorCamera from '@/components/position-monitor-camera-edit/UpdatePositionMonitorCamera.vue';
  109. import ThumbnailClick from '@/components/thumbnail/ThumbnailClick.vue';
  110. import useTableConfig from '@/hooks/useTableConfigHook';
  111. import { useUserInfoHook } from '@/hooks/useUserInfoHook';
  112. import { usePositionMonitorCameraEdit } from '@/store/modules/usePositionMonitorCameraEdit';
  113. import { SECURITY_CONFIDENTIALITY_PERMISSIONS } from '../constant';
  114. import { FIELDTYPE, FIELD_CONTENT, POSITION_TYPE, securityPositionQueryOptions } from './constant';
  115. import {
  116. SECURITY_POSITION_LIST_TABLE_MAX_HEIGHT_DEFAULT,
  117. SECURITY_POSITION_LIST_TABLE_MAX_HEIGHT_PERMISSION,
  118. SECURITY_POSITION_LIST_TABLE_OPTIONS,
  119. SECURITY_POSITION_LIST_TABLE_COLUMNS,
  120. SECURITY_POSITION_LIST_SEARCH_CONFIG,
  121. } from './config';
  122. import {
  123. GetPositionListParams,
  124. PositionMonitorCameraListRes,
  125. getSecurityPositionList,
  126. AddOrUpdatePositionInfoParams,
  127. addOrUpdatePositionInfo,
  128. updateCameraGroupOrder,
  129. } from '@/api/security-confidentiality-position';
  130. import { deleteCameraGroupApi } from '@/api/nine-square-grid';
  131. const { tableConfig } = useTableConfig(SECURITY_POSITION_LIST_TABLE_COLUMNS, SECURITY_POSITION_LIST_TABLE_OPTIONS);
  132. const { permissions } = useUserInfoHook();
  133. const securityPositionManagePermission = ref<boolean>(false);
  134. const positionMonitorCameraEdit = usePositionMonitorCameraEdit();
  135. const {
  136. titleOfUpdatePositionMonitorCamera,
  137. dataOfPosition,
  138. idOfPosition,
  139. nameOfPosition,
  140. selectedCameraIdsOfPosition,
  141. } = storeToRefs(positionMonitorCameraEdit);
  142. const { initDataOfPosition, resetPositionMonitorCameraEdit } = positionMonitorCameraEdit;
  143. const searchData = reactive<GetPositionListParams>({
  144. groupName: '',
  145. cameraName: '',
  146. });
  147. const searchSelectedType = ref(FIELDTYPE.POSITION_NAME);
  148. const searchKeyword = ref('');
  149. const curSearchTypeLabel = computed(() => {
  150. const option = securityPositionQueryOptions.find((item) => item.value === searchSelectedType.value);
  151. return option ? option.label : FIELD_CONTENT[searchSelectedType.value];
  152. });
  153. const tableData = ref<PositionMonitorCameraListRes[]>([]);
  154. const updatePositionMonitorCameraVisible = ref(false);
  155. const handleAddSecurityPosition = () => {
  156. titleOfUpdatePositionMonitorCamera.value = '添加重点监控部位';
  157. dataOfPosition.value = undefined;
  158. initDataOfPosition();
  159. updatePositionMonitorCameraVisible.value = true;
  160. };
  161. const handleSearch = () => {
  162. if (searchSelectedType.value === FIELDTYPE.POSITION_NAME) searchData.groupName = searchKeyword.value;
  163. else searchData.cameraName = searchKeyword.value;
  164. getTableData();
  165. };
  166. const handleReset = () => {
  167. searchKeyword.value = '';
  168. searchSelectedType.value = FIELDTYPE.POSITION_NAME;
  169. searchData.cameraName = '';
  170. searchData.groupName = '';
  171. getTableData();
  172. };
  173. const handleSelectedTypeChange = () => {
  174. searchKeyword.value = '';
  175. };
  176. const handleUpOne = (currentRow: PositionMonitorCameraListRes, currentIndex: number) => {
  177. // 获取上一行的数据
  178. const prevRow = tableData.value[currentIndex - 1];
  179. // 构造交换数据数组
  180. const swapData = [
  181. { id: prevRow.id, orderNum: currentRow.orderNum }, // 上一行使用当前行的orderNum
  182. { id: currentRow.id, orderNum: prevRow.orderNum }, // 当前行使用上一行的orderNum
  183. ];
  184. updateCameraGroupOrder(swapData)
  185. .then(() => {
  186. ElMessage.success('上移成功');
  187. getTableData();
  188. })
  189. .catch(() => {
  190. ElMessage.error('上移失败');
  191. });
  192. };
  193. const handleDownOne = (currentRow: PositionMonitorCameraListRes, currentIndex: number) => {
  194. // 获取下一行的数据
  195. const nextRow = tableData.value[currentIndex + 1];
  196. // 构造交换数据数组
  197. const swapData = [
  198. { id: nextRow.id, orderNum: currentRow.orderNum }, // 下一行使用当前行的orderNum
  199. { id: currentRow.id, orderNum: nextRow.orderNum }, // 当前行使用下一行的orderNum
  200. ];
  201. updateCameraGroupOrder(swapData)
  202. .then(() => {
  203. ElMessage.success('下移成功');
  204. getTableData();
  205. })
  206. .catch(() => {
  207. ElMessage.error('下移失败');
  208. });
  209. };
  210. const handleEditSecurityPosition = (row: PositionMonitorCameraListRes) => {
  211. titleOfUpdatePositionMonitorCamera.value = '编辑重点监控部位';
  212. dataOfPosition.value = row;
  213. initDataOfPosition();
  214. updatePositionMonitorCameraVisible.value = true;
  215. };
  216. const handleDeleteSecurityPosition = (id: number) => {
  217. deleteCameraGroupApi(id).then(() => {
  218. ElMessage.success('治安重点部位删除成功');
  219. getTableData();
  220. });
  221. };
  222. const handleConfirmPositionMonitorCamera = () => {
  223. const params: AddOrUpdatePositionInfoParams = {
  224. id: idOfPosition.value ?? undefined,
  225. groupName: nameOfPosition.value,
  226. type: POSITION_TYPE.SECURITY_POSITION,
  227. cameraIdList: selectedCameraIdsOfPosition.value.map((item) => item.id),
  228. };
  229. addOrUpdatePositionInfo(params).then(() => {
  230. ElMessage.success(idOfPosition.value ? '治安重点部位编辑成功' : '治安重点部位新建成功');
  231. getTableData();
  232. });
  233. updatePositionMonitorCameraVisible.value = false;
  234. resetPositionMonitorCameraEdit();
  235. };
  236. const handleClosePositionMonitorCamera = () => {
  237. ElMessage.info('取消操作');
  238. updatePositionMonitorCameraVisible.value = false;
  239. resetPositionMonitorCameraEdit();
  240. };
  241. const getTableData = () => {
  242. tableConfig.loading = true;
  243. getSecurityPositionList(searchData).then((res) => {
  244. tableData.value = res;
  245. });
  246. tableConfig.loading = false;
  247. };
  248. // 当前选中的治安重点部位id和相机code
  249. const activePositionId = ref(0);
  250. const activeCameraCode = ref('');
  251. const handleCameraClick = (id: number, code: string) => {
  252. activePositionId.value = id;
  253. activeCameraCode.value = code;
  254. };
  255. const handleMouseEnter = (code: string) => {
  256. activeCameraCode.value = code;
  257. };
  258. const handleMouseLeave = () => {
  259. activeCameraCode.value = '';
  260. };
  261. // 动态生成表格列配置
  262. const getTableColumns = () => {
  263. if (securityPositionManagePermission.value) {
  264. return SECURITY_POSITION_LIST_TABLE_COLUMNS;
  265. } else {
  266. // 过滤掉操作列
  267. return SECURITY_POSITION_LIST_TABLE_COLUMNS.filter(
  268. (column) => column.prop !== 'action' && column.type !== 'selection',
  269. );
  270. }
  271. };
  272. onMounted(() => {
  273. getTableData();
  274. securityPositionManagePermission.value = Boolean(
  275. permissions.find(
  276. (item: { code: string }) => item.code === SECURITY_CONFIDENTIALITY_PERMISSIONS.SECURITY_POSITION_MANAGEMENT,
  277. ),
  278. );
  279. tableConfig.maxHeight = securityPositionManagePermission.value
  280. ? SECURITY_POSITION_LIST_TABLE_MAX_HEIGHT_PERMISSION
  281. : SECURITY_POSITION_LIST_TABLE_MAX_HEIGHT_DEFAULT;
  282. tableConfig.columns = getTableColumns();
  283. });
  284. </script>
  285. <style scoped lang="scss">
  286. @use '@/styles/page-main-layout.scss' as *;
  287. @use '@/styles/page-details-layout.scss' as *;
  288. @use '@/styles/basic-table-action.scss' as *;
  289. .action-container--div {
  290. justify-content: flex-start;
  291. }
  292. .camera-name-container {
  293. display: flex;
  294. gap: 10px;
  295. align-items: flex-start;
  296. flex-direction: column;
  297. }
  298. .camera-name-container .thumb-nail {
  299. color: #1777ff;
  300. cursor: pointer;
  301. }
  302. .camera-name-container .thumb-nail:hover {
  303. color: #94c1ff;
  304. }
  305. .camera-name-container .active {
  306. color: #003f97;
  307. }
  308. </style>