Przeglądaj źródła

feat(photography): 添加常用操作悬浮按钮功能

- 新增一键释放、一键回落和设备关机操作按钮
- 实现悬浮按钮展开收起交互效果
- 集成socket通信发送设备控制指令
- 添加操作确认弹窗防止误操作
- 增加操作日志记录功能
- 实现遮罩层点击关闭功能
- 添加危险操作样式区分
- 适配demo模式下隐藏功能
panqiuyao 19 godzin temu
rodzic
commit
7210ae592f
1 zmienionych plików z 193 dodań i 0 usunięć
  1. 193 0
      frontend/src/views/Photography/shot.vue

+ 193 - 0
frontend/src/views/Photography/shot.vue

@@ -277,6 +277,41 @@
         </div>
         </div>
     </div>
     </div>
   </div>
   </div>
+
+  <!-- 常用操作悬浮按钮 -->
+  <div class="shot-fab" v-if="!isDemoMode" :class="{ open: fabOpen }">
+    <div v-if="fabOpen" class="shot-fab-mask" @click="fabOpen = false"></div>
+    <div class="shot-fab-actions">
+      <div
+        class="shot-fab-action"
+        @click="handleOneClickRelease"
+        v-log="{ describe: { action: '点击一键释放' } }"
+      >
+        <span class="shot-fab-action-text">一键释放</span>
+      </div>
+      <div
+        class="shot-fab-action"
+        @click="handleOneKeyDown"
+        v-log="{ describe: { action: '点击一键回落' } }"
+      >
+        <span class="shot-fab-action-text">一键回落</span>
+      </div>
+      <div
+        class="shot-fab-action danger"
+        @click="handlePowerOff"
+        v-log="{ describe: { action: '点击设备关机' } }"
+      >
+        <span class="shot-fab-action-text">设备关机</span>
+      </div>
+    </div>
+    <div
+      class="shot-fab-btn"
+      @click="fabOpen = !fabOpen"
+      v-log="{ describe: { action: '点击常用操作' } }"
+    >
+      <span>{{ fabOpen ? '收起' : '常用' }}</span>
+    </div>
+  </div>
 </template>
 </template>
 <script setup lang="ts">
 <script setup lang="ts">
 import headerBar from '@/components/header-bar/index.vue'
 import headerBar from '@/components/header-bar/index.vue'
@@ -290,6 +325,9 @@ import ImagePreview, { PreviewItem } from '@/components/PreviewImage/index.vue'
 import generate from '@/utils/menus/generate'
 import generate from '@/utils/menus/generate'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import { ElMessage, ElMessageBox } from 'element-plus'
 import useUserInfo from '@/stores/modules/user'
 import useUserInfo from '@/stores/modules/user'
+import socket from '@/stores/modules/socket'
+
+const socketStore = socket()
 
 
 const userInfoStore = useUserInfo()
 const userInfoStore = useUserInfo()
 const isHLM = computed(() => userInfoStore.isHLM)
 const isHLM = computed(() => userInfoStore.isHLM)
@@ -336,6 +374,74 @@ const {
   onDemoProgramTypeChange,
   onDemoProgramTypeChange,
 } = usePhotography()
 } = usePhotography()
 
 
+const fabOpen = ref(false)
+
+async function handleOneClickRelease() {
+  fabOpen.value = false
+  try {
+    await ElMessageBox.confirm(
+      '确定要一键释放设备吗?',
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      },
+    )
+    socketStore.sendMessage({
+      type: 'oneclick_release',
+      data: 'oneclick_release',
+    })
+    ElMessage.success('已发送一键释放指令')
+  } catch {
+    // 用户取消
+  }
+}
+
+async function handleOneKeyDown() {
+  fabOpen.value = false
+  try {
+    await ElMessageBox.confirm(
+      '确定要一键回落设备吗?',
+      '提示',
+      {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      },
+    )
+    socketStore.sendMessage({
+      type: 'one_key_down',
+      data: 'one_key_down',
+    })
+    ElMessage.success('已发送一键回落指令')
+  } catch {
+    // 用户取消
+  }
+}
+
+async function handlePowerOff() {
+  fabOpen.value = false
+  try {
+    await ElMessageBox.confirm(
+      '确定要关闭拍照机吗?关机后需要手动重新开机。',
+      '设备关机',
+      {
+        confirmButtonText: '确定关机',
+        cancelButtonText: '取消',
+        type: 'warning',
+      },
+    )
+    socketStore.sendMessage({
+      type: 'mcu_power_off',
+      data: 'mcu_power_off',
+    })
+    ElMessage.success('已发送关机指令')
+  } catch {
+    // 用户取消
+  }
+}
+
 // 搜索货号
 // 搜索货号
 const handleSearch = () => {
 const handleSearch = () => {
   getPhotoRecords({ page: 1, goods_art_no: searchGoodsArtNo.value })
   getPhotoRecords({ page: 1, goods_art_no: searchGoodsArtNo.value })
@@ -1169,4 +1275,91 @@ watch(goodsList, () => {
     }
     }
   }
   }
 }
 }
+
+.shot-fab {
+  position: fixed;
+  left: 24px;
+  bottom: 24px;
+  z-index: 2000;
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+
+  .shot-fab-mask {
+    position: fixed;
+    inset: 0;
+    z-index: 1999;
+    background: transparent;
+  }
+
+  .shot-fab-actions {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+    gap: 10px;
+    margin-bottom: 12px;
+    opacity: 0;
+    pointer-events: none;
+    transform: translateY(12px);
+    transition: all 0.2s ease;
+    position: relative;
+    z-index: 2001;
+  }
+
+  &.open .shot-fab-actions {
+    opacity: 1;
+    pointer-events: auto;
+    transform: translateY(0);
+  }
+
+  .shot-fab-action {
+    min-width: 112px;
+    height: 40px;
+    padding: 0 16px;
+    background: #fff;
+    border-radius: 20px;
+    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.16);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+    user-select: none;
+
+    .shot-fab-action-text {
+      font-size: 14px;
+      color: #333;
+      font-weight: 500;
+    }
+
+    &:hover {
+      background: #f5f7fa;
+    }
+
+    &.danger .shot-fab-action-text {
+      color: #ff4c00;
+    }
+  }
+
+  .shot-fab-btn {
+    width: 56px;
+    height: 56px;
+    border-radius: 50%;
+    background: linear-gradient(135deg, #2fb0ff 0%, #b863fb 100%);
+    color: #fff;
+    font-size: 14px;
+    font-weight: 600;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+    box-shadow: 0 6px 16px rgba(47, 176, 255, 0.4);
+    user-select: none;
+    position: relative;
+    z-index: 2001;
+
+    &:hover {
+      opacity: 0.92;
+    }
+  }
+}
 </style>
 </style>