Jelajahi Sumber

fix: 制作新需求

bill 8 bulan lalu
induk
melakukan
175e5b2105

+ 8 - 0
src/api/fuse-model.ts

@@ -47,6 +47,11 @@ interface ServiceFuseModel {
   sceneData: Scene;
 }
 
+export const uploadMaterialToModel = async (uploadId: number) => {
+  const model = await axios.post<{modelId: number}>('/fusion/model/addByMediaLibrary', {caseId: params.caseId, uploadId})
+  return model
+}
+
 export const getSceneUrl = (sceneData: Scene) => {
   let url: any = [""];
   console.log(sceneData, sceneData.type);
@@ -68,6 +73,9 @@ export const getSceneUrl = (sceneData: Scene) => {
     try {
       url = JSON.parse(url);
     } catch (e) {
+      if (typeof url === 'string') {
+        url = [url]
+      }
       console.error(url, e);
     }
   }

+ 3 - 0
src/api/material.ts

@@ -43,6 +43,7 @@ export type Material = {
   size: number;
   groupId: number;
   group: string;
+  uploadId?: number;
   modelId?: number;
 };
 
@@ -67,6 +68,7 @@ export const fetchMaterialPage = async (params: MaterialPageProps) => {
       size: Number(item.fileSize),
       groupId: item.dictId,
       group: item.dictName,
+      uploadId: item.uploadId
     }))
   }
   
@@ -89,6 +91,7 @@ export const addMaterial = (file: File) => {
     data: jsonToForm({ file }),
     headers: { ...UPLOAD_HEADS },
   });
+  
 };
 
 export const delMaterial = (id: Material["id"]) => {

+ 22 - 22
src/components/bill-ui/components/bubble/index.vue

@@ -1,31 +1,31 @@
 <template>
-    <transition name="fade">
-        <div class="bubble" :class="{ [type]: true, [level]: true }" v-if="show" @click.stop>
-            <div class="bubble-layer">
-                <div class="bubble-arr"></div>
-                <slot />
-            </div>
-        </div>
-    </transition>
+  <transition name="fade">
+    <div class="bubble" :class="{ [type]: true, [level]: true }" v-if="show" @click.stop>
+      <div class="bubble-layer">
+        <div class="bubble-arr"></div>
+        <slot />
+      </div>
+    </div>
+  </transition>
 </template>
 
 <script setup>
 defineProps({
-    type: {
-        type: String,
-        default: 'right',
-    },
-    show: {
-        type: Boolean,
-        default: true,
-    },
-    level: {
-        type: String,
-        require: false,
-    },
-})
+  type: {
+    type: String,
+    default: "right",
+  },
+  show: {
+    type: Boolean,
+    default: true,
+  },
+  level: {
+    type: String,
+    require: false,
+  },
+});
 </script>
 
 <script>
-export default { name: 'ui-bubble' }
+export default { name: "ui-bubble" };
 </script>

+ 6 - 1
src/components/materials/index.vue

@@ -21,7 +21,7 @@
               class="input"
               :accept="ft"
               :maxSize="maxSize"
-              @update:modelValue="addMaterial"
+              @update:modelValue="(file: File) => uploadHandler(file)"
               type="file"
             >
               <template v-slot:replace>
@@ -197,6 +197,11 @@ const refresh = debounceStack(
   160
 );
 
+const uploadHandler = async (file: File) => {
+  await addMaterial(file);
+  refresh();
+};
+
 watch(params, refresh, { immediate: true, deep: true });
 
 const addHandler = async (file: File) => {

+ 33 - 18
src/components/static-preview/resource.vue

@@ -1,21 +1,29 @@
 <template>
-  <video v-if="type === MetaType.video" controls autoplay playsinline webkit-playsinline>
-    <source :src="url" />
-  </video>
-  <iframe v-else-if="type === MetaType.other" :src="url"></iframe>
-  <iframe
-    v-else-if="type === MetaType.xfile"
-    :src="`./xfile-viewer/index.html?file=${url}&time=${Date.now()}`"
-  ></iframe>
-  <img :src="url" v-if="type === MetaType.image" />
-  <audio
-    :src="url"
-    v-if="type === MetaType.audio"
-    controls
-    autoplay
-    playsinline
-    webkit-playsinline
-  />
+  <div>
+    <video
+      v-if="type === MetaType.video"
+      controls
+      autoplay
+      playsinline
+      webkit-playsinline
+    >
+      <source :src="url" />
+    </video>
+    <iframe v-else-if="type === MetaType.other" :src="url"></iframe>
+    <iframe
+      v-else-if="type === MetaType.xfile"
+      :src="`./xfile-viewer/index.html?file=${url}&time=${Date.now()}`"
+    ></iframe>
+    <img :src="url" v-if="type === MetaType.image" />
+    <audio
+      :src="url"
+      v-if="type === MetaType.audio"
+      controls
+      autoplay
+      playsinline
+      webkit-playsinline
+    />
+  </div>
 </template>
 
 <script lang="ts" setup>
@@ -44,6 +52,13 @@ const type = computed(() => {
 </script>
 
 <style scoped>
+div {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
 audio,
 iframe {
   width: 100%;
@@ -56,7 +71,7 @@ img {
   max-width: 100%;
   max-height: 100%;
   display: block;
-  object-fit: contain;
+  object-fit: cover;
 }
 
 iframe {

+ 27 - 2
src/components/tagging/sign-new.vue

@@ -7,8 +7,24 @@
     @mouseleave="isHover = false"
   >
     <div @click.stop>
-      <UIBubble class="hot-bubble pc" :show="showContent" type="left" level="center">
-        <h2>{{ tagging.title }}</h2>
+      <UIBubble
+        class="hot-bubble pc"
+        :show="showContent"
+        type="left"
+        level="center"
+        @click.stop
+        @pointerdown.stop
+        @pointerup.stop
+      >
+        <h2>
+          {{ tagging.title }}
+          <ui-audio
+            v-if="tagging.audio"
+            class="audio"
+            :src="getResource(getFileUrl(tagging.audio))"
+            ref="audio"
+          />
+        </h2>
         <div class="content">
           <p><span>特征描述:</span>{{ tagging.desc }}</p>
           <p><span>遗留部位:</span>{{ tagging.part }}</p>
@@ -63,6 +79,13 @@ const emit = defineEmits<{
   ): void;
 }>();
 
+const audio = ref();
+watchEffect(() => {
+  audio.value && console.error("准备好了!,");
+  if (props.show && audio.value) {
+    audio.value.play();
+  }
+});
 const [posStyle, pos, pause, recovery] = usePixel(() => undefined);
 
 const queryItems = computed(() =>
@@ -223,6 +246,8 @@ defineExpose(tag);
       margin-bottom: 10px;
       color: #ffffff;
       position: relative;
+      display: flex;
+      justify-content: space-between;
     }
 
     .content {

+ 6 - 0
src/components/tagging/sign.vue

@@ -18,6 +18,12 @@
     <div @click.stop>
       <UIBubble class="hot-bubble pc" :show="showContent" type="left" level="center">
         <h2>{{ tagging.title }}</h2>
+        <ui-audio
+          v-if="tagging.audio"
+          class="audio"
+          :src="getResource(getFileUrl(tagging.audio))"
+          ref="audio"
+        />
         <div class="content">
           <p><span>特征描述:</span>{{ tagging.desc }}</p>
           <p><span>遗留部位:</span>{{ tagging.part }}</p>

+ 5 - 1
src/layout/edit/fuse-left-pano.vue

@@ -1,6 +1,10 @@
 <template>
   <LeftPano>
-    <ModelList :can-change="custom.modelsChangeStore" @delete-model="modelDelete" @click-model="modelChangeSelect">
+    <ModelList
+      :can-change="custom.modelsChangeStore"
+      @delete-model="modelDelete"
+      @click-model="modelChangeSelect"
+    >
       <template #action v-if="custom.modelsChangeStore">
         <SelectModel>
           <ui-icon ctrl type="add" />

+ 18 - 3
src/layout/edit/fuse-switch.vue

@@ -1,7 +1,9 @@
 <template>
   <SlideMenu />
-  <!-- <Header></Header> -->
-  <ModelList />
+  <ModelList v-if="!showSceneList" />
+  <LeftPano v-else>
+    <SceneList :current="currentModel" @update:current="loadModel" />
+  </LeftPano>
 
   <router-view v-slot="{ Component }">
     <!-- <keep-alive> -->
@@ -12,6 +14,19 @@
 
 <script lang="ts" setup>
 import SlideMenu from "./fuse-slide-menu.vue";
-import Header from "./header/index.vue";
+import SceneList from "../scene-list/index.vue";
+import { LeftPano } from "@/layout";
 import ModelList from "./fuse-left-pano.vue";
+import { computed, watch, watchEffect } from "vue";
+import router from "@/router";
+import { currentModel, fuseModel, loadModel } from "@/model";
+
+const showSceneList = computed(
+  () => router.currentRoute.value.meta.left === "scene-list"
+);
+watch(showSceneList, (n, o) => {
+  if (!n && o) {
+    loadModel(fuseModel);
+  }
+});
 </script>

+ 5 - 2
src/layout/edit/scene-select.vue

@@ -89,7 +89,7 @@ import {
   initialScenes,
 } from "@/store";
 
-import type { Scene } from "@/api";
+import { uploadMaterialToModel, type Scene } from "@/api";
 import { getSceneModel } from "@/sdk";
 import { selectMaterials } from "@/components/materials/quisk";
 
@@ -203,7 +203,9 @@ const selectModel = async () => {
     maxSize: 2 * 1024 * 1024 * 1024,
   });
   if (!list?.length) return;
-  const modelIds = list
+
+  const modelList = await Promise.all(list.filter(item => item.uploadId).map(item => uploadMaterialToModel(item.uploadId!)))
+  const modelIds = modelList
     .map((item) => item.modelId!)
     .filter(
       (modelId) => modelId && !fuseModels.value.some((model) => model.modelId === modelId)
@@ -230,6 +232,7 @@ const selectModel = async () => {
 .model-header .header-desc {
   margin-bottom: 0;
 }
+
 .ant-modal-root .ant-table-tbody > tr > td {
   word-break: break-all;
 }

+ 1 - 1
src/model/app.vue

@@ -87,7 +87,7 @@ export const Model = defineComponent({
           [SceneType.SWSSMX]: `/swkk/spg.html?m=${scene.value.num}&toen=${getToken()}`,
           [SceneType.SWMX]: `index.html?caseId=${params.caseId}&modelId=${
             scene.value.num
-          }#sign-model&toen=${getToken()}`,
+          }&toen=${getToken()}#sign-model`,
           [SceneType.SWYDSS]: `/swss/index.html?m=${scene.value.num}&toen=${getToken()}`,
           [SceneType.SWYDMX]: `/swkk/spg.html?m=${scene.value.num}&toen=${getToken()}`,
         });

+ 2 - 1
src/router/config.ts

@@ -1,5 +1,4 @@
 import { RoutesName, paths, metas } from './constant'
-
 export type RouteRaw = (typeof routes)[number]
 export const routes = [
   {
@@ -15,6 +14,7 @@ export const routes = [
           {
             path: paths[RoutesName.view],
             name: RoutesName.view,
+            meta: metas[RoutesName.view],
             component: () => import('@/views/view/index.vue')
           },
           {
@@ -96,6 +96,7 @@ export const routes = [
       {
         path: paths[RoutesName.viewShow],
         name: RoutesName.viewShow,
+        mate: { left: 'scene-list' },
         component: () => import('@/views/view/show.vue')
       },
       {

+ 1 - 0
src/router/constant.ts

@@ -98,6 +98,7 @@ export const metas = {
     sysTitle: "视图提取",
     icon: "nav-setup",
     title: "视图提取",
+    left: 'scene-list'
   },
   [RoutesName.record]: { sysTitle: "屏幕录制" },
   [RoutesName.show]: { sysTitle: "" },

+ 2 - 3
src/views/folder/floder-view.vue

@@ -8,10 +8,9 @@
       class="solid header"
       :class="{ ['root-header']: index === 1 }"
       :style="{ '--index': index }"
+      @click="showChildren = !showChildren"
     >
-      <span @click="showChildren = !showChildren">
-        {{ root.title }}
-      </span>
+      <span> {{ root.title }} </span>
       <ui-icon
         :type="`pull-${showChildren ? 'up' : 'down'}`"
         class="icon"

+ 1 - 1
src/views/guide/index.vue

@@ -24,7 +24,7 @@ import PathEdit from "./path/edit.vue";
 import { ref } from "vue";
 import { isEdit } from "@/store";
 
-const current = ref("path");
+const current = ref("guide");
 const tabs = [
   { key: "guide", text: "导览" },
   { key: "path", text: "路线" },

+ 73 - 70
src/views/tagging/sign.vue

@@ -1,33 +1,30 @@
 <template>
-  <ui-group-option 
-    class="sign-tagging" 
-    :class="{active: selected, edit}" 
+  <ui-group-option
+    class="sign-tagging"
+    :class="{ active: selected, edit }"
     @click="edit && getTaggingIsShow(tagging) && emit('select', true)"
   >
     <div class="info">
-      <img 
-        :src="getResource(getFileUrl(tagging.images[0]))" 
-        v-if="tagging.images.length"
-      >
+      <img :src="getResource(getFileUrl(findImage))" v-if="findImage" />
       <div>
         <p>{{ tagging.title }}</p>
         <span>放置:{{ positions.length }}</span>
       </div>
     </div>
     <div class="actions" @click.stop>
-      <ui-icon 
-         v-if="!edit"
-        type="pin" 
-        ctrl 
-        @click.stop="$emit('select', true)" 
+      <ui-icon
+        v-if="!edit"
+        type="pin"
+        ctrl
+        @click.stop="$emit('select', true)"
         :class="{ disabled: !getTaggingIsShow(tagging) }"
       />
       <template v-else>
         <ui-icon type="pin1" ctrl @click.stop="$emit('fixed')" tip="放置" />
-        <ui-more 
-          :options="menus" 
-          style="margin-left: 20px" 
-          @click="(action: keyof typeof actions) => actions[action]()" 
+        <ui-more
+          :options="menus"
+          style="margin-left: 20px"
+          @click="(action: keyof typeof actions) => actions[action]()"
         />
       </template>
     </div>
@@ -35,88 +32,94 @@
 </template>
 
 <script setup lang="ts">
-import { getFileUrl } from '@/utils'
-import { computed, ref, watchEffect, nextTick } from 'vue';
-import { getResource, showTaggingPositionsStack } from '@/env'
-import { sdk } from '@/sdk'
-import { 
-  getTaggingStyle, 
-  getTaggingPositions, 
+import { getFileUrl, getUrlType, MetaType } from "@/utils";
+import { computed, ref, watchEffect, nextTick } from "vue";
+import { getResource, showTaggingPositionsStack } from "@/env";
+import { sdk } from "@/sdk";
+import {
+  getTaggingStyle,
+  getTaggingPositions,
   getFuseModel,
   getFuseModelShowVariable,
-  getTaggingIsShow
-} from '@/store'
+  getTaggingIsShow,
+} from "@/store";
 
-import type { Tagging } from '@/store'
+import type { Tagging } from "@/store";
 
 const props = withDefaults(
-  defineProps<{ tagging: Tagging, selected?: boolean, edit?: boolean }>(),
+  defineProps<{ tagging: Tagging; selected?: boolean; edit?: boolean }>(),
   { edit: true }
-)
-const style = computed(() => getTaggingStyle(props.tagging.styleId))
-const positions = computed(() => getTaggingPositions(props.tagging))
+);
+const style = computed(() => getTaggingStyle(props.tagging.styleId));
+const positions = computed(() => getTaggingPositions(props.tagging));
 
-const emit = defineEmits<{ 
-  (e: 'delete'): void 
-  (e: 'edit'): void
-  (e: 'select', selected: boolean): void
-  (e: 'fixed'): void
-}>()
+const emit = defineEmits<{
+  (e: "delete"): void;
+  (e: "edit"): void;
+  (e: "select", selected: boolean): void;
+  (e: "fixed"): void;
+}>();
+
+const findImage = computed(() => {
+  return props.tagging.images.find(
+    (a) => getUrlType(getResource(getFileUrl(a))) === MetaType.image
+  );
+});
 
 const menus = [
-  { label: '编辑', value: 'edit' },
-  { label: '删除', value: 'delete' },
-]
+  { label: "编辑", value: "edit" },
+  { label: "删除", value: "delete" },
+];
 const actions = {
-  edit: () => emit('edit'),
-  delete: () => emit('delete')
-}
+  edit: () => emit("edit"),
+  delete: () => emit("delete"),
+};
 
 const flyTaggingPositions = (tagging: Tagging, callback?: () => void) => {
-  const positions = getTaggingPositions(tagging)
+  const positions = getTaggingPositions(tagging);
 
-  let isStop = false
+  let isStop = false;
   const flyIndex = (i: number) => {
     if (isStop || i >= positions.length) {
-      callback && nextTick(callback)
+      callback && nextTick(callback);
       return;
     }
-    const position = positions[i]
-    const model = getFuseModel(position.modelId)
+    const position = positions[i];
+    const model = getFuseModel(position.modelId);
     if (!model || !getFuseModelShowVariable(model).value) {
-      flyIndex(i + 1)
+      flyIndex(i + 1);
       return;
     }
 
-    const pop = showTaggingPositionsStack.push(ref(new WeakSet([position])))
-    sdk.comeTo({ 
-      position: position.localPos, 
+    const pop = showTaggingPositionsStack.push(ref(new WeakSet([position])));
+    sdk.comeTo({
+      position: position.localPos,
       modelId: position.modelId,
       dur: 300,
-      distance: 3
-    })
-    
+      distance: 3,
+    });
+
     setTimeout(() => {
-      pop()
-      flyIndex(i + 1)
-    }, 2000)
-  }
-  flyIndex(0)
-  return () => isStop = true
-}
+      pop();
+      flyIndex(i + 1);
+    }, 2000);
+  };
+  flyIndex(0);
+  return () => (isStop = true);
+};
 watchEffect((onCleanup) => {
   if (props.selected) {
-    const success = () => emit('select', false)
-    const stop = flyTaggingPositions(props.tagging, success)
-    const keyupHandler = (ev: KeyboardEvent) => ev.code === 'Escape' && success()
+    const success = () => emit("select", false);
+    const stop = flyTaggingPositions(props.tagging, success);
+    const keyupHandler = (ev: KeyboardEvent) => ev.code === "Escape" && success();
 
-    document.documentElement.addEventListener('keyup', keyupHandler, false)
+    document.documentElement.addEventListener("keyup", keyupHandler, false);
     onCleanup(() => {
-      stop()
-      document.documentElement.removeEventListener('keyup', keyupHandler, false)
-    })
+      stop();
+      document.documentElement.removeEventListener("keyup", keyupHandler, false);
+    });
   }
-})
+});
 </script>
 
-<style lang="scss" scoped src="./style.scss"></style>
+<style lang="scss" scoped src="./style.scss"></style>

+ 2 - 7
src/views/view/index.vue

@@ -83,7 +83,7 @@ const deleteView = (record: View) => {
   }
 };
 
-const showLeftPano = ref(false);
+const showLeftPano = ref(true);
 watch(currentModel, () => {
   if (currentModel.value) {
     showLeftPano.value = false;
@@ -91,12 +91,7 @@ watch(currentModel, () => {
 });
 
 useViewStack(autoSaveViews);
-useViewStack(() =>
-  togetherCallback([
-    showLeftPanoStack.push(showLeftPano),
-    showRightPanoStack.push(ref(false)),
-  ])
-);
+useViewStack(() => togetherCallback([showLeftPanoStack.push(showLeftPano)]));
 </script>
 
 <style lang="scss" src="./style.scss" scoped></style>

+ 2 - 7
vite.config.ts

@@ -42,15 +42,10 @@ const proxy = {
     changeOrigin: true,
     rewrite: path => path.replace(/^\/laser-data/, '/laser-data')
   },
-  // '/fdkk': {
-  //   target: `${ip}/`,
-  //   changeOrigin: true,
-  //   rewrite: path => path.replace(/^\/fdkk/, '/fdkk')
-  // },
-  '/laser-data': {
+  '/fdkk': {
     target: `${ip}/`,
     changeOrigin: true,
-    rewrite: path => path.replace(/^\/laser-data/, '/laser-data')
+    rewrite: path => path.replace(/^\/fdkk/, '/fdkk')
   },
   '/service': {
     target: ip,