Browse Source

更新定位功能

wangfumin 2 days ago
parent
commit
044a2f2f65

+ 0 - 0
src/views/positioning/components/editMedia.vue


+ 43 - 2
src/views/positioning/components/mediaList.vue

@@ -2,13 +2,54 @@
   <div class="media-list">
     <div class="media-item">
       <div class="media-item-img">
-        11111111111
+        <p>定位设备</p>
+        <ui-group style="padding-bottom: 60px">
+            <positionSign
+                v-for="guide in guides"
+                :key="guide.id"
+                :guide="guide"
+                @edit="edit(guide)"
+                @delete="deleteGuide(guide)"
+                @updateTitle="(title) => (guide.title = title)"
+            />
+        </ui-group>
       </div>
     </div>
   </div>
 </template>
 
-<script lang="ts" setup></script>
+<script lang="ts" setup>
+import positionSign from "@/views/positioning/components/sign.vue";
+import { ref, watchEffect } from "vue";
+import { useViewStack } from "@/hook";
+import { guides, createGuide, enterEdit, sysBus, autoSaveGuides } from "@/store";
+
+import type { Guide } from "@/store";
+
+const currentGuide = ref<Guide | null>(null);
+const emit = defineEmits<{ (e: "update:current", v: Guide | null): void }>();
+watchEffect(() => emit("update:current", currentGuide.value));
+
+const leaveEdit = () => (currentGuide.value = null);
+const edit = (guide: Guide) => {
+  currentGuide.value = guide;
+  enterEdit();
+  sysBus.on("leave", leaveEdit);
+};
+
+const deleteGuide = (guide: Guide) => {
+  const index = guides.value.indexOf(guide);
+  guides.value.splice(index, 1);
+};
+
+useViewStack(autoSaveGuides);
+
+defineExpose({
+  add: () => {
+    edit(createGuide());
+  },
+});
+</script>
 
 <style lang="scss" scoped>
 .media-list {

+ 211 - 0
src/views/positioning/components/sign.vue

@@ -0,0 +1,211 @@
+<template>
+    <ui-group-option class="sign-guide" :class="{ search }">
+      <div class="info">
+        <div class="guide-cover">
+          <img :src="getResource(getFileUrl(guide.cover))" />
+          <ui-icon
+            type="preview"
+            class="icon"
+            ctrl
+            @click="flyPlayGuide(guide)"
+            v-if="paths.length"
+          />
+        </div>
+        <div>
+          <p>{{ guide.title }}</p>
+          <!-- <ui-input
+            class="view-title-input"
+            type="text"
+            :modelValue="guide.title"
+            :maxlength="15"
+            @update:modelValue="(title: string) => $emit('updateTitle', title.trim())"
+            v-show="isEditTitle"
+            ref="inputRef"
+            height="28px"
+          /> -->
+        </div>
+      </div>
+      <div class="actions" v-if="edit">
+        <ui-more
+          :options="menus"
+          style="margin-left: 20px"
+          @click="(action: keyof typeof actions) => actions[action]()"
+        />
+      </div>
+    </ui-group-option>
+  </template>
+  
+  <script setup lang="ts">
+  import { Guide, getGuidePaths } from "@/store";
+  import { getFileUrl, saveAs } from "@/utils";
+  import { getResource } from "@/env";
+  import { computed, watchEffect, nextTick, ref } from "vue";
+  import { playSceneGuide, isScenePlayIng, pauseSceneGuide } from "@/sdk";
+  import { VideoRecorder } from "simaqcore";
+  import useFocus from "bill/hook/useFocus";
+  import { Message } from "bill/expose-common";
+  import { flyPlayGuide } from "@/hook/use-fly";
+  
+  const props = withDefaults(
+    defineProps<{ guide: Guide; edit?: boolean; search?: boolean }>(),
+    {
+      edit: true,
+      search: false,
+    }
+  );
+  
+  const inputRef = ref();
+  const isEditTitle = useFocus(computed(() => inputRef.value?.vmRef.root));
+  watchEffect(() => {
+    if (!isEditTitle.value && !props.guide.title.length) {
+      isEditTitle.value = true;
+      Message.warning("导览名称不可为空");
+    }
+  });
+  
+  const emit = defineEmits<{
+    (e: "updateTitle", t: string): void;
+    (e: "delete"): void;
+    (e: "play"): void;
+    (e: "edit"): void;
+    (e: "record"): void;
+  }>();
+  
+//   const menus = [
+//     { label: "重命名", value: "editTitle" },
+//     { label: "编辑", value: "edit" },
+//     { label: "删除", value: "delete" },
+//   ];
+    const menus = [
+        { label: "编辑", value: "edit" },
+        { label: "录制", value: "record" },
+    ];
+  const actions = {
+    edit: () => emit("edit"),
+    record: () => emit("record"),
+    delete: () => emit("delete"),
+    download: () => {
+      const config: any = {
+        // uploadUrl: '',
+        // resolution: '4k',
+        // autoDownload: false,
+        // systemAudio: true,
+        // debug: true,
+        resolution: "4k",
+        autoDownload: false,
+        platform: "canvas",
+  
+        config: {
+          frameRate: 60,
+          canvasId: ".scene-canvas > canvas",
+        },
+        disbaledAudio: false,
+        systemAudio: false,
+        debug: false,
+      };
+  
+      const videoRecorder = new VideoRecorder(config);
+      videoRecorder.startRecord();
+  
+      let stopWatch: () => void;
+      const stopRecord = () => {
+        stopWatch && stopWatch();
+        pauseSceneGuide();
+      };
+  
+      videoRecorder.on("record", (blob) => {
+        saveAs(
+          new File([blob], "录屏.mp4", { type: "video/mp4; codecs=h264" }),
+          props.guide.title + ".mp4"
+        );
+      });
+  
+      videoRecorder.off("*");
+      videoRecorder.on("startRecord", () => {
+        flyPlayGuide(props.guide);
+        stopWatch = watchEffect(() => {
+          if (!isScenePlayIng.value) {
+            videoRecorder.endRecord();
+            nextTick(stopWatch);
+          }
+        });
+      });
+      videoRecorder.on("cancelRecord", stopRecord);
+      videoRecorder.on("endRecord", stopRecord);
+    },
+  };
+  const paths = computed(() => getGuidePaths(props.guide));
+  </script>
+  
+  <style lang="scss" scoped>
+  .sign-guide {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 20px 0;
+    border-bottom: 1px solid var(--colors-border-color);
+  
+    &.search {
+      padding: 0;
+      border: none;
+      margin-bottom: 5px;
+    }
+  
+    .info {
+      flex: 1;
+  
+      display: flex;
+      align-items: center;
+  
+      .guide-cover {
+        position: relative;
+        &::after {
+          content: "";
+          position: absolute;
+          inset: 0;
+          background: rgba(0, 0, 0, 0.2);
+        }
+  
+        .icon {
+          position: absolute;
+          z-index: 1;
+          left: 50%;
+          top: 50%;
+          transform: translate(-50%, -50%);
+          font-size: 16px;
+        }
+  
+        img {
+          width: 48px;
+          height: 48px;
+          object-fit: cover;
+          border-radius: 4px;
+          overflow: hidden;
+          background-color: rgba(255, 255, 255, 0.6);
+          display: block;
+        }
+      }
+  
+      div:not(.view-title-input) {
+        margin-left: 10px;
+  
+        p {
+          color: #fff;
+          font-size: 14px;
+          margin-bottom: 6px;
+        }
+      }
+    }
+  
+    .actions {
+      flex: none;
+    }
+  }
+  </style>
+  
+  <style>
+  .view-title-input.ui-input .text.suffix input {
+    padding-right: 50px;
+  }
+  </style>
+  

+ 26 - 0
src/views/setting/index.vue

@@ -14,6 +14,23 @@
         <selectBack :value="setting?.back || setting?.mapId ? [setting?.back, setting?.mapId] : ['dt', 1]" @update:value="changeBack" />
       </ui-group-option>
     </ui-group>
+
+    <ui-group title="设置定位参照模型">
+      <ui-group-option>
+        <p class="model-name">模型名称</p>
+        <div class="model-name-input">
+          11111111111111
+        </div>
+        <ui-input
+            class="view-title-input"
+            type="text"
+            :modelValue="modelName"
+            :maxlength="100"
+            height="40px"
+            @update:modelValue="(title: string) => modelName = title.trim()"
+        />
+      </ui-group-option>
+    </ui-group>
   </RightFillPano>
 </template>
 
@@ -50,6 +67,7 @@ const enterSetPic = () => {
 let initBack = setting.value!.back;
 let initMapId = setting.value!.mapId;
 let isFirst = true;
+const modelName = ref("");
 const changeBack = ([back, mapId]: [string | null, number | null]) => {
   setting.value!.back = back;
   setting.value!.mapId = mapId;
@@ -102,4 +120,12 @@ const changeBack = ([back, mapId]: [string | null, number | null]) => {
   text-align: center;
   cursor: pointer;
 }
+.model-name{
+  margin-bottom: 20px;
+}
+.model-name-input{
+  height: 40px;
+  display: flex;
+  align-items: center;
+}
 </style>