|
|
@@ -277,6 +277,41 @@
|
|
|
</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>
|
|
|
<script setup lang="ts">
|
|
|
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 { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
import useUserInfo from '@/stores/modules/user'
|
|
|
+import socket from '@/stores/modules/socket'
|
|
|
+
|
|
|
+const socketStore = socket()
|
|
|
|
|
|
const userInfoStore = useUserInfo()
|
|
|
const isHLM = computed(() => userInfoStore.isHLM)
|
|
|
@@ -336,6 +374,74 @@ const {
|
|
|
onDemoProgramTypeChange,
|
|
|
} = 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 = () => {
|
|
|
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>
|