Sfoglia il codice sorgente

feat: 设备配置的北向夹角更名为电源灯朝向;调整雷达展示组件显示逻辑,新增xy轴展示;

liujia 1 mese fa
parent
commit
4bb962757c

+ 164 - 35
src/components/RadarView/index.vue

@@ -9,7 +9,7 @@
           left: `${radar.left}px`,
           top: `${radar.top}px`,
           position: 'absolute',
-          transform: `translate(-50%, -50%) rotate(${radar.rotate}deg)`,
+          transform: `translate(-50%, -50%) rotate(${adjustedAngle + 90}deg)`,
           cursor: 'default',
         }"
         :draggable="false"
@@ -73,18 +73,28 @@
     <div v-if="showInfo" class="info-box">
       检测区域:{{ areaWidth }} × {{ areaHeight }} cm<br />
       坐标范围:[{{ xxStart }}, {{ xxEnd }}, {{ yyStart }}, {{ yyEnd }}]<br />
-      正北夹角:{{ angle }}° {{ northArrow }}<br />
-      坐标参考:X轴 {{ xArrow }},Y轴 {{ yArrow }}
+      电源灯朝向: {{ northArrow }} ({{ angle }}°)<br />
+      坐标参考:<span style="color: red">X轴 {{ xArrow }}</span
+      >,<span style="color: blue">Y轴 {{ yArrow }}</span>
     </div>
 
     <div v-if="areaWidth > 50 && areaHeight > 50" class="info-toggle" @click="showInfo = !showInfo">
       <QuestionCircleOutlined />
     </div>
+
+    <div class="full-coordinate-axes">
+      <div class="axis-line x-axis" :style="getFullAxisStyle('x')"> </div>
+      <div class="axis-line y-axis" :style="getFullAxisStyle('y')"> </div>
+    </div>
+    <div class="axis-markers">
+      <div class="axis-dot x-dot" :style="getAxisDotStyle('x')"></div>
+      <div class="axis-dot y-dot" :style="getAxisDotStyle('y')"></div>
+    </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import { computed, ref } from 'vue'
+import { computed, ref, type CSSProperties } from 'vue'
 import type { FurnitureItem, TargetPoint } from '@/types/radar'
 import { QuestionCircleOutlined } from '@ant-design/icons-vue'
 
@@ -109,6 +119,7 @@ const areaWidth = computed(() => xxEnd.value - xxStart.value)
 const areaHeight = computed(() => yyEnd.value - yyStart.value)
 // 雷达角度(默认值为 0°,表示正北朝上)
 const angle = computed(() => props.angle ?? 0)
+const adjustedAngle = computed(() => props.angle - 90)
 
 /**
  * 坐标转换函数:将雷达坐标系中的点 (x, y) 转换为 CSS 坐标系中的位置 (left, top)
@@ -120,7 +131,7 @@ const angle = computed(() => props.angle ?? 0)
 function convertRadarToCss(x: number, y: number): { left: number; top: number } {
   let rx = x,
     ry = y
-  switch (angle.value) {
+  switch (adjustedAngle.value) {
     case 90:
       ;[rx, ry] = [y, -x]
       break
@@ -185,47 +196,112 @@ const showInfo = ref(false)
 const northArrow = computed(() => {
   switch (angle.value) {
     case 0:
-      return ''
+      return '北边'
     case 90:
-      return ''
+      return '东边'
     case 180:
-      return ''
+      return '南边'
     case 270:
-      return ''
+      return '西边'
     default:
       return ''
   }
 })
 
-const xArrow = computed(() => {
-  switch (angle.value) {
-    case 0:
-      return '➡'
-    case 90:
-      return '⬆'
-    case 180:
-      return '⬅'
-    case 270:
-      return '⬇'
-    default:
-      return ''
+function getDirectionLabelWithIcon(axis: 'x' | 'y', angleDeg: number): string {
+  const rad = (angleDeg * Math.PI) / 180
+  const dx = axis === 'x' ? Math.cos(rad) : Math.sin(rad)
+  const dy = axis === 'x' ? -Math.sin(rad) : Math.cos(rad)
+
+  const deg = Math.atan2(dx, dy) * (180 / Math.PI)
+  const normalized = (deg + 360) % 360
+
+  if (normalized >= 45 && normalized < 135) return '东 ➡'
+  if (normalized >= 135 && normalized < 225) return '南 ⬇'
+  if (normalized >= 225 && normalized < 315) return '西 ⬅'
+  return '北 ⬆'
+}
+
+const xArrow = computed(() => getDirectionLabelWithIcon('x', adjustedAngle.value))
+const yArrow = computed(() => getDirectionLabelWithIcon('y', adjustedAngle.value))
+
+function getFullAxisStyle(axis: 'x' | 'y') {
+  const originX = radar.value.left
+  const originY = radar.value.top
+  const normalized = ((adjustedAngle.value % 360) + 360) % 360
+
+  // 轴线长度:贯穿整个盒子
+  const length = axis === 'x' ? areaWidth.value : areaHeight.value
+
+  // 方向角度(以雷达为原点)
+  let rotateDeg = 0
+  if (axis === 'x') {
+    rotateDeg = normalized
+  } else {
+    rotateDeg = normalized - 90
   }
-})
 
-const yArrow = computed(() => {
-  switch (angle.value) {
-    case 0:
-      return '⬆'
-    case 90:
-      return '⬅'
-    case 180:
-      return '⬇'
-    case 270:
-      return '➡'
-    default:
-      return ''
+  return {
+    position: 'absolute',
+    left: `${originX}px`,
+    top: `${originY}px`,
+    width: `${length}px`,
+    height: '1px',
+    backgroundColor: axis === 'x' ? 'red' : 'blue',
+    transform: `translate(-50%, -50%) rotate(${rotateDeg}deg)`,
+    transformOrigin: 'center center',
+    zIndex: 5,
+    pointerEvents: 'none',
+  } as CSSProperties
+}
+
+function getAxisDirectionVector(axis: 'x' | 'y', angleDeg: number) {
+  const rad = (angleDeg * Math.PI) / 180
+
+  // Y轴正方向是正北,X轴正方向是正东(默认)
+  // 所以我们旋转 Y轴 (0, 1) 和 X轴 (1, 0)
+  if (axis === 'y') {
+    return {
+      dx: Math.sin(rad), // Y轴旋转后 x 分量
+      dy: Math.cos(rad), // Y轴旋转后 y 分量
+    }
+  } else {
+    return {
+      dx: Math.cos(rad), // X轴旋转后 x 分量
+      dy: -Math.sin(rad), // X轴旋转后 y 分量(注意负号)
+    }
   }
-})
+}
+
+function getAxisDotStyle(axis: 'x' | 'y') {
+  const radarX = radar.value.left
+  const radarY = radar.value.top
+  const boxWidth = areaWidth.value
+  const boxHeight = areaHeight.value
+
+  const { dx, dy } = getAxisDirectionVector(axis, adjustedAngle.value)
+
+  // 计算最大可延伸距离,避免超出盒子
+  const scaleX = dx !== 0 ? (dx > 0 ? boxWidth - radarX : -radarX) / dx : Infinity
+  const scaleY = dy !== 0 ? (dy > 0 ? boxHeight - radarY : -radarY) / dy : Infinity
+  const scale = Math.min(scaleX, scaleY)
+
+  const x = radarX + dx * scale
+  const y = radarY - dy * scale
+
+  return {
+    position: 'absolute',
+    left: `${x}px`,
+    top: `${y}px`,
+    width: '8px',
+    height: '8px',
+    borderRadius: '50%',
+    backgroundColor: axis === 'x' ? 'red' : 'blue',
+    transform: 'translate(-50%, -50%)',
+    zIndex: 10,
+    pointerEvents: 'none',
+  } as CSSProperties
+}
 </script>
 
 <style lang="less" scoped>
@@ -292,6 +368,59 @@ const yArrow = computed(() => {
     pointer-events: none;
     min-width: 200px;
   }
+
+  .full-coordinate-axes {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    z-index: 5;
+    pointer-events: none;
+
+    .axis-line {
+      position: absolute;
+      height: 1px;
+      background-color: #000;
+    }
+
+    .x-axis {
+      background-color: red;
+      opacity: 0.6;
+    }
+
+    .y-axis {
+      background-color: blue;
+      opacity: 0.6;
+    }
+
+    .axis-arrow {
+      position: absolute;
+      font-weight: bold;
+      font-size: 12px;
+      line-height: 1;
+    }
+  }
+
+  .axis-markers {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    z-index: 10;
+    pointer-events: none;
+
+    .axis-dot {
+      position: absolute;
+    }
+
+    .x-dot {
+      background-color: red;
+      opacity: 0.6;
+    }
+
+    .y-dot {
+      background-color: blue;
+      opacity: 0.6;
+    }
+  }
 }
 
 .target-dot {

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

@@ -173,12 +173,12 @@
           </template>
         </a-input>
       </a-form-item>
-      <a-form-item label="正北向夹角" name="northAngle">
+      <a-form-item label="电源灯朝向" name="northAngle">
         <a-select ref="select" v-model:value="baseFormState.northAngle" :style="inputStyle">
-          <a-select-option :value="0">0度</a-select-option>
-          <a-select-option :value="90">90度</a-select-option>
-          <a-select-option :value="180">180度</a-select-option>
-          <a-select-option :value="270">270度</a-select-option>
+          <a-select-option :value="0">北(0度</a-select-option>
+          <a-select-option :value="90">东(90度</a-select-option>
+          <a-select-option :value="180">南(180度</a-select-option>
+          <a-select-option :value="270">西(270度</a-select-option>
         </a-select>
       </a-form-item>