Kaynağa Gözat

Merge branch 'dev' into feature-1.1.1

liujia 1 gün önce
ebeveyn
işleme
471831bf8b

+ 14 - 0
CHANGELOG.md

@@ -1,4 +1,18 @@
 
+## v0.9.0 (2025-10-21)
+- fix(EditableFurniture): 修复家具旋转后位置计算错误并添加序号显示 (5985c73)
+- feat: 调整转换函数变量名; (d708571)
+- feat: 添加检测区域提示的判断条件; (59b5d37)
+- fix: 告警计划模板交互调整,生效方式在四种频次统计的时候不需要进行选择;(如厕频次统计、夜间如厕频次统计、如厕频次异常、卫生间频次统计) (1a16648)
+- feat: 调整家具在90度和270度的时候位置的偏移;设置子区域随着角度回显逻辑; (2b648ce)
+- fix: 告警提示统计时间不完整; (caeae6c)
+- fix: 设备列表仅超管才展示抹掉数据按钮; (3987040)
+- feat: 新增baseRadarView (a5b9888)
+- fix: 修复家具坐标转换和初始化问题 (15e04a7)
+- feat: 调整点位坐标根据角度旋转位置; (b04901e)
+- feat(雷达编辑): 增加家具坐标转换和旋转功能 (8a4d934)
+- fix(区域配置): 修复区域配置保存失败时未清空缓存的问题 (958a6cc)
+
 ## v0.8.2 (2025-10-15)
 - feat: 家具编辑数据源使用roomStore数据; (75c8ce1)
 - feat: 1、新增roomStore管理房间数据;2、将展示与编辑房间组件的数据源换成roomStore数据;3、调整设备区域配置组件,删除冗余代码; (606aec2)

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "ln-web",
-  "version": "0.8.2",
+  "version": "0.9.0",
   "private": true,
   "type": "module",
   "scripts": {

+ 2 - 2
src/components/DetectionAreaView/index.vue

@@ -45,7 +45,7 @@ import {
   type CanvasRect,
   type RadarPosition,
   type RadarFurniture,
-  rotateRect,
+  rotateRect_cw,
 } from '@/utils/coordTransform'
 import radarUrl from '@/assets/furnitures/radar.png'
 import type { LocalFurnitureItem } from '@/api/room/types'
@@ -118,7 +118,7 @@ const localFurnitureItems = computed(() => {
       },
       radarPosition
     )
-    const rotatedRect = rotateRect(
+    const rotatedRect = rotateRect_cw(
       {
         left: itemConvert.left,
         top: itemConvert.top,

+ 47 - 29
src/components/EditableFurniture/index.vue

@@ -3,6 +3,7 @@
     <div class="furniture-rotated-container" :style="rotatedContainerStyle">
       <div class="furniture-wrapper">
         <furnitureIcon :icon="localItem.type" :width="localItem.width" :height="localItem.length" />
+        <span class="text"> {{ idx + 1 }}</span>
       </div>
     </div>
   </div>
@@ -11,12 +12,13 @@
 <script setup lang="ts">
 import { reactive, watch, computed, onMounted, nextTick, type CSSProperties } from 'vue'
 import type { FurnitureItem, FurnitureType, LocalFurnitureItem } from '@/api/room/types'
-import { convert_furniture_r2c, rotateRect } from '@/utils/coordTransform'
+import { convert_furniture_r2c, rotateRect_cw } from '@/utils/coordTransform'
 
 defineOptions({ name: 'EditableFurniture' })
 
 interface Props {
   item: FurnitureItem
+  idx: number
   angle: number
   coordinates: [number, number, number, number]
   canvasSize?: number
@@ -118,33 +120,34 @@ const pixelPosition = reactive({ left: props.item.left ?? 0, top: props.item.top
 const boundingBox = computed(() => calculateBoundingBox())
 
 // 容器样式 - 基于边界框
-const containerStyle = computed(
-  () =>
-    ({
-      position: 'absolute',
-      left: `${pixelPosition.left}px`,
-      top: `${pixelPosition.top}px`,
-      width: `${boundingBox.value.width}px`,
-      height: `${boundingBox.value.height}px`,
-      pointerEvents: props.disabled ? 'none' : 'auto',
-      zIndex: 100,
-      cursor: 'move',
-    }) as CSSProperties
-)
+const containerStyle = computed(() => {
+  const cssObj: CSSProperties = {
+    position: 'absolute',
+    left: `${pixelPosition.left}px`,
+    top: `${pixelPosition.top}px`,
+    width: `${boundingBox.value.width}px`,
+    height: `${boundingBox.value.height}px`,
+    pointerEvents: props.disabled ? 'none' : 'auto',
+    zIndex: 100,
+  }
+  console.log('💆‍♀️💆‍♀️💆‍♀️💆‍♀️ AAAAA containerStyle', cssObj)
+  return cssObj
+})
 
 // 旋转容器样式 - 调整位置使左上角对齐
-const rotatedContainerStyle = computed(
-  () =>
-    ({
-      position: 'absolute',
-      left: `${-boundingBox.value.left}px`,
-      top: `${-boundingBox.value.top}px`,
-      width: `${localItem.width}px`,
-      height: `${localItem.length}px`,
-      transform: `rotate(${localItem.rotate}deg)`,
-      transformOrigin: '0 0',
-    }) as CSSProperties
-)
+const rotatedContainerStyle = computed(() => {
+  const cssObj: CSSProperties = {
+    position: 'absolute',
+    left: `${-boundingBox.value.left}px`,
+    top: `${-boundingBox.value.top}px`,
+    width: `${localItem.width}px`,
+    height: `${localItem.length}px`,
+    transform: `rotate(${localItem.rotate}deg)`,
+    transformOrigin: '0 0',
+  }
+  console.log('💆‍♀️💆‍♀️💆‍♀️💆‍♀️ BBBBB rotatedContainerStyle', cssObj)
+  return cssObj
+})
 
 // 监听props变化
 watch(
@@ -208,7 +211,7 @@ const initPixelPosition = () => {
   )
 
   // === 2️⃣ 再根据雷达朝向旋转 ===
-  const rotatedRect = rotateRect(
+  const rotatedRect = rotateRect_cw(
     {
       left: itemConvert.left,
       top: itemConvert.top,
@@ -219,10 +222,15 @@ const initPixelPosition = () => {
     props.angle
   )
 
+  console.log('🌸🌸🌸回显的家具数据AAAA', props.angle, props.item, itemConvert, rotatedRect)
+
   // === 3️⃣ 应用最终位置 ===
   /* 90度和270度需要特殊处理,因为旋转后家具的中心位置会改变 */
-  pixelPosition.left = props.angle === 90 ? rotatedRect.left + length / 2 : rotatedRect.left
-  pixelPosition.top = props.angle === 270 ? rotatedRect.top - length / 2 : rotatedRect.top
+  // pixelPosition.left = props.angle === 90 ? rotatedRect.left + length / 2 : rotatedRect.left
+  // pixelPosition.top = props.angle === 270 ? rotatedRect.top - length / 2 : rotatedRect.top
+
+  pixelPosition.left = rotatedRect.left
+  pixelPosition.top = rotatedRect.top
 
   // 同步像素位置到 localItem(方便 emit)
   // localItem.left = rotatedRect.left
@@ -326,10 +334,20 @@ const startDrag = (e: MouseEvent) => {
       display: flex;
       align-items: center;
       justify-content: center;
+      position: relative;
 
       :deep(*) {
         pointer-events: none;
       }
+
+      .text {
+        font-size: 14px;
+        color: #f41313;
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%, -50%);
+      }
     }
   }
 

+ 2 - 4
src/components/EditableSubregion/index.vue

@@ -49,7 +49,7 @@ import { message } from 'ant-design-vue'
 import { nanoid } from 'nanoid'
 import { useRoomStore } from '@/stores/room'
 import type { LocalSubRegionItem } from '@/api/room/types'
-import { convert_region_r2c, rotateRect } from '@/utils/coordTransform'
+import { convert_region_r2c, rotateRect_cw } from '@/utils/coordTransform'
 
 defineOptions({ name: 'EditableSubregion' })
 const roomStore = useRoomStore()
@@ -306,9 +306,7 @@ const echoSubRegions = () => {
       }
     )
 
-    const routeRegion = rotateRect(itemRegion, { x: 250, y: 250 }, props.angle)
-
-    console.log(item, itemRegion, routeRegion, '🚀🚀🚀🚀🚀🚀🚀')
+    const routeRegion = rotateRect_cw(itemRegion, { x: 250, y: 250 }, props.angle)
 
     return {
       ...item,

+ 18 - 14
src/components/RadarEditor/index.vue

@@ -4,9 +4,10 @@
       <template #furnitures>
         <template v-if="showFurniture">
           <EditableFurniture
-            v-for="item in roomStore.localFurnitureItems"
+            v-for="(item, index) in roomStore.localFurnitureItems"
             :key="item.nanoid"
             :item="item"
+            :idx="index"
             :angle="angle"
             :coordinates="coordinates"
             :canvas-size="500"
@@ -113,7 +114,10 @@
                 class="list-item"
               >
                 <a-collapse v-model:activeKey="furnitureActiveKey" ghost>
-                  <a-collapse-panel :key="index + 1" :header="`${furniture.name} 属性`">
+                  <a-collapse-panel
+                    :key="index + 1"
+                    :header="`${index + 1}. ${furniture.name} 属性`"
+                  >
                     <div class="mapConfig">
                       <div class="mapConfig-item">
                         <label class="mapConfig-item-label">家具尺寸:</label>
@@ -245,13 +249,13 @@
                 checked-children="显示"
                 un-checked-children="隐藏"
               />
-              <a-popconfirm
+              <!-- <a-popconfirm
                 v-if="roomStore.localSubRegions.length"
                 title="确定清空子区域吗?"
                 @confirm="clearSubregions"
               >
                 <a-button size="small" type="link">清空</a-button>
-              </a-popconfirm>
+              </a-popconfirm> -->
               <a-button size="small" type="link" @click="createSubregion">新建</a-button>
             </a-space>
           </div>
@@ -341,7 +345,7 @@
                         <div class="mapConfig-item-content"> 默认开启 </div>
                       </div>
 
-                      <div class="mapConfig-item">
+                      <!-- <div class="mapConfig-item">
                         <div class="mapConfig-item-label">删除区域:</div>
                         <div class="mapConfig-item-content">
                           <a-popconfirm
@@ -351,7 +355,7 @@
                             <DeleteOutlined />
                           </a-popconfirm>
                         </div>
-                      </div>
+                      </div> -->
                     </div>
                   </a-collapse-panel>
                 </a-collapse>
@@ -544,11 +548,11 @@ onUnmounted(() => {
 const regionActiveKey = ref<number[]>([])
 const furnitureActiveKey = ref<number[]>([])
 
-const deleteBlockArea = (id: string) => {
-  if (id) {
-    roomStore.localSubRegions = roomStore.localSubRegions.filter((item) => item.nanoid !== id)
-  }
-}
+// const deleteBlockArea = (id: string) => {
+//   if (id) {
+//     roomStore.localSubRegions = roomStore.localSubRegions.filter((item) => item.nanoid !== id)
+//   }
+// }
 
 const modeRadioChange = () => {
   regionActiveKey.value = []
@@ -559,9 +563,9 @@ const modeRadioChange = () => {
 //   console.log('同步坐标', localFurniture.value, localSubRegions.value)
 // }
 
-const clearSubregions = () => {
-  roomStore.localSubRegions = []
-}
+// const clearSubregions = () => {
+//   roomStore.localSubRegions = []
+// }
 
 const clearFurniture = () => {
   roomStore.localFurnitureItems = []

+ 2 - 2
src/components/baseRadarView/index.vue

@@ -21,7 +21,7 @@
 <script setup lang="ts">
 import { onMounted, ref, watch, type CSSProperties } from 'vue'
 import radarUrl from '@/assets/furnitures/radar.png'
-import { convert_region_r2c, rotateRect } from '@/utils/coordTransform'
+import { convert_region_r2c, rotateRect_cw } from '@/utils/coordTransform'
 import type { TargetPoint } from '@/types/radar'
 
 defineOptions({ name: 'BaseRadarView' })
@@ -250,7 +250,7 @@ const drawDetectionArea = (ctx: CanvasRenderingContext2D, options: DetectionArea
     radarPosition
   )
 
-  const rotatedRect = rotateRect(
+  const rotatedRect = rotateRect_cw(
     {
       left: canvasRect.left,
       top: canvasRect.top,

+ 58 - 1
src/utils/coordTransform.ts

@@ -142,7 +142,7 @@ interface Point {
  * @param angle - 顺时针旋转角度 0, 90, 180, 270
  * @returns 旋转后的矩形 { left, top, width, height }
  */
-export function rotateRect(src_rect: Rect, pRadar: Point, angle: number): Rect {
+export function rotateRect_cw(src_rect: Rect, pRadar: Point, angle: number): Rect {
   if (![0, 90, 180, 270].includes(angle)) angle = 0
 
   const { left, top, width, height } = src_rect
@@ -192,6 +192,63 @@ export function rotateRect(src_rect: Rect, pRadar: Point, angle: number): Rect {
   }
 }
 
+/**
+ * 逆时针旋转矩形(家具/检测区域)
+ * @param src_rect - 输入矩形 { left, top, width, height }
+ * @param pRadar - 雷达中心坐标 { x, y }
+ * @param angle - 逆时针旋转角度 0, 90, 180, 270
+ * @returns 旋转后的矩形 { left, top, width, height }
+ */
+export function rotateRect_ccw(src_rect: Rect, pRadar: Point, angle: number): Rect {
+  if (![0, 90, 180, 270].includes(angle)) angle = 0
+
+  const { left, top, width, height } = src_rect
+  const cx = left + width / 2
+  const cy = top + height / 2
+
+  const dx = cx - pRadar.x
+  const dy = cy - pRadar.y
+
+  let new_dx: number = 0
+  let new_dy: number = 0
+
+  switch (angle) {
+    case 0:
+      new_dx = dx
+      new_dy = dy
+      break
+    case 90:
+      new_dx = dy
+      new_dy = -dx
+      break
+    case 180:
+      new_dx = -dx
+      new_dy = -dy
+      break
+    case 270:
+      new_dx = -dy
+      new_dy = dx
+      break
+  }
+
+  const new_cx = pRadar.x + new_dx
+  const new_cy = pRadar.y + new_dy
+
+  let new_width = width
+  let new_height = height
+  if (angle === 90 || angle === 270) {
+    new_width = height
+    new_height = width
+  }
+
+  return {
+    left: new_cx - new_width / 2,
+    top: new_cy - new_height / 2,
+    width: new_width,
+    height: new_height,
+  }
+}
+
 interface Point {
   x: number
   y: number

+ 5 - 1
src/views/device/detail/components/alarmPlanModal/index.vue

@@ -1065,7 +1065,11 @@ const submit = () => {
           return
         }
       }
-      if ([1, 2, 3, 9].includes(formState.eventType as number) && formState.region.length !== 4) {
+      if (
+        props.type === 'plan' &&
+        [1, 2, 3, 9].includes(formState.eventType as number) &&
+        formState.region.length !== 4
+      ) {
         message.warn('请选择检测区域')
         return
       }

+ 38 - 6
src/views/device/detail/components/deviceAreaConfig/index.vue

@@ -46,7 +46,13 @@ import * as roomApi from '@/api/room'
 import { message } from 'ant-design-vue'
 import RadarEditor from '@/components/RadarEditor/index.vue'
 import { useRoomStore } from '@/stores/room'
-import { convert_furniture_r2c, rotateRect } from '@/utils/coordTransform'
+import {
+  convert_furniture_r2c,
+  convert_region_c2r,
+  convert_region_r2c,
+  rotateRect_ccw,
+  rotateRect_cw,
+} from '@/utils/coordTransform'
 
 defineOptions({
   name: 'deviceAreaConfig',
@@ -126,7 +132,7 @@ const fetchRoomLayout = async () => {
           y_radar: 250,
         }
       )
-      const rotatedRect = rotateRect(
+      const rotatedRect = rotateRect_cw(
         {
           left: itemConvert.left,
           top: itemConvert.top,
@@ -181,12 +187,38 @@ const saveAllConfig = () => {
     }
   })
 
+  console.log('保存的家具数据📚📚📚📚📚📚furnitureItems', furnitureItems)
+
   const subRegions = roomStore.localSubRegions.map((item) => {
+    const pRadar = {
+      x: 250,
+      y: 250,
+    }
+
+    const rectRotatedBefore = convert_region_r2c(
+      {
+        x_cm_start: Number(item.startXx),
+        x_cm_stop: Number(item.stopXx),
+        y_cm_start: Number(item.startYy),
+        y_cm_stop: Number(item.stopYy),
+      },
+      {
+        x_radar: 250,
+        y_radar: 250,
+      }
+    )
+
+    const rectRotatedBack = rotateRect_ccw(rectRotatedBefore, pRadar, props.angle)
+    const newRegion = convert_region_c2r(rectRotatedBack, {
+      x_radar: 250,
+      y_radar: 250,
+    })
+
     return {
-      startXx: item.startXx,
-      stopXx: item.stopXx,
-      startYy: item.startYy,
-      stopYy: item.stopYy,
+      startXx: newRegion.x_cm_start,
+      stopXx: newRegion.x_cm_stop,
+      startYy: newRegion.y_cm_start,
+      stopYy: newRegion.y_cm_stop,
       startZz: item.startZz,
       stopZz: item.stopZz,
       isLowSnr: Number(item.isBed),