Parcourir la source

Merge branch 'v1.7.0-local' of http://192.168.0.115:3000/bill/fuse-code into v1.7.0-local

xzw il y a 9 mois
Parent
commit
f0aae799c4

+ 14 - 0
index.html

@@ -7,9 +7,23 @@
     <title>案件信息</title>
     <link rel="stylesheet" type="text/css" href="./lib/Cesium/Widgets/CesiumWidget/CesiumWidget.css">
     <link rel="stylesheet" type="text/css" href="./lib/Cesium/Widgets/CesiumWidget/lighter.css">
+    <style>
+      .app-floor-mark {
+        position: absolute;
+        bottom: 20px;
+        left: 50%;
+        transform: translateX(-50%);
+        z-index: 3;
+        color: #fff;
+        pointer-events: none;
+        font-size: 12px;
+        text-shadow: 0 0 10px rgba(0, 0, 0, 1);
+      }
+    </style>
   </head>
   <body>
     <div id="app"></div>
+    <p class="app-floor-mark">公安部鉴定中心 / 四维时代 | 公安部科技强警基础工作计划(2022JC13)</p>
     <script type="module" src="/src/main.ts"></script>
   </body>
 </html>

+ 1 - 0
public/lib/potree/potree.js

@@ -85262,6 +85262,7 @@
 	    //path = `${Potree.scriptPath}/data/${Potree.settings.number}/panos-${datasetId}.json`
 	    path = "".concat(Potree.settings.urls.prefix, "/laser/filter/").concat(Potree.settings.number, "/query");
 	  }
+		path = path + '/'+datasetId
 	  return loadFile(path, {
 	    datasetId: datasetId
 	  }, callback);

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

@@ -67,6 +67,9 @@ export const getSceneUrl = (sceneData: Scene) => {
     }
     try {
       url = JSON.parse(url);
+      url = url.map((u: any) => location.origin + (import.meta.env.DEV ? '/res' : '/') + u)
+      console.log(url)
+      
     } catch (e) {
       console.error(url, e);
     }

+ 1 - 1
src/api/instance.ts

@@ -64,7 +64,7 @@ addHook({
   } 
 })
 
-setDefaultURI(baseURL)
+setDefaultURI('/api')
 params.token && setToken(params.token)
 
 export default axios

+ 1 - 0
src/api/setup.ts

@@ -110,6 +110,7 @@ export const axiosFactory = () => {
   } = getExponseApi('hook')
 
   const setDefaultURI = (url: string) => {
+    console.error(url)
     axiosRaw.defaults.baseURL = url
   }
 

+ 29 - 28
src/components/static-preview/sign.vue

@@ -1,54 +1,55 @@
 <template>
   <div class="preview-layer">
     <div class="pull-meta">
-      <video v-if="media.type === MediaType.video" controls autoplay playsinline webkit-playsinline>
+      <video
+        v-if="media.type === MediaType.video"
+        controls
+        autoplay
+        playsinline
+        webkit-playsinline
+      >
         <source :src="staticURL" />
       </video>
       <iframe v-else-if="media.type === MediaType.web" :src="staticURL"></iframe>
-      <img :src="staticURL" v-if="media.type === MediaType.img"/>
+      <img :src="staticURL" v-if="media.type === MediaType.img" />
     </div>
   </div>
 </template>
 
 <script lang="ts">
-import { ref, watchEffect, defineComponent, PropType } from 'vue'
-import { getResource } from '@/env'
-import { MediaType } from './index.vue'
-import type { MediaItem } from './index.vue'
+import { ref, watchEffect, defineComponent, PropType } from "vue";
+import { MediaType } from "./index.vue";
+import type { MediaItem } from "./index.vue";
 
-
-export const Preview =  defineComponent({
-  name: 'static-preview',
+export const Preview = defineComponent({
+  name: "static-preview",
   props: {
     media: {
       type: Object as PropType<MediaItem>,
-        required: true
-    }
+      required: true,
+    },
   },
   emits: {
-    close: () => true
+    close: () => true,
   },
   setup(props) {
-    const staticURL = ref('')
+    const staticURL = ref("");
     watchEffect(() => {
-      const data = props.media.url
-      const url = typeof data === 'string'
-        ? getResource(data)
-        : URL.createObjectURL(data)
+      const data = props.media.url;
+      const url = typeof data === "string" ? data : URL.createObjectURL(data);
+
+      staticURL.value = url;
+      return () => URL.revokeObjectURL(url);
+    });
 
-      staticURL.value = url
-      return () => URL.revokeObjectURL(url)
-    })
-    
     return {
       staticURL,
-      MediaType
-    }
-  }
-})
-
+      MediaType,
+    };
+  },
+});
 
-export default Preview
+export default Preview;
 </script>
 
-<style scoped lang="scss" src="./style.scss"></style>
+<style scoped lang="scss" src="./style.scss"></style>

+ 80 - 77
src/components/tagging/sign.vue

@@ -1,40 +1,38 @@
 <template>
-  <div 
+  <div
     v-if="posStyle"
-    class="hot-item pc" 
-    :style="posStyle" 
+    class="hot-item pc"
+    :style="posStyle"
     @mouseenter="isHover = true"
     @mouseleave="isHover = false"
-    :class="{active: showContent}"
+    :class="{ active: showContent }"
   >
     <ui-tip :tip="tagging.title" foreShow tipV="top" class="tag-tip">
-      <img 
+      <img
         class="tag-img"
-        :src="getResource(getFileUrl(taggingStyle.icon))" 
-        @click="iconClickHandler" 
-        v-if="taggingStyle" 
+        :src="getResource(getFileUrl(taggingStyle.icon))"
+        @click="iconClickHandler"
+        v-if="taggingStyle"
       />
     </ui-tip>
     <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">
+        <h2>{{ tagging.title }}</h2>
         <div class="content">
           <p><span>特征描述:</span>{{ tagging.desc }}</p>
           <p><span>遗留部位:</span>{{ tagging.part }}</p>
           <p><span>提取方法:</span>{{ tagging.method }}</p>
           <p><span>提取人:</span>{{ tagging.principal }}</p>
         </div>
-        <Images 
-          :tagging="tagging" 
-          :in-full="true" 
-          @pull="index => (pullIndex = index)" 
+        <Images
+          :tagging="tagging"
+          :in-full="true"
+          @pull="(index) => (pullIndex = index)"
         />
-        <div class="edit-hot" v-if="router.currentRoute.value.name === RoutesName.tagging">
+        <div
+          class="edit-hot"
+          v-if="router.currentRoute.value.name === RoutesName.tagging"
+        >
           <span @click="$emit('delete')" class="fun-ctrl">
             <ui-icon type="del" />
             删除
@@ -42,85 +40,91 @@
         </div>
       </UIBubble>
 
-      <Preview 
+      <Preview
         @close="pullIndex = -1"
         :current="pullIndex"
         :items="queryItems"
-        v-if="!!~pullIndex" 
+        v-if="!!~pullIndex"
       />
     </div>
   </div>
 </template>
 
 <script lang="ts" setup>
-import { computed, ref, watchEffect, watch, onUnmounted } from 'vue'
-import { router, RoutesName } from '@/router'
-import UIBubble from 'bill/components/bubble/index.vue'
-import Images from '@/views/tagging/images.vue'
-import Preview, { MediaType } from '../static-preview/index.vue'
-import { getTaggingStyle, getFuseModel } from '@/store';
-import { getFileUrl } from '@/utils'
-import { sdk } from '@/sdk'
-import { custom, getResource } from '@/env'
-import { useViewStack } from '@/hook'
-
-import type { Tagging, TaggingPosition } from '@/store';
-
-export type SignProps = { tagging: Tagging, scenePos: TaggingPosition, show?: boolean }
-
-defineEmits<{ (e: 'delete'): void }>()
-
-const props = defineProps<SignProps>()
-const posStyle = ref<null | { left: string, top: string}>(null)
+import { computed, ref, watchEffect, watch, onUnmounted } from "vue";
+import { router, RoutesName } from "@/router";
+import UIBubble from "bill/components/bubble/index.vue";
+import Images from "@/views/tagging/images.vue";
+import Preview, { MediaType } from "../static-preview/index.vue";
+import { getTaggingStyle, getFuseModel } from "@/store";
+import { getFileUrl } from "@/utils";
+import { sdk } from "@/sdk";
+import { custom, getResource } from "@/env";
+import { useViewStack } from "@/hook";
+
+import type { Tagging, TaggingPosition } from "@/store";
+import { getTypeByUrl } from "@/utils/type";
+
+export type SignProps = { tagging: Tagging; scenePos: TaggingPosition; show?: boolean };
+
+defineEmits<{ (e: "delete"): void }>();
+
+const props = defineProps<SignProps>();
+const posStyle = ref<null | { left: string; top: string }>(null);
 const updatePosStyle = () => {
-  const screenPos = sdk.getScreenByPosition(props.scenePos.localPos, props.scenePos.modelId)
+  const screenPos = sdk.getScreenByPosition(
+    props.scenePos.localPos,
+    props.scenePos.modelId
+  );
   if (!screenPos?.trueSide) {
-    posStyle.value = null
+    posStyle.value = null;
   } else {
     posStyle.value = {
-      left: screenPos.pos.x + 'px',
-      top: screenPos.pos.y + 'px',
-    }
+      left: screenPos.pos.x + "px",
+      top: screenPos.pos.y + "px",
+    };
   }
-}
-
+};
 
 useViewStack(() => {
-  sdk.sceneBus.on('cameraChange', updatePosStyle)
+  sdk.sceneBus.on("cameraChange", updatePosStyle);
   return () => {
-    sdk.sceneBus.off('cameraChange', updatePosStyle)
-  }
-})
-watchEffect(updatePosStyle)
-
-const model = getFuseModel(props.scenePos.modelId)
-model && watch(model, updatePosStyle, { deep: true })
+    sdk.sceneBus.off("cameraChange", updatePosStyle);
+  };
+});
+watchEffect(updatePosStyle);
 
+const model = getFuseModel(props.scenePos.modelId);
+model && watch(model, updatePosStyle, { deep: true });
 
 const showContent = computed(() => {
-  return !~pullIndex.value 
-    && (isHover.value || custom.showTaggingPositions.has(props.scenePos))
-})
-
-const taggingStyle = computed(() => getTaggingStyle(props.tagging.styleId)) 
-
-const pullIndex = ref(-1)
-const isHover = ref(false)
-const queryItems = computed(() => 
-  props.tagging.images.map(image => ({
-    type: MediaType.img, 
-    url: getResource(getFileUrl(image))
-  }))
-)
+  return (
+    !~pullIndex.value &&
+    (isHover.value || custom.showTaggingPositions.has(props.scenePos))
+  );
+});
+
+const taggingStyle = computed(() => getTaggingStyle(props.tagging.styleId));
+
+const pullIndex = ref(-1);
+const isHover = ref(false);
+const queryItems = computed(() =>
+  props.tagging.images.map((image) => {
+    const tagType = getTypeByUrl(getResource(getFileUrl(image)));
+    return {
+      type: tagType === "img" ? MediaType.img : MediaType.video,
+      url: getResource(getFileUrl(image)),
+    };
+  })
+);
 
 const iconClickHandler = () => {
   if (custom.showTaggingPositions.has(props.scenePos)) {
-    custom.showTaggingPositions.delete(props.scenePos)
+    custom.showTaggingPositions.delete(props.scenePos);
   } else {
-    custom.showTaggingPositions.add(props.scenePos)
+    custom.showTaggingPositions.add(props.scenePos);
   }
-}
-
+};
 </script>
 
 <style lang="scss" scoped>
@@ -149,7 +153,7 @@ const iconClickHandler = () => {
     h2 {
       font-size: 20px;
       margin-bottom: 10px;
-      color: #FFFFFF;
+      color: #ffffff;
       position: relative;
     }
 
@@ -183,7 +187,6 @@ const iconClickHandler = () => {
     cursor: pointer;
   }
 }
-
 </style>
 
 <style>
@@ -194,4 +197,4 @@ const iconClickHandler = () => {
   padding: 6px 10px !important;
   margin: 5px 0 !important;
 }
-</style>
+</style>

+ 7 - 2
src/env/index.ts

@@ -79,10 +79,15 @@ export type Params = {
   token?: string
 }
 
-export const baseURL = params.baseURL ? params.baseURL : '/'
+export const baseURL = import.meta.env.DEV ? '/res' : ''
 
 
 export const getResource = (uri: string) => {
   if (~uri.indexOf('base64') || ~uri.indexOf('bolb') || ~uri.indexOf('//')) return uri
-  return `${baseURL}/${uri}`
+
+  if (uri[0] === '/') {
+    return `${baseURL}${uri}`
+  } else {
+    return `${baseURL}/${uri}`
+  }
 }

+ 4 - 3
src/model/platform.ts

@@ -27,7 +27,7 @@ export async function modelSDKFactory(
   let center: number[] | undefined = undefined;
 
   if (caseProject.value) {
-    const lonlatStr = caseProject.value?.latAndLong || "22.364093,113.600356";
+    const lonlatStr = caseProject.value?.latAndLong || "22.27545304080856, 113.53817378515573";
     const lonlat = lonlatStr.split(",").map(Number);
     const gcenter = aMapToWgs84({
       x: lonlat[1],
@@ -39,8 +39,9 @@ export async function modelSDKFactory(
     if (!fuseInitialed) {
       await initialSDK({
         layout: dom,
-        panoBasePath: '',
-        cloudBasePath: '',
+        panoBasePath: import.meta.env.DEV ? '/res' : './',
+        cloudBasePath: import.meta.env.DEV ? '/res' : './',
+        // https://uat-laser.4dkankan.com/laser/dataset/SG-t-WReTBN204dQ/getDataSet
         scenes: scenes.value,
         lonlat: center,
       });

+ 2 - 0
src/utils/index.ts

@@ -1,3 +1,5 @@
+import { getResource } from '@/env';
+
 // 加载第三方库
 export const loadLib = (() => {
   const cache: Record<string, Promise<void>> = {};

+ 9 - 0
src/utils/type.ts

@@ -0,0 +1,9 @@
+export const getTypeByUrl = (url: string) => {
+  if (url.includes('data:video') || url.includes('.mp4'))  {
+    return 'video'
+  } else if (url.includes('data:audio') || url.includes('.mp3'))  {
+    return 'audio'
+  } else {
+    return 'img'
+  }
+}

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

@@ -45,7 +45,7 @@ const emit = defineEmits<{
 
 const menus = [
   { label: "编辑", value: "edit" },
-  { label: "下载", value: "download" },
+  // { label: "下载", value: "download" },
   { label: "删除", value: "delete" },
 ];
 const actions = {

+ 98 - 85
src/views/record/sign.vue

@@ -1,23 +1,26 @@
 <template>
-  <ui-group-option class=" record-sign" :class="{sign: record.status === RecordStatus.SUCCESS}">
+  <ui-group-option
+    class="record-sign"
+    :class="{ sign: record.status === RecordStatus.SUCCESS }"
+  >
     <div class="content">
       <span class="cover">
-        <img :src="getResource(getFileUrl(record.cover))" alt="" v-if="record.cover">
-        <ui-icon 
-          type="preview" 
-          ctrl 
-          class="preview" 
-          @click="actions.play()"  
+        <img :src="getResource(getFileUrl(record.cover))" alt="" v-if="record.cover" />
+        <ui-icon
+          type="preview"
+          ctrl
+          class="preview"
+          @click="actions.play()"
           v-if="record.status === RecordStatus.SUCCESS"
         />
       </span>
-      <ui-input 
-        type="text" 
-        :modelValue="record.title" 
+      <ui-input
+        type="text"
+        :modelValue="record.title"
         @update:modelValue="(title: string) => $emit('updateTitle', title.trim())"
-        v-show="isEditTitle" 
-        ref="inputRef" 
-        height="28px" 
+        v-show="isEditTitle"
+        ref="inputRef"
+        height="28px"
         :maxlength="15"
       />
       <div class="title" v-show="!isEditTitle">
@@ -27,112 +30,124 @@
     </div>
     <div class="action" v-if="edit && record.status === RecordStatus.SUCCESS">
       <ui-icon type="order" ctrl />
-      <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]()"
       />
     </div>
 
-    <Shot 
-      v-if="isShot" 
+    <Shot
+      v-if="isShot"
       @close="closeHandler"
-      @append="appendFragment" 
-      @updateCover="(cover: string) => $emit('updateCover', cover)" 
+      @append="appendFragment"
+      @updateCover="(cover: string) => $emit('updateCover', cover)"
       @deleteRecord="$emit('delete')"
-      :record="record" />
-    <Preview 
-      v-if="isPlayVideo" 
+      :record="record"
+    />
+    <Preview
+      v-if="isPlayVideo"
       :items="[{ type: MediaType.video, url: record.url! }]"
-      @close="isPlayVideo = false" 
+      @close="isPlayVideo = false"
     />
   </ui-group-option>
 </template>
 
 <script lang="ts">
-import type {PropType} from 'vue'
-import {computed, defineComponent, ref, watchEffect} from 'vue'
-import {getExtname, getFileUrl, loadPack, saveAs} from '@/utils'
-import {useFocus} from 'bill/hook/useFocus'
-import {createRecordFragment, getRecordFragmentBlobs, isTemploraryID, recordFragments, RecordStatus} from '@/store'
-import {MediaType, Preview} from '@/components/static-preview/index.vue'
-import {getResource} from '@/env'
-import Shot from './shot.vue'
-import type {RecordProcess} from './help'
-import {Message} from 'bill/index'
+import type { PropType } from "vue";
+import { computed, defineComponent, ref, watchEffect } from "vue";
+import { getExtname, getFileUrl, loadPack, saveAs } from "@/utils";
+import { useFocus } from "bill/hook/useFocus";
+import {
+  createRecordFragment,
+  getRecordFragmentBlobs,
+  isTemploraryID,
+  recordFragments,
+  RecordStatus,
+} from "@/store";
+import { MediaType, Preview } from "@/components/static-preview/index.vue";
+import { getResource } from "@/env";
+import Shot from "./shot.vue";
+import type { RecordProcess } from "./help";
+import { Message } from "bill/index";
 
 export default defineComponent({
   props: {
     record: {
       type: Object as PropType<RecordProcess>,
-      required: true
+      required: true,
     },
     edit: {
       type: Boolean,
       required: false,
-      default: true
-    }
+      default: true,
+    },
   },
   emits: {
-    'updateCover': (cover: string) => true,
-    'updateTitle': (title: string) => true,
-    'delete': () => true
+    updateCover: (cover: string) => true,
+    updateTitle: (title: string) => true,
+    delete: () => true,
   },
   setup(props, { emit }) {
     const menus = computed(() => {
-      const base = []
+      const base = [];
       if ([RecordStatus.SUCCESS, RecordStatus.UN].includes(props.record.status)) {
         base.push(
-          { label: '重命名', value: 'rename' },
-          { label: '继续录制', value: 'continue' },
-        )
+          { label: "重命名", value: "rename" },
+          { label: "继续录制", value: "continue" }
+        );
 
         if (props.record.status === RecordStatus.SUCCESS) {
-          base.push({ label: '下载', value: 'download' },)
+          // base.push({ label: '下载', value: 'download' },)
         }
       }
-      base.push({ label: '删除', value: 'delete' })
-      return base
-    })
+      base.push({ label: "删除", value: "delete" });
+      return base;
+    });
 
-    const isShot = ref<boolean>(false)
-    const inputRef = ref()
-    const isEditTitle = useFocus(computed(() => inputRef.value?.vmRef.root))
+    const isShot = ref<boolean>(false);
+    const inputRef = ref();
+    const isEditTitle = useFocus(computed(() => inputRef.value?.vmRef.root));
 
     watchEffect(() => {
       if (!isEditTitle.value && !props.record.title.length) {
-        isEditTitle.value = true
-        Message.warning('视频名称不可为空')
+        isEditTitle.value = true;
+        Message.warning("视频名称不可为空");
       }
-    })
+    });
 
-    const isPlayVideo = ref(false)
+    const isPlayVideo = ref(false);
     const actions = {
-      continue: () => isShot.value = true,
-      delete: () => emit('delete'),
-      rename: () => isEditTitle.value = true,
-      play: () => isPlayVideo.value = true,
+      continue: () => (isShot.value = true),
+      delete: () => emit("delete"),
+      rename: () => (isEditTitle.value = true),
+      play: () => (isPlayVideo.value = true),
       download() {
-        const url = getResource(props.record.url!)
-        const ext = getExtname(url) || 'mp4'
-        loadPack(saveAs(url, `${props.record.title}.${ext}`))
+        const url = getResource(props.record.url!);
+        const ext = getExtname(url) || "mp4";
+        loadPack(saveAs(url, `${props.record.title}.${ext}`));
       },
-    }
-    props.record.immediately && actions.continue()
+    };
+    props.record.immediately && actions.continue();
 
     const closeHandler = () => {
-      if (getRecordFragmentBlobs(props.record).length === 0 && isTemploraryID(props.record.id)) {
-        emit('delete')
+      if (
+        getRecordFragmentBlobs(props.record).length === 0 &&
+        isTemploraryID(props.record.id)
+      ) {
+        emit("delete");
       }
-      isShot.value = false
-    }
+      isShot.value = false;
+    };
 
     const appendFragment = (blobs: Blob[]) => {
       recordFragments.value.push(
-        ...blobs.map(blob => createRecordFragment({ url: blob, recordId: props.record.id }))
-      )
-      props.record.status = RecordStatus.UN
-    }
+        ...blobs.map((blob) =>
+          createRecordFragment({ url: blob, recordId: props.record.id })
+        )
+      );
+      props.record.status = RecordStatus.UN;
+    };
 
     return {
       menus,
@@ -146,22 +161,20 @@ export default defineComponent({
       isPlayVideo,
       getResource,
       getFileUrl,
-      appendFragment
-    }
+      appendFragment,
+    };
   },
   components: {
     Shot,
-    Preview
-  }
-})
+    Preview,
+  },
+});
 </script>
 
-
-<style lang="scss" src="./style.scss" scoped>
-</style>
+<style lang="scss" src="./style.scss" scoped></style>
 
 <style>
-  .record-sign .ui-input .text.suffix input {
-    padding-right: 60px;
-  }
-</style>
+.record-sign .ui-input .text.suffix input {
+  padding-right: 60px;
+}
+</style>

+ 2 - 2
src/views/setting/index.vue

@@ -3,7 +3,7 @@
     <ui-group title="初始画面" borderBottom>
       <ui-group-option>
         <div class="init-pic" :class="{ disabled: isEdit }">
-          <img :src="getFileUrl(setting!.cover)" class="init-puc-cover" />
+          <img :src="getResource(getFileUrl(setting!.cover))" class="init-puc-cover" />
           <div class="init-pic-set" @click="enterSetPic">设置</div>
         </div>
       </ui-group-option>
@@ -35,7 +35,7 @@ import { RightFillPano } from "@/layout";
 import { enterEdit, enterOld, setting, isEdit, updataSetting } from "@/store";
 import { ref, watchEffect } from "vue";
 import { togetherCallback, getFileUrl, loadPack } from "@/utils";
-import { showRightPanoStack, showRightCtrlPanoStack, custom } from "@/env";
+import { showRightPanoStack, showRightCtrlPanoStack, custom, getResource } from "@/env";
 import { analysisPose, sdk, SettingResourceType } from "@/sdk";
 
 const backs = ref<{ label: string; type: string; image: string; value: string }[]>([]);

+ 4 - 4
src/views/tagging/edit.vue

@@ -66,12 +66,12 @@
         width="100%"
         height="225px"
         preview
-        placeholder="上传图片"
-        othPlaceholder="支持JPG、PNG图片格式,单张不超过5MB,最多支持上传9张。"
-        accept=".jpg, .png"
+        placeholder="上传图片、视频、音频"
+        othPlaceholder="支持.jpg, .png, .mp4, .mp3等格式,单个最大1G,最多支持上传9个。"
+        accept=".jpg, .png, .mp4, .mp3"
         :disable="true"
         :multiple="true"
-        :maxSize="5 * 1024 * 1024"
+        :maxSize="1024 * 1024 * 1024"
         :maxLen="9"
         :modelValue="tagging.images"
         @update:modelValue="fileChange"

+ 44 - 29
src/views/tagging/images.vue

@@ -1,20 +1,33 @@
 <template>
   <div class="mates">
-    <ui-slide 
-      v-if="tagging" 
-      :items="tagging.images" 
-      :showCtrl="tagging.images.length > 1" 
+    <ui-slide
+      v-if="tagging"
+      :items="tagging.images"
+      :showCtrl="tagging.images.length > 1"
       :currentIndex="index"
-      @change="(i: number) => index = i" 
+      @change="(i: number) => index = i"
       :showInfos="tagging.images.length > 1 && !hideInfo"
     >
       <template v-slot="{ raw, index }">
-        <div 
-          class="meta-item" 
-          :class="{ full: inFull }" 
+        <div
+          class="meta-item"
+          :class="{ full: inFull }"
           @click="inFull && $emit('pull', index)"
         >
-          <img :src="getResource(getFileUrl(raw))" />
+          <img
+            :src="getResource(getFileUrl(raw))"
+            v-if="getTypeByUrl(getResource(getFileUrl(raw))) === 'img'"
+          />
+          <audio
+            :src="getResource(getFileUrl(raw))"
+            v-else-if="getTypeByUrl(getResource(getFileUrl(raw))) === 'audio'"
+            controls
+          />
+          <video
+            :src="getResource(getFileUrl(raw))"
+            v-else-if="getTypeByUrl(getResource(getFileUrl(raw))) === 'video'"
+            controls
+          />
         </div>
       </template>
       <template v-slot:attach="{ active }">
@@ -27,27 +40,29 @@
 </template>
 
 <script lang="ts" setup>
-import { ref } from 'vue'
-import { getFileUrl } from '@/utils'
-import { Tagging } from '@/store'
-import { getResource } from '@/env'
+import { computed, ref, watchEffect } from "vue";
+import { getFileUrl } from "@/utils";
+import { Tagging } from "@/store";
+import { getResource } from "@/env";
+import { getTypeByUrl } from "@/utils/type";
 
 export type ImagesProps = {
-  tagging: Tagging
-  inFull?: boolean
-  hideInfo?: boolean
-}
+  tagging: Tagging;
+  inFull?: boolean;
+  hideInfo?: boolean;
+};
 
-defineProps<ImagesProps>()
+const props = defineProps<ImagesProps>();
 defineEmits<{
-    (e: 'pull', index: number): void
-    (e: 'change', i: number): void
-}>()
-const index = ref(0)
+  (e: "pull", index: number): void;
+  (e: "change", i: number): void;
+}>();
+const index = ref(0);
+
+watchEffect(() => console.log(props.tagging.images));
 </script>
 
 <style lang="scss" scoped>
-
 .mates {
   width: 100%;
   height: 100%;
@@ -69,7 +84,7 @@ const index = ref(0)
     position: relative;
 
     &::after {
-      content: '';
+      content: "";
       position: absolute;
       bottom: 0;
       top: 0;
@@ -81,6 +96,7 @@ const index = ref(0)
 
   iframe,
   video,
+  audio,
   img {
     width: 100%;
     height: 203px;
@@ -90,7 +106,7 @@ const index = ref(0)
   img {
     object-fit: contain;
   }
-  iframe{
+  iframe {
     border: none;
   }
 
@@ -102,9 +118,9 @@ const index = ref(0)
       display: block;
       width: 24px;
       height: 24px;
-      background-color: rgba(0,0,0,0.3);
+      background-color: rgba(0, 0, 0, 0.3);
       font-size: 14px;
-      color: rgba(255,255,255,0.6);
+      color: rgba(255, 255, 255, 0.6);
       border-radius: 50%;
       display: flex;
       align-items: center;
@@ -117,5 +133,4 @@ const index = ref(0)
     }
   }
 }
-
-</style>
+</style>

+ 74 - 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,95 @@
 </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 } 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";
+import { getTypeByUrl } from "@/utils/type";
 
 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 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) => getTypeByUrl(getResource(getFileUrl(a))) === "img"
+  );
+});
+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 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 - 1
src/views/tagging/styles.vue

@@ -22,7 +22,7 @@
       @click="clickHandler(hotStyle)"
     >
       <span>
-        <img :src="getFileUrl(hotStyle.icon)" />
+        <img :src="getResource(getFileUrl(hotStyle.icon))" />
         <ui-icon
           v-if="!hotStyle.default"
           class="delete"
@@ -64,6 +64,7 @@ import { createTaggingStyle } from "@/store";
 import { ref, computed, defineEmits } from "vue";
 import { Cropper } from "bill/index";
 import { getFileUrl } from "@/utils";
+import { getResource } from "@/env";
 
 const props = defineProps<{
   styles: TaggingStyles;

+ 10 - 1
vite.config.ts

@@ -17,10 +17,19 @@ const proxy = {
     rewrite: path => path.replace(/^\/local/, '')
   },
   '/fusion': {
-    target: config.dev ? 'https://test-mix3d.4dkankan.com' : 'https://mix3d.4dkankan.com',
+    target: config.dev ? 'http://192.168.0.140:8808/' : 'https://mix3d.4dkankan.com',
     changeOrigin: true,
     rewrite: path => path.replace(/^\/api/, '')
   },
+  '/res': {
+    target: config.dev ? 'http://192.168.0.140' : 'https://mix3d.4dkankan.com',
+    changeOrigin: true,
+    rewrite: path => path.replace(/^\/res/, '')
+  },
+  '/api': {
+    target: config.dev ? 'http://192.168.0.140' : 'https://mix3d.4dkankan.com',
+    changeOrigin: true,
+  },
   '/swkk': {
     target: config.dev ? 'https://test.4dkankan.com' : 'https://www.4dkankan.com',
     changeOrigin: true,