FenceToolbar.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. <template>
  2. <div class="fenceWrapper">
  3. <div>
  4. <div class="fenceTitle">电子围栏</div>
  5. <ElSwitch
  6. size="small"
  7. class="fenceSwitchBtn"
  8. v-model="selectedAlgoDetail.electronicFenceBool"
  9. @update:modelValue="handleUpdateFenceStatus"
  10. />
  11. </div>
  12. <div class="algoName">
  13. {{ selectedAlgoDetail?.algoInfo?.name }}
  14. </div>
  15. <PresetSelect />
  16. <Description :is-simple="Boolean(!cameraDetailStore.detail?.isPtz)" v-if="fenceStore.allFences.length === 0" />
  17. <div v-if="fenceStore.allFences.length > 0">
  18. <div style="display: flex">
  19. <ElCheckbox label="检测围栏外部" v-model="isFenceRegionOut" @update:modelValue="handleUpdateRegion" />
  20. <ElCheckbox label="前台画面显示" v-model="isDisplayFenceInVideo" @update:modelValue="handleUpdateDisplay" />
  21. <a :href="previewUrl" target="_blank" style="margin-left: 20px" v-if="previewUrl && false">平台相机预览</a>
  22. </div>
  23. <div class="fenceListWrapper">
  24. <FenceNameItem
  25. :active="item.id === fenceStore.currentFenceId"
  26. v-for="item in fenceStore.allFences"
  27. :detail="item"
  28. :key="item.id"
  29. @click="handleSelectFence(item.id)"
  30. @delete="handleDeleteFence"
  31. @edit="handleEditFenceInfo(item)"
  32. />
  33. </div>
  34. <div style="text-align: right">
  35. <ElButton size="small" @click="handleCancelFence">取消</ElButton>
  36. <ElButton type="primary" size="small" @click="handleSaveFence">保存</ElButton>
  37. </div>
  38. </div>
  39. <div>
  40. <EditFenceDialog
  41. v-if="showEditFenceDialog"
  42. @cancel="handleEditCancel"
  43. @submit="handleEditSubmit"
  44. :detail="selectedDetail"
  45. />
  46. </div>
  47. </div>
  48. </template>
  49. <script setup lang="ts">
  50. import { computed, defineEmits, ref, watch } from 'vue';
  51. import { ElMessage, ElSwitch } from 'element-plus';
  52. import useCameraAlgoStore from '../../store/useCameraAlgoStore';
  53. import PresetSelect from '../PresetSelect/PresetSelect.vue';
  54. import FenceNameItem from './FenceNameItem.vue';
  55. import useFenceStore from '../../store/useFenceStore';
  56. import useCameraDetailStore from '../../store/useCameraDetailStore';
  57. import usePresetListStore from '../../store/usePresetListStore';
  58. import EditFenceDialog from './EditFenceDialog.vue';
  59. import { ServerLineInfo } from '../FenceEditor/constants';
  60. import { storeToRefs } from 'pinia';
  61. import { RegionJudge } from './constants';
  62. import { choosePreset, updateFenceDisplayStatus } from '@/api/camera/camera-preview';
  63. import { FenceDisplayStatus } from '@/types/camera/constant';
  64. import { useGlobSetting } from '@/hooks/setting';
  65. import useParamsSettingFn from '../../hooks/useParamsSettingFn';
  66. import Description from './Description.vue';
  67. const cameraAlgoStore = useCameraAlgoStore();
  68. const fenceStore = useFenceStore();
  69. const cameraDetailStore = useCameraDetailStore();
  70. const presetStore = usePresetListStore();
  71. const props = defineProps<{ isEdit: boolean }>();
  72. const showEditFenceDialog = ref(false);
  73. const selectedDetail = ref<ServerLineInfo | null>(null);
  74. const paramsSettingFn = useParamsSettingFn();
  75. const { selectedAlgoDetail } = storeToRefs(cameraAlgoStore);
  76. const emits = defineEmits<{
  77. (e: 'toggleRange'): unknown;
  78. (e: 'remove'): unknown;
  79. (e: 'cancel'): unknown;
  80. (e: 'save'): unknown;
  81. (e: 'select', fenceId: number): unknown;
  82. /** 切换电子围栏打开关闭状态 */
  83. (e: 'toggleFenceStatus', nextStatus: boolean): unknown;
  84. }>();
  85. const isFenceRegionOut = ref(false);
  86. const isDisplayFenceInVideo = ref(false);
  87. const { detail } = storeToRefs(cameraDetailStore);
  88. const { appPCUrl } = useGlobSetting();
  89. const previewUrl = computed(() => {
  90. const firstSceneId = detail.value?.sceneTemplateList[0]?.sceneId;
  91. if (!detail.value?.workshopId || !detail.value?.code || !firstSceneId) return '';
  92. return appPCUrl + `#/shop?id=${detail.value?.workshopId}&cameraCode=${detail.value?.code!}&sceneId=${firstSceneId}`;
  93. });
  94. watch(
  95. () => selectedAlgoDetail.value?.regionJudge,
  96. (newVal) => {
  97. isFenceRegionOut.value = newVal === RegionJudge.out;
  98. },
  99. {
  100. immediate: true,
  101. },
  102. );
  103. watch(
  104. () => cameraDetailStore.isDisplayFence,
  105. (isDisplayFence) => {
  106. isDisplayFenceInVideo.value = isDisplayFence;
  107. },
  108. {
  109. immediate: true,
  110. },
  111. );
  112. const handleSaveFence = () => {
  113. emits('save');
  114. };
  115. const handleCancelFence = () => {
  116. emits('cancel');
  117. };
  118. const handleSelectFence = (nextFenceId: number) => {
  119. emits('select', nextFenceId);
  120. };
  121. const handleUpdateRegion = (val: string) => {
  122. emits('toggleRange');
  123. };
  124. const handleEditFenceInfo = (detail) => {
  125. showEditFenceDialog.value = true;
  126. selectedDetail.value = detail;
  127. };
  128. const handleEditCancel = () => {
  129. showEditFenceDialog.value = false;
  130. selectedDetail.value = null;
  131. };
  132. const handleDeleteFence = (fenceId: number) => {
  133. paramsSettingFn.deleteFence(fenceId);
  134. };
  135. const handleEditSubmit = (data: { label: string; name: string }) => {
  136. const fenceId = selectedDetail.value?.id;
  137. if (!fenceId) return;
  138. paramsSettingFn.editFence({
  139. id: fenceId,
  140. label: data.label,
  141. name: data.name,
  142. });
  143. handleEditCancel();
  144. };
  145. const handleUpdateDisplay = (nextStatus: boolean) => {
  146. const params = {
  147. cameraCode: cameraDetailStore.detail?.code!,
  148. isDisplayFence: nextStatus ? FenceDisplayStatus.enabled : FenceDisplayStatus.disabled,
  149. };
  150. updateFenceDisplayStatus(params);
  151. if (nextStatus) {
  152. // 由于历史原因,需要调用两次接口
  153. const cameraId = cameraDetailStore.cameraId;
  154. const algoId = cameraAlgoStore.selectedAlgoId!;
  155. const presetToken = presetStore.currentPresetToken;
  156. const params = {
  157. algoId,
  158. cameraId,
  159. presetToken,
  160. };
  161. choosePreset(params).then((res) => {
  162. ElMessage.success('修改成功');
  163. });
  164. }
  165. };
  166. const handleUpdateFenceStatus = (nextStatus: boolean) => {
  167. console.log('nextFenceStatus', nextStatus);
  168. emits('toggleFenceStatus', nextStatus);
  169. };
  170. </script>
  171. <style scoped>
  172. .toolbar {
  173. display: flex;
  174. align-items: center;
  175. z-index: 10;
  176. padding: 5px;
  177. border-radius: 50px;
  178. }
  179. .fenceDrawingTip {
  180. background: #e8f5ff;
  181. border-radius: 6px;
  182. border: 1px solid #bae0ff;
  183. text-align: center;
  184. font-size: 12px;
  185. padding: 2px 20px;
  186. margin-right: 30px;
  187. color: #1890ff;
  188. }
  189. .fenceSwitchBtn {
  190. position: absolute;
  191. right: 10px;
  192. top: 10px;
  193. }
  194. .algoName {
  195. color: #ccc;
  196. margin-top: 10px;
  197. font-size: 12px;
  198. }
  199. .fenceWrapper {
  200. padding: 10px;
  201. }
  202. .fenceListWrapper {
  203. height: 365px;
  204. overflow-y: auto;
  205. margin-bottom: 10px;
  206. }
  207. .fenceTitle {
  208. font-weight: bold;
  209. font-size: 16px;
  210. position: relative;
  211. margin-left: 10px;
  212. &::before {
  213. content: '';
  214. width: 4px;
  215. position: absolute;
  216. left: -8px;
  217. height: 20px;
  218. top: 2px;
  219. background-color: #1890ff;
  220. }
  221. }
  222. </style>