index.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. <template>
  2. <a-spin :spinning="spinning">
  3. <a-alert
  4. v-if="areaAvailable"
  5. message="检测区域范围未配置或数值较小,请在设备配置调整参数!"
  6. banner
  7. />
  8. <div class="viewer">
  9. <div class="viewer-header">
  10. <div class="viewer-header-title">家具与子区域配置</div>
  11. <div class="viewer-header-extra">
  12. <a-space>
  13. <span v-if="props.online === 0" style="color: red">⚠️设备离线,不允许编辑保存</span>
  14. <a-switch
  15. :checked="isEditDraggable"
  16. checked-children="启用"
  17. un-checked-children="禁用"
  18. :disabled="props.online === 0"
  19. @change="isEditDraggable = !isEditDraggable"
  20. />
  21. <a-button
  22. type="primary"
  23. size="small"
  24. :disabled="!isEditDraggable"
  25. @click="saveAllConfig"
  26. >保存配置</a-button
  27. >
  28. </a-space>
  29. </div>
  30. </div>
  31. <RadarEditor
  32. v-if="!areaAvailable"
  33. ref="radarEditorRef"
  34. :coordinates="props.ranges"
  35. :angle="props.angle"
  36. :disabled="!isEditDraggable"
  37. />
  38. </div>
  39. </a-spin>
  40. </template>
  41. <script setup lang="ts">
  42. import { ref, computed } from 'vue'
  43. import * as roomApi from '@/api/room'
  44. import { message } from 'ant-design-vue'
  45. import RadarEditor from '@/components/RadarEditor/index.vue'
  46. import { useRoomStore } from '@/stores/room'
  47. import { convert_furniture_r2c, rotateRect_cw } from '@/utils/coordTransform'
  48. defineOptions({
  49. name: 'deviceAreaConfig',
  50. })
  51. type Props = {
  52. devId: string // 设备id,用于获取房间布局
  53. // x,y的范围,用于初始化画布的大小
  54. length: number
  55. width: number
  56. ranges: [number, number, number, number] // 区域范围
  57. angle: number // 设备角度,用于初始化画布的旋转角度
  58. online?: SwitchType | 9 // 设备在线状态,用于判断是否可以保存配置
  59. }
  60. const emit = defineEmits<{
  61. (e: 'success', value: void): void
  62. }>()
  63. const props = withDefaults(defineProps<Props>(), {
  64. devId: '',
  65. length: 0, // 区域宽度
  66. width: 0, // 区域高度
  67. ranges: () => [Infinity, Infinity, Infinity, Infinity], // 区域范围
  68. online: 0,
  69. })
  70. const deviceRoomId = ref('')
  71. const roomStore = useRoomStore()
  72. // 检测区域宽度 length
  73. const areaWidth = computed(() => {
  74. return Math.abs(props.length)
  75. })
  76. // 检测区域高度 width
  77. const areaHeight = computed(() => {
  78. return Math.abs(props.width)
  79. })
  80. // 检测区域是否可用,小于50cm的区域,不可用
  81. const areaAvailable = computed(() => {
  82. return areaWidth.value < 50 || areaHeight.value < 50
  83. })
  84. const spinning = ref(false)
  85. // 获取房间布局
  86. const fetchRoomLayout = async () => {
  87. console.log('fetchRoomLayout', props, props.devId)
  88. if (!props.devId) {
  89. message.error('设备ID不能为空')
  90. return
  91. }
  92. try {
  93. spinning.value = true
  94. const res = await roomApi.queryRoomInfo({
  95. devId: props.devId,
  96. })
  97. console.log('✅获取到房间布局信息', res)
  98. if (!res) {
  99. spinning.value = false
  100. return
  101. }
  102. const { furnitures, roomId, subRegions } = res.data
  103. deviceRoomId.value = roomId || ''
  104. roomStore.cacheFurniture(furnitures ?? [])
  105. roomStore.cacheSubRegion(subRegions ?? [])
  106. spinning.value = false
  107. roomStore.localFurnitureItems = roomStore.localFurnitureItems.map((item) => {
  108. const itemConvert = convert_furniture_r2c(
  109. {
  110. x: item.x,
  111. y: item.y,
  112. width: item.width,
  113. height: item.length,
  114. },
  115. {
  116. x_radar: 250,
  117. y_radar: 250,
  118. }
  119. )
  120. const rotatedRect = rotateRect_cw(
  121. {
  122. left: itemConvert.left,
  123. top: itemConvert.top,
  124. width: item.width,
  125. height: item.length,
  126. },
  127. {
  128. x: 250,
  129. y: 250,
  130. },
  131. props.angle
  132. )
  133. return {
  134. ...item,
  135. left: rotatedRect.left,
  136. top: rotatedRect.top,
  137. width: rotatedRect.width,
  138. length: rotatedRect.height,
  139. }
  140. })
  141. } catch (error) {
  142. console.error('❌获取房间布局信息失败', error)
  143. spinning.value = false
  144. roomStore.cacheFurniture([])
  145. roomStore.cacheSubRegion([])
  146. }
  147. }
  148. fetchRoomLayout()
  149. const radarEditorRef = ref<InstanceType<typeof RadarEditor>>()
  150. // 画布上的家具列表
  151. const isEditDraggable = ref(false)
  152. // 保存所有配置
  153. const saveAllConfig = () => {
  154. console.log('保存所有配置', {
  155. furnitureItems: roomStore.localFurnitureItems,
  156. subRegions: roomStore.localSubRegions,
  157. })
  158. const furnitureItems = roomStore.localFurnitureItems.map((item) => {
  159. return {
  160. name: item.name,
  161. type: item.type,
  162. width: item.width,
  163. length: item.length,
  164. rotate: item.rotate,
  165. x: item?.x || 0,
  166. y: item?.y || 0,
  167. }
  168. })
  169. const subRegions = roomStore.localSubRegions.map((item) => {
  170. return {
  171. startXx: item.startXx,
  172. stopXx: item.stopXx,
  173. startYy: item.startYy,
  174. stopYy: item.stopYy,
  175. startZz: item.startZz,
  176. stopZz: item.stopZz,
  177. isLowSnr: Number(item.isBed),
  178. isDoor: item.isDoor,
  179. presenceEnterDuration: item.presenceEnterDuration,
  180. presenceExitDuration: item.presenceExitDuration,
  181. trackPresence: Number(item.isTracking),
  182. excludeFalling: Number(item.isFalling),
  183. }
  184. })
  185. try {
  186. const res = roomApi.saveRoomInfo({
  187. roomId: deviceRoomId.value,
  188. devId: props.devId,
  189. furnitures: furnitureItems,
  190. subRegions,
  191. })
  192. console.log('保存所有配置 ✅', res)
  193. message.success('保存成功')
  194. emit('success')
  195. } catch (error) {
  196. console.error('保存所有配置 ❌', error)
  197. }
  198. }
  199. </script>
  200. <style scoped lang="less">
  201. .viewer {
  202. flex-shrink: 0;
  203. &-header {
  204. display: flex;
  205. justify-content: space-between;
  206. padding-bottom: 20px;
  207. &-title {
  208. font-size: 16px;
  209. font-weight: 600;
  210. line-height: 24px;
  211. }
  212. }
  213. &-content {
  214. display: flex;
  215. gap: 20px;
  216. }
  217. }
  218. </style>