ananzhusen 10 mēneši atpakaļ
vecāks
revīzija
d377344ac0

+ 12 - 1
src/services/selections.ts

@@ -6,12 +6,14 @@ export enum SelectionMode {
   File,
   Pen,
   Pens,
+  Fit // 布局模式
 }
 
 const selections = reactive<{
   mode: SelectionMode;
   pen?: Pen;
   pens?: Pen[];
+  fit?: any;
 }>({
   // 选中对象类型:0 - 画布;1 - 单个图元;2 - 多选
   mode: SelectionMode.File,
@@ -20,7 +22,16 @@ const selections = reactive<{
 });
 
 export const useSelection = () => {
-  const select = (pens?: Pen[]) => {
+  const select = (pens?: Pen[], fit?: any) => {
+    if (fit) {
+      selections.mode = SelectionMode.Fit;
+      selections.pen = undefined;
+      selections.pens = undefined;
+      selections.fit = fit;
+      return;
+    }else{
+      selections.fit = undefined;
+    }
     if (!pens || !pens.length) {
       selections.mode = SelectionMode.File;
       selections.pen = undefined;

+ 3 - 1
src/views/Index.vue

@@ -6,7 +6,8 @@
       <Graphics />
       <View />
       <div style="border-left: 1px solid var(--color-border);">
-        <FileProps v-if="selections.mode === SelectionMode.File" />
+        <FitProps v-if="selections.mode === SelectionMode.Fit" />
+        <FileProps v-else-if="selections.mode === SelectionMode.File" />
         <PenProps v-else-if="selections.mode === SelectionMode.Pen" />
         <PensProps v-else />
       </div>
@@ -21,6 +22,7 @@ import View from './components/View.vue';
 import FileProps from './components/FileProps.vue';
 import PenProps from './components/PenProps.vue';
 import PensProps from './components/PensProps.vue';
+import FitProps from './components/FitProps.vue';
 
 import { useSelection, SelectionMode } from '@/services/selections';
 import {onMounted} from "vue";

+ 260 - 0
src/views/components/FitProps.vue

@@ -0,0 +1,260 @@
+<template>
+  <div class="props">
+    <t-tabs v-model="data.tab">
+      <t-tab-panel :value="1" label="布局容器">
+        <t-space direction="vertical" class="py-16 w-full">
+          <div class="form-item px-12">
+            <label style="width: 67px">ID</label>
+            <div style="padding-left: 8px; line-height: 30px">
+              {{ data.fit.id }}
+            </div>
+          </div>
+          <div class="form-item px-12" style="margin-top: -12px">
+            <label style="width: 50px">名称</label>
+            <div style="padding-left: 8px; line-height: 30px">
+              <t-input
+                class="ml-8"
+                v-model="data.fit.name"
+                placeholder="默认0"
+              />
+            </div>
+          </div>
+          <div class="form-item px-12">
+            <label>靠左</label>
+            <div class="flex center" style="align-items: center">
+              <t-switch size="small" v-model="data.fit.left" />
+              <t-input-number
+                theme="normal"
+                class="ml-8"
+                @change="onFitChange"
+                v-model="data.fit.leftValue"
+                placeholder="默认0"
+              />
+            </div>
+          </div>
+          <div class="form-item px-12">
+            <label>靠右</label>
+            <div class="flex center" style="align-items: center">
+              <t-switch size="small" v-model="data.fit.right" />
+              <t-input-number
+                theme="normal"
+                class="ml-8"
+                @change="onFitChange"
+                v-model="data.fit.rightValue"
+                placeholder="默认0"
+              />
+            </div>
+          </div>
+          <div class="form-item px-12">
+            <label>靠上</label>
+            <div class="flex center" style="align-items: center">
+              <t-switch size="small" v-model="data.fit.top" />
+              <t-input-number
+                theme="normal"
+                class="ml-8"
+                @change="onFitChange"
+                v-model="data.fit.topValue"
+                placeholder="默认0"
+              />
+            </div>
+          </div>
+          <div class="form-item px-12">
+            <label>靠下</label>
+            <div class="flex center" style="align-items: center">
+              <t-switch size="small" v-model="data.fit.bottom" />
+              <t-input-number
+                theme="normal"
+                class="ml-8"
+                v-model="data.fit.bottomValue"
+                @change="onFitChange"
+                placeholder="默认0"
+              />
+            </div>
+          </div>
+          <div class="form-item px-12">
+            <label>展示图元</label>
+            <div>
+              <t-switch size="small" @change="fitPenVisible" />
+            </div>
+          </div>
+          <div class="px-12">
+            <div class="mb-8">图元列表</div>
+            <div class="grid head">
+              <div class="title">名称</div>
+              <div class="title">图元id</div>
+              <div class="title">操作</div>
+            </div>
+            <div class="body">
+              <div
+                :data-penid="item.value"
+                v-for="(item, i) in data.list"
+                class="grid"
+                :class="data.active.includes(item.value) ? 'active' : ''"
+              >
+                <span class="hover" @click="doActive(item.value)">
+                  {{ item.label }}
+                </span>
+                <span class="hover" @click="doActive(item.value)">
+                  {{ item.value }}
+                </span>
+                <t-popconfirm content="确认移除吗" @confirm="doRemove(i)">
+                  <t-tooltip content="从布局容器中移除此图元">
+                    <div class="hover"><delete-icon /></div>
+                  </t-tooltip>
+                </t-popconfirm>
+              </div>
+            </div>
+          </div>
+        </t-space>
+      </t-tab-panel>
+    </t-tabs>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import {
+  reactive,
+  onBeforeMount,
+  ref,
+  watch,
+  onUnmounted,
+  onMounted,
+} from 'vue';
+import { useSelection } from '@/services/selections';
+import { DeleteIcon } from 'tdesign-icons-vue-next';
+
+const data = reactive({
+  fit: undefined,
+  tab: 1,
+  list: [],
+  active: [],
+});
+const { selections } = useSelection();
+
+onBeforeMount(() => {
+  data.fit = selections.fit;
+  getChildrenList();
+});
+
+onMounted(() => {
+  meta2d.on('active', active);
+  meta2d.on('fit', childChange);
+});
+
+onUnmounted(() => {
+  watcher();
+  meta2d.off('active', active);
+  meta2d.off('fit', childChange);
+});
+
+const childChange = () => {
+  getChildrenList();
+};
+
+const watcher = watch(
+  () => selections.fit.id,
+  (id) => {
+    data.fit = selections.fit;
+    getChildrenList();
+  }
+);
+
+const getChildrenList = () => {
+  data.list = [];
+  selections.fit&&selections.fit.children?.forEach((id) => {
+    const pen = meta2d.store.pens[id];
+    if (pen) {
+      data.list.push({
+        label: (pen as any).description || pen.name,
+        value: pen.id,
+        locked: pen.locked,
+        visible: pen.visible,
+        tag: (pen as any).tag,
+      });
+    }
+  });
+};
+
+const fitPenVisible = (e) => {
+  if (e) {
+    meta2d.store.data.pens.forEach((pen) => {
+      if (!selections.fit.children?.includes(pen.id)) {
+        meta2d.setValue(
+          { id: pen.id, visible: false },
+          { render: false, doEvent: false, history: false }
+        );
+      }
+    });
+    meta2d.canvas.hideFit();
+    meta2d.canvas.canvasImage.init();
+    meta2d.canvas.canvasImageBottom.init();
+
+    meta2d.render();
+  } else {
+    meta2d.store.data.pens.forEach((pen) => {
+      if (!selections.fit.children?.includes(pen.id)) {
+        meta2d.setValue(
+          { id: pen.id, visible: true },
+          { render: false, doEvent: false, history: false }
+        );
+      }
+    });
+    meta2d.canvas.showFit();
+    meta2d.canvas.canvasImage.init();
+    meta2d.canvas.canvasImageBottom.init();
+    meta2d.render();
+  }
+};
+
+const doActive = (id) => {
+  let pen = meta2d.store.pens[id];
+  meta2d.active([pen], false);
+};
+
+const doRemove = (idx) => {
+  let childId = data.list[idx].value;
+  data.fit.children.splice(idx, 1);
+  meta2d.inactive();
+  meta2d.setVisible(meta2d.store.pens[childId], false);
+  getChildrenList();
+};
+
+const active = () => {
+  data.active = meta2d.store.active.map((pen) => pen.id);
+  const element = document.body.querySelector(
+    `[data-penid="${data.active[0]}"]`
+  );
+  if (element) {
+    element.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
+  }
+};
+
+const onFitChange = () => {
+  meta2d.canvas.updateFitRect(data.fit);
+};
+</script>
+<style lang="postcss" scoped>
+.props {
+  .grid {
+    grid-template-columns: 100px 100px auto;
+    padding: 0 12px;
+
+    &.head {
+      background: var(--color-background-input);
+      line-height: 36px;
+      margin-bottom: 6px;
+
+      .title {
+        line-height: 36px;
+      }
+    }
+  }
+  .active {
+    color: var(--color-primary);
+  }
+  .body {
+    max-height: 300px;
+    overflow-y: auto;
+  }
+}
+</style>

+ 58 - 1
src/views/components/View.vue

@@ -293,6 +293,16 @@
           </svg>
         </a>
       </t-tooltip>
+      <t-tooltip content="自适应设置" placement="bottom">
+        <a
+        :style="{
+            background: fitFlag ? '#4480F929' : '',
+            color: fitFlag ?( theme==='light'?'#4480F9':'#4480F9'):''
+          }"
+        @click="changeFit">
+          <LayoutIcon />
+        </a>
+      </t-tooltip>
       <div class="flex-grow"></div>
       <!-- <t-tooltip content="数据管理" placement="bottom">
         <a @click="onShowDataDialog">
@@ -967,7 +977,7 @@ import ContextMenu from './ContextMenu.vue';
 import Network from './Network.vue';
 import Dataset from './Dataset.vue';
 import ChargeCloudPublish from './ChargeCloudPublish.vue';
-import { AddIcon, EditIcon, SaveIcon, RootListIcon,SlashIcon, RefreshIcon, ServerIcon, CaretRightIcon, ShareIcon, QrcodeIcon, CloudIcon, DeleteIcon, SearchIcon, RollbackIcon, LaptopIcon, StopCircleIcon , PlayCircleIcon, PlayCircleStrokeIcon, LayersIcon, FullscreenExitIcon, FileIcon, FileExcelIcon, CloudDownloadIcon, PenBrushIcon, PenIcon } from 'tdesign-icons-vue-next';
+import { AddIcon, EditIcon, SaveIcon, RootListIcon,SlashIcon, RefreshIcon, ServerIcon, CaretRightIcon, ShareIcon, QrcodeIcon, CloudIcon, DeleteIcon, SearchIcon, RollbackIcon, LaptopIcon, StopCircleIcon , PlayCircleIcon, PlayCircleStrokeIcon, LayersIcon, FullscreenExitIcon, FileIcon, FileExcelIcon, CloudDownloadIcon, PenBrushIcon, PenIcon, LayoutIcon } from 'tdesign-icons-vue-next';
 import {transformData} from '@/services/utils';
 import { importExcel, saveAsExcel } from '@/services/excel';
 import { typeOptions } from '@/services/common';
@@ -1022,6 +1032,7 @@ onMounted(() => {
   open(true);
   meta2d.on('active', active);
   meta2d.on('inactive', inactive);
+  meta2d.on('fit',fit);
   meta2d.on('scale', scaleSubscriber);
   meta2d.on('add', lineAdd);
   meta2d.on('opened', openedListener);
@@ -1045,6 +1056,28 @@ onMounted(() => {
   };
 });
 
+onUnmounted(()=>{
+  meta2d.off('active', active);
+  meta2d.off('inactive', inactive);
+  meta2d.off('fit',fit);
+  meta2d.off('scale', scaleSubscriber);
+  meta2d.off('add', lineAdd);
+  meta2d.off('opened', openedListener);
+
+  meta2d.off('undo', patchFlag);
+  meta2d.off('redo', patchFlag);
+  meta2d.off('add', patchFlag);
+  meta2d.off('delete', patchFlag);
+  meta2d.off('rotatePens', patchFlag);
+  meta2d.off('translatePens', patchFlag);
+
+  // 所有编辑栏所做修改
+  meta2d.off('components-update-value', patchFlag);
+  meta2d.off('contextmenu', onContextmenu);
+  meta2d.off('click', canvasClick);
+  // meta2d.off('business-showPayDiagram',showPayDiagram);
+})
+
 const watcher = watch(
   () => route.query,
   async () => {
@@ -1169,6 +1202,8 @@ const openedListener = () => {
   toArrow.value = canvasToArrow || '';
   deal2DToV();
   meta2d.centerView();
+  meta2d.canvas.hideFit();
+  fitFlag.value = false;
 };
 
 const deal2DToV = ()=>{
@@ -1219,10 +1254,16 @@ onUnmounted(() => {
 });
 
 const inactive = () => {
+  if(fitFlag.value){
+    return;
+  }
   select();
 };
 
 const active = (pens: Pen[]) => {
+  if(fitFlag.value){
+    return;
+  }
   select(pens);
 
   //格式刷处理
@@ -1232,6 +1273,10 @@ const active = (pens: Pen[]) => {
   }
 };
 
+const fit = (fit) => {
+  select(undefined, fit);
+}
+
 const one = ref(false);
 const always = ref(false);
 
@@ -2338,6 +2383,18 @@ const theme = computed(() => {
   return localStorage.getItem('theme') || 'dark';
 });
 
+const fitFlag = ref(false);
+
+const changeFit = () => {
+  fitFlag.value = !fitFlag.value;
+  if(fitFlag.value){
+    meta2d.canvas.showFit();
+  }else{
+    meta2d.canvas.hideFit();
+    select();
+  }
+}
+
 </script>
 <style lang="postcss" scoped>
 .meta2d {