bill 8 mēneši atpakaļ
vecāks
revīzija
217bfe13fb

+ 2 - 0
src/api/constant.ts

@@ -99,4 +99,6 @@ export const UPLOAD_FILE = `/fusion/upload/file`
 
 // 素材库分页
 export const MATERIAL_PAG = `/fusion/material/allList`
+export const ADD_MATERIAL = `/fusion/material/add`
+export const DEL_MATERIAL = `/fusion/material/del`
 export const MATERIAL_GROUP_LIST = `/fusion/material/allList`

+ 16 - 2
src/api/material.ts

@@ -1,6 +1,6 @@
-import { asyncTimeout } from "@/utils";
+import { asyncTimeout, jsonToForm } from "@/utils";
 import { PagingRequest, PagingResult } from ".";
-import { MATERIAL_GROUP_LIST, MATERIAL_PAG } from "./constant";
+import { ADD_MATERIAL, DEL_MATERIAL, MATERIAL_GROUP_LIST, MATERIAL_PAG, UPLOAD_HEADS } from "./constant";
 import axios from "./instance";
 
 export type MaterialGroup = {
@@ -82,3 +82,17 @@ export const fetchMaterialGroups = async () => {
   return groups
   // return axios.get<MaterialGroup[]>(MATERIAL_GROUP_LIST);
 };
+
+export const addMaterial = (file: File) => {
+  console.log(file)
+  return axios<string>({
+    method: "POST",
+    url: ADD_MATERIAL,
+    data: jsonToForm({ file }),
+    headers: { ...UPLOAD_HEADS },
+  });
+}
+
+export const delMaterial = (id: Material['id']) => {
+  return axios.post(DEL_MATERIAL, { id })
+}

+ 79 - 23
src/components/materials/index.vue

@@ -15,14 +15,31 @@
         <p class="header-desc">
           已选择数据<span>( {{ rowSelection.selectedRowKeys.length }} )</span>
         </p>
-        <Search
-          v-if="Object.keys(allData).length"
-          className="content-header-search"
-          placeholder="输入名称搜索"
-          v-model:value="params.name"
-          allow-clear
-          style="width: 244px"
-        />
+        <div class="up-se">
+          <span class="upload fun-ctrls">
+            <ui-input
+              class="input"
+              :accept="ft"
+              :maxSize="maxSize"
+              @update:modelValue="addMaterial"
+              type="file"
+            >
+              <template v-slot:replace>
+                <Button type="primary" ghost>
+                  <ui-icon type="add" class="icon" />上传文件
+                </Button>
+              </template>
+            </ui-input>
+          </span>
+          <Search
+            v-if="Object.keys(allData).length"
+            className="content-header-search"
+            placeholder="输入名称搜索"
+            v-model:value="params.name"
+            allow-clear
+            style="width: 244px"
+          />
+        </div>
       </div>
 
       <div class="table-layout">
@@ -39,6 +56,11 @@
             <template v-if="column.key === 'size'">
               {{ getSizeStr(record.size) }}
             </template>
+            <template v-else-if="column.key === 'action'">
+              <span>
+                <a @click="delHandler(column.id)">删除</a>
+              </span>
+            </template>
           </template>
         </Table>
         <div style="padding: 1px" v-else>
@@ -54,11 +76,13 @@
 </template>
 
 <script lang="ts" setup>
-import { Modal, Input, Table, Empty, TableProps } from "ant-design-vue";
+import { Modal, Input, Table, Empty, TableProps, Button } from "ant-design-vue";
 import { computed, reactive, ref, watch } from "vue";
-import { createLoadPack, debounce, debounceStack, getSizeStr } from "@/utils";
-import type { PagingResult, Scene } from "@/api";
+import { createLoadPack, debounceStack, getSizeStr } from "@/utils";
+import type { PagingResult } from "@/api";
 import {
+  addMaterial,
+  delMaterial,
   fetchMaterialGroups,
   fetchMaterialPage,
   Material,
@@ -66,8 +90,10 @@ import {
   MaterialPageProps,
 } from "@/api/material";
 import Message from "bill/components/message/message.vue";
+import { Dialog } from "bill/expose-common";
 
 const props = defineProps<{
+  uploadFormat?: string[];
   format?: string[];
   maxSize?: number;
   visible: boolean;
@@ -80,6 +106,12 @@ const emit = defineEmits<{
   (e: "selectMaterials", v: Material[]): void;
 }>();
 
+const ft = computed(() => {
+  const ft = props.uploadFormat || props.format;
+  if (!ft) return void 0;
+  return ft.map((t) => `.${t}`).join(",");
+});
+
 const Search = Input.Search;
 const params = reactive({ pageNum: 1, pageSize: 12 }) as MaterialPageProps;
 const origin = ref<PagingResult<Material[]>>({
@@ -139,25 +171,38 @@ const cloumns = computed(() => [
       value: g.id,
     })),
   },
+  {
+    title: "操作",
+    key: "action",
+  },
 ]);
 
 const fetchData = createLoadPack(() =>
   Promise.all([fetchMaterialGroups(), fetchMaterialPage(params)])
 );
-watch(
-  params,
-  debounceStack(
-    fetchData,
-    ([group, pag]) => {
-      groups.value = group;
-      origin.value = pag;
-      pag.list.forEach((item) => (allData[item.id] = item));
-    },
-    160
-  ),
-  { immediate: true, deep: true }
+const refresh = debounceStack(
+  fetchData,
+  ([group, pag]) => {
+    groups.value = group;
+    origin.value = pag;
+    pag.list.forEach((item) => (allData[item.id] = item));
+  },
+  160
 );
 
+watch(params, refresh, { immediate: true, deep: true });
+
+const addHandler = async (file: File) => {
+  await addMaterial(file);
+  refresh();
+};
+const delHandler = async (id: Material["id"]) => {
+  if (await Dialog.confirm("确定要删除此数据吗?")) {
+    await delMaterial(id);
+    refresh();
+  }
+};
+
 const okHandler = () => {
   emit(
     "selectMaterials",
@@ -183,12 +228,23 @@ const handleTableChange: TableProps["onChange"] = (pag, filters) => {
   max-height: 500px;
   overflow-y: auto;
 }
+.up-se {
+  display: flex;
+  align-items: center;
+
+  .upload {
+    margin-right: 10px;
+    width: 100px;
+    // overflow: hidden;
+  }
+}
 </style>
 
 <style lang="less">
 .model-header .header-desc {
   margin-bottom: 0;
 }
+
 .ant-modal-root .ant-table-tbody > tr > td {
   word-break: break-all;
 }

+ 1 - 0
src/components/materials/quisk.ts

@@ -4,6 +4,7 @@ import { Material } from "@/api/material";
 import { reactive } from "vue";
 
 export const selectMaterials = async (props: {
+  uploadFormat?: string[],
   format?: string[];
   maxSize?: number;
   count?: number

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

@@ -197,7 +197,8 @@ watch(visible, (visible, oldvisible) => {
 
 const selectModel = async () => {
   const list = await selectMaterials({
-    format: ["obj", "ply", "las", "laz", "b3dm"],
+    uploadFormat: ["zip"],
+    format: ["obj", "ply", "las", "laz", "b3dm", "shp"],
     maxSize: 2 * 1024 * 1024 * 1024,
   });
   if (!list?.length) return;

+ 1 - 0
src/sdk/association/path.ts

@@ -61,6 +61,7 @@ export const associationPaths = (sdk: SDK, el: HTMLDivElement) => {
   mount<PathsNode>(el, TaggingComponent, { paths }, (pathsNode) => {
     watch(
       () => {
+        if (!pathsNode) return []
         pathsNode.forEach(i => !!i?.id)
         return pathsNode
       },

+ 0 - 1
src/sdk/sdk.ts

@@ -368,7 +368,6 @@ export const initialSDK = async (props: InialSDKProps) => {
     mapDom: null,
   }) as unknown as SDK;
 
-  console.log(scenes.value);
   (window as any).sdk = sdk = localSdk;
   sdk.layout = props.layout;
 

+ 1 - 4
src/store/tagging-positions.ts

@@ -53,10 +53,7 @@ export const getTaggingPositionIsShow = (position: TaggingPosition) => {
 let bcPositions: TaggingPositions = []
 export const getBackupTaggingPositions = () => bcPositions
 export const backupTaggingPositions = () => {
-  bcPositions = taggingPositions.value.map(position => ({
-    ...position,
-    localPos: { ...position.localPos },
-  }))
+  bcPositions = JSON.parse(JSON.stringify(taggingPositions.value))
 }
 
 export const initTaggingPositionsByTagging = async (tagging: Tagging) => {

+ 1 - 0
src/store/tagging.ts

@@ -70,6 +70,7 @@ export const backupTaggings = () => {
   bcTaggings = taggings.value.map(tagging => ({
     ...tagging,
     images: [...tagging.images],
+    
   }))
 }
 

+ 3 - 23
src/views/guide/guide/show.vue

@@ -1,28 +1,8 @@
 <template>
-  <ui-group title="路径列表" class="show-guides">
-    <GuideSign 
-      v-for="guide in guides" 
-      :key="guide.id" 
-      :guide="guide" 
-      :edit="false"
-    />
-  </ui-group>
+  <GuideSign v-for="guide in guides" :key="guide.id" :guide="guide" :edit="false" />
 </template>
 
 <script setup lang="ts">
-import GuideSign from '@/views/guide/sign.vue'
-import { guides } from '@/store'
-
+import GuideSign from "./sign.vue";
+import { guides } from "@/store";
 </script>
-
-
-<style lang="scss">
-.show-guides.ui-group {
-   h3.group-title {
-    margin-bottom: 0;
-  }
-  .sign-guide:first-child {
-    border-top: none;
-  }
-}
-</style>

+ 8 - 0
src/views/guide/path/show.vue

@@ -0,0 +1,8 @@
+<template>
+  <PathSign v-for="path in paths" :key="path.id" :path="path" :edit="false" />
+</template>
+
+<script setup lang="ts">
+import PathSign from "./sign.vue";
+import { paths } from "@/store";
+</script>

+ 117 - 1
src/views/guide/show.vue

@@ -1,7 +1,123 @@
 <template>
-  <Guide />
+  <ui-group title="标签列表" class="show-taggings">
+    <template #header>
+      <Dropdown placement="bottom">
+        <span class="tab-span">
+          {{ current.text }}
+          <span class="count">({{ current.count }})</span>
+          <DownOutlined />
+        </span>
+        <template #overlay>
+          <Menu
+            style="width: 120px"
+            :active-key="currentKey"
+            mode="vertical"
+            :items="items"
+            @click="(info: any) => currentKey = info.key"
+          />
+        </template>
+      </Dropdown>
+    </template>
+    <div class="show-guides">
+      <Guide v-if="currentKey === 'guide'" />
+      <Path v-if="currentKey === 'path'" />
+    </div>
+  </ui-group>
 </template>
 
 <script setup lang="ts">
+import { computed, ref } from "vue";
 import Guide from "./guide/show.vue";
+import Path from "./path/show.vue";
+import { guides, paths } from "@/store";
+import { Menu, Dropdown } from "ant-design-vue";
+import { DownOutlined } from "@ant-design/icons-vue";
+
+const currentKey = ref("path");
+const tabs = computed(() => [
+  { key: "guide", text: "导览", count: guides.value.length },
+  { key: "path", text: "路线", count: paths.value.length },
+]);
+const current = computed(() => tabs.value.find((i) => i.key === currentKey.value)!);
+const items = computed(() => {
+  return tabs.value.map((i) => ({
+    ...i,
+    label: i.text + `(${i.count})`,
+    title: i.text + `(${i.count})`,
+  }));
+});
 </script>
+
+<style lang="scss">
+.show-guides.ui-group {
+  h3.group-title {
+    margin-bottom: 0;
+  }
+  .sign-guide:first-child {
+    border-top: none;
+  }
+}
+</style>
+
+<style lang="scss" scoped>
+.tabs {
+  height: 60px;
+  border-bottom: 1px solid rgba(255, 255, 255, 0.16);
+  display: flex;
+  margin: -20px;
+  margin-bottom: 20px;
+
+  > span {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    position: relative;
+    transition: color 0.3s ease;
+    cursor: pointer;
+    font-size: 16px;
+
+    &::after {
+      content: "";
+      transition: height 0.3s ease;
+      position: absolute;
+      background-color: #00c8af;
+      left: 0;
+      right: 0;
+      bottom: 0;
+      height: 0;
+    }
+
+    &:hover,
+    &.active {
+      color: #00c8af;
+    }
+
+    &.active::after {
+      height: 3px;
+    }
+  }
+}
+
+.tab-span {
+  font-size: 14px;
+  cursor: pointer;
+
+  .count {
+    color: var(--color-main-normal);
+  }
+}
+</style>
+
+<style lang="scss">
+.show-taggings.ui-group > h3.group-title {
+  margin-bottom: 0;
+}
+.show-taggings.sign-tagging.active::after {
+  display: none;
+}
+
+.show-taggings .sign-guide:first-child {
+  border-top: none;
+}
+</style>

+ 10 - 1
src/views/tagging-position/index.vue

@@ -10,6 +10,7 @@
         :title="`位置${i + 1}`"
         @applyGlobal="(keys) => applyGlobal(position, keys)"
         @delete="deletePosition(position)"
+        @show="showId = position.id"
       />
     </Collapse>
     <Teleport to="#layout-app">
@@ -34,7 +35,15 @@ import { Dialog, Message } from "bill/index";
 import { RightFillPano } from "@/layout";
 import { asyncTimeout } from "@/utils";
 import { useViewStack } from "@/hook";
-import { computed, nextTick, onUnmounted, ref, shallowRef, watch } from "vue";
+import {
+  computed,
+  nextTick,
+  onUnmounted,
+  ref,
+  shallowRef,
+  watch,
+  watchEffect,
+} from "vue";
 import { sdk } from "@/sdk";
 import { showTaggingPositionsStack } from "@/env";
 import {

+ 18 - 3
src/views/tagging-position/sign.vue

@@ -91,13 +91,28 @@ import SignItem from "./sign-item.vue";
 
 import type { TaggingPosition } from "@/store";
 import { TaggingPositionType } from "@/api";
+import { computed, watchEffect } from "vue";
+import { getTaggingPosNode } from "@/sdk";
 
-defineProps<{ position: TaggingPosition; title: string }>();
-
-defineEmits<{
+const props = defineProps<{ position: TaggingPosition; title: string }>();
+const emit = defineEmits<{
   (e: "applyGlobal", k: string | string[]): void;
   (e: "delete"): void;
+  (e: "show"): void;
 }>();
+const node = computed(() => getTaggingPosNode(props.position));
+watchEffect((onCleanup) => {
+  if (!node.value) return;
+  const $node = node.value;
+  const clickHandler = () => {
+    emit("show");
+  };
+  $node.bus.on("click", clickHandler);
+
+  onCleanup(() => {
+    $node.bus.off("click", clickHandler);
+  });
+});
 </script>
 
 <style lang="scss" scoped src="./style.scss"></style>

+ 27 - 15
src/views/tagging/show.vue

@@ -1,33 +1,45 @@
 <template>
   <ui-group title="标签列表" class="show-taggings">
+    <template #header>
+      <StyleTypeSelect v-model:value="type" all count />
+    </template>
     <template #icon>
-      <ui-icon 
+      <ui-icon
         ctrl
-        :type="custom.showTaggings ? 'eye-s' : 'eye-n'" 
-        @click="custom.showTaggings = !custom.showTaggings" 
+        :type="custom.showTaggings ? 'eye-s' : 'eye-n'"
+        @click="custom.showTaggings = !custom.showTaggings"
       />
     </template>
-    <TaggingSign 
-      v-for="tagging in taggings" 
-      :key="tagging.id" 
-      :tagging="tagging" 
+    <TaggingSign
+      v-for="tagging in filterTaggings"
+      :key="tagging.id"
+      :tagging="tagging"
       :selected="selectTagging === tagging"
       :edit="false"
-      @select="selected => selectTagging = selected ? tagging : null"
+      @select="(selected) => (selectTagging = selected ? tagging : null)"
       class="show-tagging"
     />
   </ui-group>
 </template>
 
 <script setup lang="ts">
-import { ref } from 'vue'
-import { custom } from '@/env'
-import TaggingSign from './sign.vue'
-import { taggings } from '@/store'
+import { computed, ref } from "vue";
+import { custom } from "@/env";
+import TaggingSign from "./sign.vue";
+import { getTaggingStyle, taggings } from "@/store";
+import StyleTypeSelect from "./style-type-select.vue";
 
-import type { Tagging } from '@/store'
+import type { Tagging, TaggingStyle } from "@/store";
 
-const selectTagging = ref<Tagging | null>(null)
+const selectTagging = ref<Tagging | null>(null);
+const type = ref<TaggingStyle["typeId"]>(-1);
+const filterTaggings = computed(() =>
+  taggings.value.filter((tagging) => {
+    if (type.value === -1) return true;
+    const style = getTaggingStyle(tagging.styleId);
+    return style?.typeId === type.value;
+  })
+);
 </script>
 
 <style lang="scss">
@@ -37,4 +49,4 @@ const selectTagging = ref<Tagging | null>(null)
 .show-tagging.sign-tagging.active::after {
   display: none;
 }
-</style>
+</style>

+ 2 - 2
src/views/tagging/style-type-select.vue

@@ -4,8 +4,8 @@
     <span>
       {{ current.title }}
       <span class="count" v-if="count">({{ current.count }})</span>
-      <DownOutlined
-    /></span>
+      <DownOutlined />
+    </span>
     <template #overlay>
       <Menu
         style="width: 120px"