bill 1 ano atrás
pai
commit
a57d46502a

+ 190 - 164
src/components/bill-ui/components/input/file.vue

@@ -1,194 +1,220 @@
 <template>
-    <div class="input file" :class="{ suffix: $slots.icon, disabled: disabled, valuable }">
-        <template v-if="valuable">
-            <slot name="valuable" :key="modelValue" />
-        </template>
-        <input class="ui-text" type="file" ref="inputRef" :accept="accept" :multiple="multiple" @change="selectFileHandler" v-if="!maxLen || maxLen > modelValue.length" />
-        <template v-if="!$slots.replace">
-            <span class="replace">
-                <div class="placeholder" v-if="!valuable">
-                    <p><ui-icon type="add" /></p>
-                    <p>{{ placeholder }}</p>
-                    <p class="bottom">
-                        <template v-if="!othPlaceholder">
-                            <template v-if="accept">支持 {{ accept }} 等格式,</template>
-                            <template v-if="normalizeScale">宽*高比例 {{ scale }},</template>
-                            <template v-if="maxSize">大小不超过 {{ sizeStr }}{{ maxLen ? ',' : '' }}</template>
-                            <template v-if="maxLen">个数不超过 {{ maxLen }}个</template>
-                        </template>
-                        <template v-else>
-                            {{ othPlaceholder }}
-                        </template>
-                    </p>
-                </div>
-
-                <span v-else v-if="!maxLen || maxLen > modelValue.length">
-                    {{ multiple ? '继续添加' : '替换' }}
-                </span>
-                <span class="tj" v-if="maxLen && modelValue.length">
-                    <span>{{ modelValue.length || 0 }}</span> / {{ maxLen }}
-                </span>
-            </span>
-        </template>
-        <div class="use-replace" v-else>
-            <slot name="replace" />
+  <div class="input file" :class="{ suffix: $slots.icon, disabled: disabled, valuable }">
+    <template v-if="valuable">
+      <slot name="valuable" :key="modelValue" />
+    </template>
+    <input
+      class="ui-text"
+      type="file"
+      ref="inputRef"
+      :accept="accept"
+      :multiple="multiple"
+      @change="selectFileHandler"
+      v-if="!maxLen || maxLen > modelValue.length"
+    />
+    <template v-if="!$slots.replace">
+      <span class="replace">
+        <div class="placeholder" v-if="!valuable">
+          <p><ui-icon type="add" /></p>
+          <p>{{ placeholder }}</p>
+          <p class="bottom">
+            <template v-if="!othPlaceholder">
+              <template v-if="accept">支持 {{ accept }} 等格式,</template>
+              <template v-if="normalizeScale">宽*高比例 {{ scale }},</template>
+              <template v-if="maxSize"
+                >大小不超过 {{ sizeStr }}{{ maxLen ? "," : "" }}</template
+              >
+              <template v-if="maxLen">个数不超过 {{ maxLen }}个</template>
+            </template>
+            <template v-else>
+              {{ othPlaceholder }}
+            </template>
+          </p>
         </div>
+
+        <span v-else v-if="!maxLen || maxLen > modelValue.length">
+          {{ multiple ? "继续添加" : "替换" }}
+        </span>
+        <span class="tj" v-if="maxLen && modelValue.length">
+          <span>{{ modelValue.length || 0 }}</span> / {{ maxLen }}
+        </span>
+      </span>
+    </template>
+    <div class="use-replace" v-else>
+      <slot name="replace" />
     </div>
+  </div>
 </template>
 
 <script setup>
-import { filePropsDesc } from './state'
-import { toRawType } from '../../utils'
-import Message from '../message'
-import { ref, computed } from 'vue'
+import { filePropsDesc } from "./state";
+import { toRawType } from "../../utils";
+import Message from "../message";
+import { ref, computed } from "vue";
 
 const props = defineProps({
-    ...filePropsDesc,
-})
-const emit = defineEmits(['update:modelValue'])
-const inputRef = ref(null)
+  ...filePropsDesc,
+});
+const emit = defineEmits(["update:modelValue"]);
+const inputRef = ref(null);
 const normalizeScale = computed(() => {
-    if (props.scale) {
-        const [w, h] = props.scale.split(':')
-        if (Number(w) && Number(h)) {
-            return [Number(w), Number(h)]
-        }
+  if (props.scale) {
+    const [w, h] = props.scale.split(":");
+    if (Number(w) && Number(h)) {
+      return [Number(w), Number(h)];
     }
-})
+  }
+});
 
-const valuable = computed(() => (Array.isArray(props.modelValue) ? props.modelValue.length : !!props.modelValue))
+const valuable = computed(() =>
+  Array.isArray(props.modelValue) ? props.modelValue.length : !!props.modelValue
+);
 const sizeStr = computed(() => {
-    if (props.maxSize) {
-        const mb = props.maxSize / 1024 / 1024
-        if (mb > 1024) {
-            return mb / 1024 + 'GB'
-        } else {
-            return mb + 'MB'
-        }
+  if (props.maxSize) {
+    const mb = props.maxSize / 1024 / 1024;
+    if (mb > 1024) {
+      return mb / 1024 + "GB";
+    } else {
+      return mb + "MB";
     }
-})
+  }
+});
 
 const supports = {
-    image: {
-        types: ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp'],
-        preview(file, url) {
-            return new Promise((resolve, reject) => {
-                const img = new Image()
-                img.onload = () => resolve([img.width, img.height, file])
-                img.onerror = reject
-                img.src = url
-            })
-        },
+  image: {
+    types: ["image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp"],
+    preview(file, url) {
+      return new Promise((resolve, reject) => {
+        const img = new Image();
+        img.onload = () => resolve([img.width, img.height, file]);
+        img.onerror = reject;
+        img.src = url;
+      });
     },
-    video: {
-        types: ['video/mp4'],
-        preview(file, url) {
-            return new Promise((resolve, reject) => {
-                const video = document.createElement('video')
-                video.preload = 'metadata'
-                video.onloadedmetadata = () => resolve([video.videoWidth, video.videoHeight, file])
-                video.onerror = reject
-                video.src = url
-            })
-        },
+  },
+  video: {
+    types: ["video/mp4"],
+    preview(file, url) {
+      return new Promise((resolve, reject) => {
+        const video = document.createElement("video");
+        video.preload = "metadata";
+        video.onloadedmetadata = () =>
+          resolve([video.videoWidth, video.videoHeight, file]);
+        video.onerror = reject;
+        video.src = url;
+      });
     },
-}
-
-const producePreviews = files =>
-    Promise.all(
-        files.map(
-            file =>
-                new Promise((resolve, reject) => {
-                    const fr = new FileReader()
-                    fr.onloadend = e => resolve(e.target.result)
-                    fr.onerror = e => loaderror(file, reject(e))
-                    fr.readAsDataURL(file)
-                })
-        )
+  },
+};
+
+const producePreviews = (files) =>
+  Promise.all(
+    files.map(
+      (file) =>
+        new Promise((resolve, reject) => {
+          resolve(URL.createObjectURL(file));
+          return;
+          const fr = new FileReader();
+          fr.onloadend = (e) => resolve(e.target.result);
+          fr.onerror = (e) => loaderror(file, reject(e));
+
+          fr.readAsDataURL(file);
+        })
     )
-
-const calcScale = (w, h) => parseInt((w / h) * 1000)
-
-const selectFileHandler = async ev => {
-    const fileEl = ev.target
-    const files = Array.from(fileEl.files)
-    const previewError = (e, msg = `预览加载失败!`) => {
-        console.error(e)
-        Message.error(msg)
-        fileEl.value = ''
+  );
+
+const calcScale = (w, h) => parseInt((w / h) * 1000);
+
+const selectFileHandler = async (ev) => {
+  const fileEl = ev.target;
+  const files = Array.from(fileEl.files);
+  const previewError = (e, msg = `预览加载失败!`) => {
+    console.error(e);
+    Message.error(msg);
+    fileEl.value = "";
+  };
+
+  if (props.accept) {
+    for (const file of files) {
+      const accepts = props.accept.split(",").map((atom) => atom.trim().toUpperCase());
+      const hname = file.name.substr(file.name.lastIndexOf(".")).toUpperCase();
+      if (!accepts.includes(hname)) {
+        return previewError("格式错误", `仅支持${props.accept}格式文件`);
+      }
     }
-
-    if (props.accept) {
-        for (const file of files) {
-            const accepts = props.accept.split(',').map(atom => atom.trim().toUpperCase())
-            const hname = file.name.substr(file.name.lastIndexOf('.')).toUpperCase()
-            if (!accepts.includes(hname)) {
-                return previewError('格式错误', `仅支持${props.accept}格式文件`)
-            }
-        }
+  }
+
+  let previews;
+  if (props.preview || normalizeScale.value) {
+    try {
+      previews = await producePreviews(files);
+    } catch (e) {
+      return previewError(e);
     }
-
-    let previews
-    if (props.preview || normalizeScale.value) {
-        try {
-            previews = await producePreviews(files)
-        } catch (e) {
-            return previewError(e)
-        }
+  }
+
+  if (normalizeScale.value) {
+    const sizesConfirm = [];
+    for (let i = 0; i < files.length; i++) {
+      const support = Object.values(supports).find((support) =>
+        support.types.includes(files[i].type)
+      );
+      if (support) {
+        sizesConfirm.push(support.preview(files[i], previews[i]));
+      }
     }
 
-    if (normalizeScale.value) {
-        const sizesConfirm = []
-        for (let i = 0; i < files.length; i++) {
-            const support = Object.values(supports).find(support => support.types.includes(files[i].type))
-            if (support) {
-                sizesConfirm.push(support.preview(files[i], previews[i]))
-            }
-        }
-
-        let sizes
-        try {
-            sizes = await Promise.all(sizesConfirm)
-        } catch (e) {
-            return previewError(e)
-        }
-
-        for (const [w, h, file] of sizes) {
-            const scaleDiff = calcScale(...normalizeScale.value) - calcScale(w, h)
-
-            if (Math.abs(scaleDiff) > 300) {
-                return previewError('error scale', `${file.name}的比例部位不为${props.scale}`)
-            }
-        }
+    let sizes;
+    try {
+      sizes = await Promise.all(sizesConfirm);
+    } catch (e) {
+      return previewError(e);
     }
 
-    if (props.maxSize) {
-        for (const file of files) {
-            if (file.size > props.maxSize) {
-                return previewError('error size', `${file.name}的大小超过${sizeStr.value}`)
-            }
-        }
-    }
-
-    const value = props.modelValue ? (props.multiple ? (toRawType(props.modelValue) === 'Array' ? props.modelValue : [props.modelValue]) : null) : props.multiple ? [] : null
+    for (const [w, h, file] of sizes) {
+      const scaleDiff = calcScale(...normalizeScale.value) - calcScale(w, h);
 
-    const emitData = props.multiple
-        ? props.preview
-            ? [...value, ...files.map((file, i) => ({ file, preview: previews[i] }))]
-            : [...value, files]
-        : props.preview
-        ? { file: files[0], preview: previews[0] }
-        : files[0]
-
-    if (Array.isArray(emitData) && props.maxLen && emitData.length > props.maxLen) {
-        return previewError('err len', `最多仅支持${props.maxLen}个文件!`)
+      if (Math.abs(scaleDiff) > 300) {
+        return previewError("error scale", `${file.name}的比例部位不为${props.scale}`);
+      }
     }
+  }
 
-    emit('update:modelValue', emitData)
-    fileEl.value = ''
-}
+  if (props.maxSize) {
+    for (const file of files) {
+      if (file.size > props.maxSize) {
+        return previewError("error size", `${file.name}的大小超过${sizeStr.value}`);
+      }
+    }
+  }
+
+  const value = props.modelValue
+    ? props.multiple
+      ? toRawType(props.modelValue) === "Array"
+        ? props.modelValue
+        : [props.modelValue]
+      : null
+    : props.multiple
+    ? []
+    : null;
+
+  const emitData = props.multiple
+    ? props.preview
+      ? [...value, ...files.map((file, i) => ({ file, preview: previews[i] }))]
+      : [...value, files]
+    : props.preview
+    ? { file: files[0], preview: previews[0] }
+    : files[0];
+
+  if (Array.isArray(emitData) && props.maxLen && emitData.length > props.maxLen) {
+    return previewError("err len", `最多仅支持${props.maxLen}个文件!`);
+  }
+
+  emit("update:modelValue", emitData);
+  fileEl.value = "";
+};
 
 defineExpose({
-    input: inputRef,
-})
+  input: inputRef,
+});
 </script>

+ 11 - 1
src/components/tagging/metas/metas-mange.vue

@@ -16,7 +16,7 @@
         >
           <img :src="raw" v-if="data.type === 'IMAGE'" />
           <video
-            v-else-if="data.type === 'VIDEO' || data.type === 'AUDIO'"
+            v-else-if="data.type === 'VIDEO'"
             class="video"
             autoplay
             controls
@@ -24,6 +24,16 @@
             webkit-playsinline
             :src="raw"
           ></video>
+          <audio
+            v-else-if="data.type === 'AUDIO'"
+            class="audeo"
+            autoplay
+            controls
+            playsinline
+            webkit-playsinline
+            :src="raw"
+          ></audio>
+
           <div class="iframe" v-else-if="data.type === 'WEB'">
             <iframe :src="raw"> </iframe>
           </div>

+ 2 - 0
src/components/tagging/metas/style.scss

@@ -177,11 +177,13 @@
 
   iframe,
   video,
+  audio,
   img {
     width: 100%;
     height: 203px;
     object-fit: cover;
   }
+  audio,
   video,
   img {
     object-fit: cover;

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

@@ -55,8 +55,9 @@
 <script lang="ts" setup>
 import { Modal, Input, Table, Empty } from "ant-design-vue";
 import { computed, nextTick, ref, watch, watchEffect } from "vue";
-import { scenes, save, SceneTypeDesc } from "@/store";
-import { createLoadPack } from "@/utils";
+import { scenes, save, SceneTypeDesc, getFuseModel } from "@/store";
+import { asyncTimeout, createLoadPack } from "@/utils";
+import { getSceneModel } from "@/sdk";
 import {
   fuseModels,
   createFuseModels,
@@ -121,10 +122,10 @@ const cloumns = [
 ];
 
 const okHandler = createLoadPack(async () => {
-  const addPromises = selects.value
+  const models = selects.value
     .filter((modelId) => !fuseModels.value.some((model) => model.modelId === modelId))
-    .map((modelId) => createFuseModels({ modelId }))
-    .map(addFuseModel);
+    .map((modelId) => createFuseModels({ modelId }));
+  const addPromises = models.map(addFuseModel);
 
   await Promise.all(addPromises);
   await new Promise<void>((resolve) => {
@@ -139,6 +140,12 @@ const okHandler = createLoadPack(async () => {
       });
     });
   });
+  models.forEach((model) => {
+    if (getSceneModel(model)) {
+      model.rotation = getSceneModel(model)!.getDefaultRotation();
+    }
+  });
+  await asyncTimeout(100);
   await save();
   visible.value = false;
 });

+ 0 - 1
src/sdk/association.ts

@@ -4,7 +4,6 @@ import {
   viewModeStack, 
   showLeftPanoStack, 
   custom, 
-  getResource 
 } from '@/env'
 import { 
   mount,