|
@@ -17,14 +17,53 @@
|
|
|
{{ group.name }}
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div class="list" :class="groupType ? 'two-list' : ''">
|
|
|
- <t-collapse v-model:value="activedPanel" @change="onChangeGroupPanel">
|
|
|
+ <div class="list" :class="groupType ? 'two-columns' : ''">
|
|
|
+ <div v-if="activedGroup === '我的'" class="px-16 mt-12 mb-8 ml-4">
|
|
|
+ <a @click="onCreateFolder">+ 新建文件夹</a>
|
|
|
+ </div>
|
|
|
+ <t-collapse
|
|
|
+ v-if="groupType < 2"
|
|
|
+ v-model:value="activedPanel"
|
|
|
+ @change="onChangeGroupPanel"
|
|
|
+ >
|
|
|
<t-collapse-panel
|
|
|
:value="item.name"
|
|
|
- :header="item.name"
|
|
|
v-for="item in subGroups"
|
|
|
:key="item.name"
|
|
|
>
|
|
|
+ <template #header>
|
|
|
+ <div class="flex middle mr-8">
|
|
|
+ <div v-if="item.edited" @click.stop>
|
|
|
+ <t-input
|
|
|
+ v-model="item.label"
|
|
|
+ style="width: 140px"
|
|
|
+ @blur="createFoder"
|
|
|
+ @enter="createFoder"
|
|
|
+ @keyup="onKeyHeader"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div v-else class="ellipsis" style="width: 140px">
|
|
|
+ {{ item.name }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template #headerRightContent v-if="item.canEdited">
|
|
|
+ <t-space size="small" @click.stop>
|
|
|
+ <t-icon
|
|
|
+ name="edit"
|
|
|
+ class="hover mr-4"
|
|
|
+ @click="onEditHeader(item)"
|
|
|
+ />
|
|
|
+
|
|
|
+ <t-popconfirm
|
|
|
+ content="确认删除该文件夹吗"
|
|
|
+ placement="left"
|
|
|
+ @confirm=""
|
|
|
+ >
|
|
|
+ <t-icon name="delete" class="hover" />
|
|
|
+ </t-popconfirm>
|
|
|
+ </t-space>
|
|
|
+ </template>
|
|
|
<div v-if="item.loading">
|
|
|
<t-loading
|
|
|
text="加载中..."
|
|
@@ -36,14 +75,21 @@
|
|
|
<div
|
|
|
class="graphic"
|
|
|
v-for="elem in item.list"
|
|
|
- :draggable="!groupType || groupType >= 10"
|
|
|
+ :draggable="elem.draggable !== false"
|
|
|
@dragstart="dragStart($event, elem)"
|
|
|
@drag="drag($event, elem)"
|
|
|
@dragend="dragEnd()"
|
|
|
@click.stop="dragStart($event, elem)"
|
|
|
@dblclick.stop="open(elem)"
|
|
|
+ @contextmenu="onContextMenu($event, item, elem)"
|
|
|
+ :title="elem.draggable === false ? '双击打开' : '拖拽到画布'"
|
|
|
>
|
|
|
- <t-image v-if="elem.image" :src="elem.image" />
|
|
|
+ <t-image
|
|
|
+ v-if="elem.image"
|
|
|
+ :src="elem.image"
|
|
|
+ :lazy="true"
|
|
|
+ fit="contain"
|
|
|
+ />
|
|
|
<div class="svg-box" v-else-if="elem.svg" v-html="elem.svg" />
|
|
|
<svg v-else class="l-icon" aria-hidden="true">
|
|
|
<use :xlink:href="'#' + elem.icon"></use>
|
|
@@ -63,34 +109,110 @@
|
|
|
</template>
|
|
|
</t-collapse-panel>
|
|
|
</t-collapse>
|
|
|
+ <div v-else class="t-collapse-panel__content" style="padding: 8px">
|
|
|
+ <div
|
|
|
+ class="graphic"
|
|
|
+ v-for="elem in subGroups"
|
|
|
+ :draggable="elem.draggable !== false"
|
|
|
+ @dragstart="dragStart($event, elem)"
|
|
|
+ @drag="drag($event, elem)"
|
|
|
+ @dragend="dragEnd()"
|
|
|
+ @click.stop="dragStart($event, elem)"
|
|
|
+ @dblclick.stop="open(elem)"
|
|
|
+ :title="elem.draggable === false ? '双击打开' : '拖拽到画布'"
|
|
|
+ >
|
|
|
+ <t-image
|
|
|
+ v-if="elem.image"
|
|
|
+ :src="elem.image"
|
|
|
+ :lazy="true"
|
|
|
+ fit="contain"
|
|
|
+ />
|
|
|
+ <div class="svg-box" v-else-if="elem.svg" v-html="elem.svg" />
|
|
|
+ <svg v-else class="l-icon" aria-hidden="true">
|
|
|
+ <use :xlink:href="'#' + elem.icon"></use>
|
|
|
+ </svg>
|
|
|
+ <p :title="elem.name">{{ elem.name }}</p>
|
|
|
+ <div class="price" v-if="elem.price > 0">¥{{ elem.price }}</div>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ v-if="!subGroups.length"
|
|
|
+ class="gray center"
|
|
|
+ style="white-space: nowrap; margin-left: 32px"
|
|
|
+ >
|
|
|
+ 暂无数据,待更新
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <div
|
|
|
+ class="context-menu-box"
|
|
|
+ ref="contextmenuDom"
|
|
|
+ v-if="contextmenu.visible"
|
|
|
+ tabindex="0"
|
|
|
+ :style="contextmenu.style"
|
|
|
+ @blu1r="contextmenu.visible = false"
|
|
|
+ >
|
|
|
+ <t-menu class="context-menu" @change="onMenu" expandType="popup">
|
|
|
+ <t-submenu
|
|
|
+ value="move"
|
|
|
+ title="移动到"
|
|
|
+ v-if="contextmenu.subMenus.length"
|
|
|
+ >
|
|
|
+ <t-menu-item
|
|
|
+ v-for="subMenu in contextmenu.subMenus"
|
|
|
+ :value="'move:' + subMenu.name"
|
|
|
+ >
|
|
|
+ {{ subMenu.name }}
|
|
|
+ </t-menu-item>
|
|
|
+ </t-submenu>
|
|
|
+ <t-menu-item value="edit"> 编辑 </t-menu-item>
|
|
|
+ <t-menu-item value="del"> 删除 </t-menu-item>
|
|
|
+ </t-menu>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <t-dialog
|
|
|
+ v-if="delDialog.show"
|
|
|
+ theme="danger"
|
|
|
+ header="删除"
|
|
|
+ :visible="true"
|
|
|
+ @close="delDialog.show = false"
|
|
|
+ @confirm="delComponet"
|
|
|
+ >
|
|
|
+ 确定删除该数据吗?删除后不可恢复!
|
|
|
+ </t-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-import { onMounted, onUnmounted, reactive, ref, watch } from 'vue';
|
|
|
+import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
|
|
import { useRouter } from 'vue-router';
|
|
|
import axios from 'axios';
|
|
|
|
|
|
import { cases, shapes, charts, formComponents } from '@/services/defaults';
|
|
|
-import { getPngFolders, getPngs } from '@/services/png';
|
|
|
-import { getIconFolders, getIcons } from '@/services/icons';
|
|
|
-import { getComponents, getComponentsList, getLe5leV } from '@/services/api';
|
|
|
+import { getFolders, getFiles, getIcons } from '@/services/png';
|
|
|
+import {
|
|
|
+ getComponents,
|
|
|
+ getComponentsList,
|
|
|
+ getLe5leV,
|
|
|
+ updateCollection,
|
|
|
+} from '@/services/api';
|
|
|
import { convertPen } from '@/services/upgrade';
|
|
|
import { deepClone } from '@meta2d/core';
|
|
|
import { isGif } from '@/services/utils';
|
|
|
import { autoSave, delAttrs } from '@/services/common';
|
|
|
+import { MessagePlugin } from 'tdesign-vue-next';
|
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
|
-const activedGroup = ref('图形');
|
|
|
+const activedGroup = ref('');
|
|
|
|
|
|
const groups = reactive([
|
|
|
{
|
|
|
icon: 'desktop',
|
|
|
name: '场景',
|
|
|
key: '',
|
|
|
+ class: 'tow',
|
|
|
},
|
|
|
{
|
|
|
icon: 'root-list',
|
|
@@ -103,18 +225,18 @@ const groups = reactive([
|
|
|
key: 'chart',
|
|
|
},
|
|
|
{
|
|
|
- icon: 'control-platform',
|
|
|
- name: '控件',
|
|
|
+ icon: 'image',
|
|
|
+ name: '素材',
|
|
|
key: '',
|
|
|
},
|
|
|
{
|
|
|
- icon: 'image',
|
|
|
- name: '素材',
|
|
|
+ icon: 'control-platform',
|
|
|
+ name: '图元',
|
|
|
key: '',
|
|
|
},
|
|
|
{
|
|
|
- icon: 'file-icon',
|
|
|
- name: '图标',
|
|
|
+ icon: 'relativity',
|
|
|
+ name: '控件',
|
|
|
key: '',
|
|
|
},
|
|
|
{
|
|
@@ -132,9 +254,11 @@ const subGroups = ref<any[]>([]);
|
|
|
const groupType = ref(0);
|
|
|
const activedPanel = ref([]);
|
|
|
|
|
|
-const templates = reactive<any>({});
|
|
|
-const materials = reactive([]);
|
|
|
-const icons = reactive([]);
|
|
|
+const caseCaches = reactive<any>({});
|
|
|
+const templates = ref([]);
|
|
|
+const materials = ref([]);
|
|
|
+const pngs = ref([]);
|
|
|
+const icons = ref([]);
|
|
|
|
|
|
const groupChange = async (name: string) => {
|
|
|
activedPanel.value = [];
|
|
@@ -145,27 +269,21 @@ const groupChange = async (name: string) => {
|
|
|
groupType.value = 1;
|
|
|
subGroups.value = cases;
|
|
|
subGroups.value[0].loading = true;
|
|
|
- if (!templates[name + cases[0].name]) {
|
|
|
- templates[name + cases[0].name] = await getCaseProjects(
|
|
|
+ if (!caseCaches[name + cases[0].name]) {
|
|
|
+ caseCaches[name + cases[0].name] = await getCaseProjects(
|
|
|
name,
|
|
|
cases[0].name
|
|
|
);
|
|
|
}
|
|
|
- subGroups.value[0].list = templates[name + cases[0].name];
|
|
|
+ subGroups.value[0].list = caseCaches[name + cases[0].name];
|
|
|
subGroups.value[0].loading = false;
|
|
|
break;
|
|
|
case '模板':
|
|
|
groupType.value = 2;
|
|
|
- subGroups.value = cases;
|
|
|
- subGroups.value[0].loading = true;
|
|
|
- if (!templates[name + cases[0].name]) {
|
|
|
- templates[name + cases[0].name] = await getCaseProjects(
|
|
|
- name,
|
|
|
- cases[0].name
|
|
|
- );
|
|
|
+ if (!templates.value.length) {
|
|
|
+ templates.value = await getCaseProjects(name);
|
|
|
}
|
|
|
- subGroups.value[0].list = templates[name + cases[0].name];
|
|
|
- subGroups.value[0].loading = false;
|
|
|
+ subGroups.value = templates.value;
|
|
|
break;
|
|
|
case '图表':
|
|
|
subGroups.value = charts;
|
|
@@ -174,33 +292,45 @@ const groupChange = async (name: string) => {
|
|
|
subGroups.value = formComponents;
|
|
|
break;
|
|
|
case '素材':
|
|
|
- if (!materials.length) {
|
|
|
- materials.push(...(await getPngFolders()));
|
|
|
+ groupType.value = 2;
|
|
|
+ if (!materials.value.length) {
|
|
|
+ materials.value = await getFiles('material/');
|
|
|
}
|
|
|
- subGroups.value = materials;
|
|
|
+ subGroups.value = materials.value;
|
|
|
break;
|
|
|
- case '图标':
|
|
|
- if (!icons.length) {
|
|
|
- icons.push(...(await getIconFolders()));
|
|
|
+ case '图元':
|
|
|
+ subGroups.value = [];
|
|
|
+ if (!pngs.value.length) {
|
|
|
+ pngs.value = await getFolders('png');
|
|
|
}
|
|
|
- subGroups.value = icons;
|
|
|
+ subGroups.value.push(...pngs.value);
|
|
|
+ if (!icons.value.length) {
|
|
|
+ icons.value = await getFolders('svg');
|
|
|
+ }
|
|
|
+ subGroups.value.push(...icons.value);
|
|
|
+ onChangeGroupPanel([subGroups.value[0].name]);
|
|
|
break;
|
|
|
case '图形':
|
|
|
subGroups.value = shapes;
|
|
|
break;
|
|
|
case '我的':
|
|
|
- subGroups.value = await getPrivateCommponents();
|
|
|
- groupType.value = 10;
|
|
|
+ subGroups.value = await getPrivateGroups();
|
|
|
+ groupType.value = 1;
|
|
|
+ onChangeGroupPanel([subGroups.value[0].name]);
|
|
|
break;
|
|
|
}
|
|
|
activedPanel.value = [subGroups.value[0].name];
|
|
|
};
|
|
|
|
|
|
-const getCaseProjects = async (name: string, group: string) => {
|
|
|
+const getCaseProjects = async (name: string, group?: string) => {
|
|
|
+ const query: any = { tags: name };
|
|
|
+ if (group) {
|
|
|
+ query.case = group;
|
|
|
+ }
|
|
|
const ret: any = await axios.post(
|
|
|
- '/api/data/le5leV/list?current=1&pageSize=1000',
|
|
|
+ '/api/data/le5leV/list?current=1&pageSize=100',
|
|
|
{
|
|
|
- query: { tags: name, case: group },
|
|
|
+ query,
|
|
|
shared: 'true',
|
|
|
projection: { _id: 1, name: 1, image: 1, price: 1 },
|
|
|
}
|
|
@@ -209,119 +339,112 @@ const getCaseProjects = async (name: string, group: string) => {
|
|
|
if (!ret) {
|
|
|
return [];
|
|
|
}
|
|
|
+ for (const item of ret.list) {
|
|
|
+ item.draggable = false;
|
|
|
+ }
|
|
|
return ret.list;
|
|
|
};
|
|
|
|
|
|
-const getPrivateCommponents = async () => {
|
|
|
- const data = {
|
|
|
- projection: {
|
|
|
- image: 1,
|
|
|
- _id: 1,
|
|
|
- name: 1,
|
|
|
- folder: 1,
|
|
|
- component: 1,
|
|
|
+const getPrivateGroups = async () => {
|
|
|
+ const list = [
|
|
|
+ {
|
|
|
+ name: '我的组件',
|
|
|
+ list: [],
|
|
|
},
|
|
|
- };
|
|
|
+ ];
|
|
|
const config = {
|
|
|
params: {
|
|
|
current: 1,
|
|
|
- pageSize: 100,
|
|
|
+ pageSize: 1000,
|
|
|
},
|
|
|
};
|
|
|
- const res: any = await getComponentsList(data, config);
|
|
|
- const folderMap: any = {};
|
|
|
- res.list?.map((item: any) => {
|
|
|
- if (!folderMap[item.folder]) {
|
|
|
- folderMap[item.folder] = [];
|
|
|
- }
|
|
|
+ let ret: any = await axios.post(
|
|
|
+ '/api/data/folders/list',
|
|
|
+ {
|
|
|
+ projection: {
|
|
|
+ image: 1,
|
|
|
+ _id: 1,
|
|
|
+ name: 1,
|
|
|
+ list: 1,
|
|
|
+ },
|
|
|
+ query: {
|
|
|
+ type: `le5leV-components`,
|
|
|
+ },
|
|
|
+ sort: { createdAt: 1 },
|
|
|
+ },
|
|
|
+ config
|
|
|
+ );
|
|
|
+ if (!ret) {
|
|
|
+ ret = { list: [] };
|
|
|
+ }
|
|
|
|
|
|
- folderMap[item.folder].push(item);
|
|
|
- });
|
|
|
- let list = [];
|
|
|
- for (let key in folderMap) {
|
|
|
- list.push({
|
|
|
- name: key === 'undefined' ? '未分类' : key,
|
|
|
- show: true,
|
|
|
- list: folderMap[key],
|
|
|
- });
|
|
|
+ for (const item of ret.list) {
|
|
|
+ item.canEdited = true;
|
|
|
}
|
|
|
|
|
|
+ list.push(...ret.list);
|
|
|
+ list.push({
|
|
|
+ name: '3D',
|
|
|
+ list: [],
|
|
|
+ });
|
|
|
+
|
|
|
return list;
|
|
|
};
|
|
|
|
|
|
-watch(
|
|
|
- () => activedPanel.value,
|
|
|
- async (newV: any[], oldV: any[]) => {
|
|
|
- const newOpen: any = [];
|
|
|
- for (let v of newV) {
|
|
|
- !oldV.includes(v) && newOpen.push(v);
|
|
|
- }
|
|
|
- if (newOpen.length === 0) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (activedGroup.value === '素材') {
|
|
|
- const data: any = materials.find((item) => item.name === newOpen[0]);
|
|
|
- if (!data.list || data.list.length === 0) {
|
|
|
- data.list = await getPngs(
|
|
|
- globalThis.folderJson ? data.pinyin : data.name
|
|
|
- );
|
|
|
- subGroups.value = deepClone(materials);
|
|
|
- }
|
|
|
- } else if (activedGroup.value === '图标') {
|
|
|
- const data: any = icons.find((item) => item.name === newOpen[0]);
|
|
|
- if (!data.list || data.list.length === 0) {
|
|
|
- data.list = await getIcons(
|
|
|
- globalThis.folderJson ? data.pinyin : data.name
|
|
|
- );
|
|
|
- subGroups.value = deepClone(icons);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-);
|
|
|
-
|
|
|
const dragStart = async (event: DragEvent | MouseEvent, item: any) => {
|
|
|
if (
|
|
|
- (groupType.value > 0 && groupType.value < 10) ||
|
|
|
!item ||
|
|
|
+ item.draggable === false ||
|
|
|
(event instanceof DragEvent && !event.dataTransfer)
|
|
|
) {
|
|
|
return;
|
|
|
}
|
|
|
let data = null;
|
|
|
- if (item._id && !item.componentDatas) {
|
|
|
- let res: any = await getComponents(item._id);
|
|
|
- item.component = true;
|
|
|
- item.componentDatas = res.componentDatas;
|
|
|
- item.componentData = res.componentData;
|
|
|
- }
|
|
|
- if (!item.data && !item.component && item.image) {
|
|
|
- let target: any = event.target;
|
|
|
- target.children[0] && (target = target.children[0].children[0]);
|
|
|
- // firefox naturalWidth svg 图片 可能是 0
|
|
|
- const normalWidth = target.naturalWidth || target.width;
|
|
|
- const normalHeight = target.naturalHeight || target.height;
|
|
|
- const [name, lockedOnCombine] = isGif(item.image)
|
|
|
- ? ['gif', 0]
|
|
|
- : ['image', undefined];
|
|
|
-
|
|
|
+ if (item['3d']) {
|
|
|
data = {
|
|
|
- name,
|
|
|
- width: 100,
|
|
|
- height: 100 * (normalHeight / normalWidth),
|
|
|
- image: item.image,
|
|
|
- imageRatio: true,
|
|
|
- lockedOnCombine,
|
|
|
+ name: 'iframe',
|
|
|
+ width: 400,
|
|
|
+ height: 300,
|
|
|
+ externElement: true,
|
|
|
+ iframe: 'https://view3d.le5le.com/?id=' + (item._id || item.id),
|
|
|
};
|
|
|
- } else if (item.component) {
|
|
|
- if (item.componentData) {
|
|
|
- const pens = convertPen([item.componentData]);
|
|
|
- data = deepClone(pens);
|
|
|
- } else if (item.componentDatas) {
|
|
|
- data = deepClone(item.componentDatas);
|
|
|
- }
|
|
|
} else {
|
|
|
- data = item.componentDatas || item.data;
|
|
|
+ if (item._id && !item.componentDatas) {
|
|
|
+ let res: any = await getComponents(item._id);
|
|
|
+ item.component = true;
|
|
|
+ item.componentDatas = res.componentDatas;
|
|
|
+ item.componentData = res.componentData;
|
|
|
+ }
|
|
|
+ if (!item.data && !item.component && item.image) {
|
|
|
+ let target: any = event.target;
|
|
|
+ target.children[0] && (target = target.children[0].children[0]);
|
|
|
+ // firefox naturalWidth svg 图片 可能是 0
|
|
|
+ const normalWidth = target.naturalWidth || target.width;
|
|
|
+ const normalHeight = target.naturalHeight || target.height;
|
|
|
+ const [name, lockedOnCombine] = isGif(item.image)
|
|
|
+ ? ['gif', 0]
|
|
|
+ : ['image', undefined];
|
|
|
+
|
|
|
+ data = {
|
|
|
+ name,
|
|
|
+ width: 100,
|
|
|
+ height: 100 * (normalHeight / normalWidth),
|
|
|
+ image: item.image,
|
|
|
+ imageRatio: true,
|
|
|
+ lockedOnCombine,
|
|
|
+ };
|
|
|
+ } else if (item.component) {
|
|
|
+ if (item.componentData) {
|
|
|
+ const pens = convertPen([item.componentData]);
|
|
|
+ data = deepClone(pens);
|
|
|
+ } else if (item.componentDatas) {
|
|
|
+ data = deepClone(item.componentDatas);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ data = item.componentDatas || item.data;
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
if (event instanceof DragEvent) {
|
|
|
meta2d.canvas.addCaches = [];
|
|
|
event.dataTransfer?.setData('Meta2d', JSON.stringify(data));
|
|
@@ -347,6 +470,9 @@ const dragend = (event: any) => {
|
|
|
};
|
|
|
|
|
|
const open = async (item: any) => {
|
|
|
+ if (item.draggable !== false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
autoSave();
|
|
|
router.push({
|
|
|
path: '/',
|
|
@@ -363,30 +489,312 @@ const open = async (item: any) => {
|
|
|
};
|
|
|
|
|
|
const onChangeGroupPanel = async (val: string[]) => {
|
|
|
- if (groupType.value > 0 && groupType.value < 10 && val?.length) {
|
|
|
+ if (val?.length) {
|
|
|
for (const name of val) {
|
|
|
- if (
|
|
|
- !templates[activedGroup.value + name] ||
|
|
|
- !templates[activedGroup.value + name].length
|
|
|
- ) {
|
|
|
- for (const item of subGroups.value) {
|
|
|
- if (item.name === name) {
|
|
|
- item.loading = true;
|
|
|
+ switch (activedGroup.value) {
|
|
|
+ case '场景':
|
|
|
+ if (
|
|
|
+ !caseCaches[activedGroup.value + name] ||
|
|
|
+ !caseCaches[activedGroup.value + name].length
|
|
|
+ ) {
|
|
|
+ for (const item of subGroups.value) {
|
|
|
+ if (item.name === name) {
|
|
|
+ item.loading = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ caseCaches[activedGroup.value + name] = await getCaseProjects(
|
|
|
+ activedGroup.value,
|
|
|
+ name
|
|
|
+ );
|
|
|
+ for (const item of subGroups.value) {
|
|
|
+ if (item.name === name) {
|
|
|
+ item.list = caseCaches[activedGroup.value + name];
|
|
|
+ item.loading = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- templates[activedGroup.value + name] = await getCaseProjects(
|
|
|
- activedGroup.value,
|
|
|
- name
|
|
|
- );
|
|
|
- for (const item of subGroups.value) {
|
|
|
- if (item.name === name) {
|
|
|
- item.list = templates[activedGroup.value + name];
|
|
|
- item.loading = false;
|
|
|
+ break;
|
|
|
+ case '图元':
|
|
|
+ for (const item of subGroups.value) {
|
|
|
+ if (item.name === name && !item.list.length) {
|
|
|
+ item.loading = true;
|
|
|
+ if (item.svg) {
|
|
|
+ item.list = await getIcons(item.folder);
|
|
|
+ } else {
|
|
|
+ item.list = await getFiles(item.folder + item.name);
|
|
|
+ }
|
|
|
+ item.loading = false;
|
|
|
+ }
|
|
|
}
|
|
|
+ break;
|
|
|
+
|
|
|
+ case '我的':
|
|
|
+ for (const item of subGroups.value) {
|
|
|
+ if (!item.list.length) {
|
|
|
+ item.loading = true;
|
|
|
+ if (item.name === '我的组件') {
|
|
|
+ const data = {
|
|
|
+ query: { folder: '' },
|
|
|
+ projection: {
|
|
|
+ image: 1,
|
|
|
+ _id: 1,
|
|
|
+ name: 1,
|
|
|
+ component: 1,
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const config = {
|
|
|
+ params: {
|
|
|
+ current: 1,
|
|
|
+ pageSize: 1000,
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const res: any = await getComponentsList(data, config);
|
|
|
+ if (res?.list) {
|
|
|
+ item.list = res.list;
|
|
|
+ }
|
|
|
+ } else if (item.name === '3D') {
|
|
|
+ const data = {
|
|
|
+ projection: {
|
|
|
+ image: 1,
|
|
|
+ _id: 1,
|
|
|
+ name: 1,
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const config = {
|
|
|
+ params: {
|
|
|
+ current: 1,
|
|
|
+ pageSize: 1000,
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const res: any = await axios.post(
|
|
|
+ '/api/data/le5le3d/list',
|
|
|
+ data,
|
|
|
+ config
|
|
|
+ );
|
|
|
+ if (res?.list) {
|
|
|
+ for (const item of res.list) {
|
|
|
+ item['3d'] = true;
|
|
|
+ }
|
|
|
+ item.list = res.list;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ item.loading = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const editedFolder = ref<any>(undefined);
|
|
|
+const onCreateFolder = () => {
|
|
|
+ editedFolder.value = {
|
|
|
+ _id: '',
|
|
|
+ name: '',
|
|
|
+ label: '新建文件夹',
|
|
|
+ list: [],
|
|
|
+ edited: true,
|
|
|
+ canEdited: true,
|
|
|
+ };
|
|
|
+ subGroups.value.splice(subGroups.value.length - 1, 0, editedFolder.value);
|
|
|
+};
|
|
|
+
|
|
|
+const createFoder = async () => {
|
|
|
+ if (!editedFolder.value.label) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (editedFolder.value.label === editedFolder.value.name) {
|
|
|
+ editedFolder.value.edited = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const found = subGroups.value.findIndex(
|
|
|
+ (group: any) => group.name === editedFolder.value.label
|
|
|
+ );
|
|
|
+ if (found >= 0) {
|
|
|
+ MessagePlugin.error('已经存在相同名称文件夹');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (editedFolder.value._id) {
|
|
|
+ const ret: any = await axios.post('/api/data/folders/update', {
|
|
|
+ _id: editedFolder.value._id,
|
|
|
+ name: editedFolder.value.label,
|
|
|
+ });
|
|
|
+ if (ret) {
|
|
|
+ editedFolder.value.name = editedFolder.value.label;
|
|
|
+ editedFolder.value.edited = false;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const ret: any = await axios.post('/api/data/folders/add', {
|
|
|
+ name: editedFolder.value.label,
|
|
|
+ type: 'le5leV-components',
|
|
|
+ list: [],
|
|
|
+ });
|
|
|
+ if (ret) {
|
|
|
+ editedFolder.value.name = editedFolder.value.label;
|
|
|
+ editedFolder.value._id = ret._id;
|
|
|
+ editedFolder.value.edited = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const onEditHeader = (item: any) => {
|
|
|
+ item.label = item.name;
|
|
|
+ item.edited = true;
|
|
|
+ editedFolder.value = item;
|
|
|
+};
|
|
|
+
|
|
|
+const onKeyHeader = (text: string, event: any) => {
|
|
|
+ if (event.e.key === 'Escape') {
|
|
|
+ editedFolder.value.edited = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 我的组件右键菜单
|
|
|
+const contextmenu = reactive<any>({
|
|
|
+ visible: false,
|
|
|
+ style: {},
|
|
|
+ // 子分类
|
|
|
+ group: undefined,
|
|
|
+ // 组件图纸
|
|
|
+ component: undefined,
|
|
|
+ // 右键二级子菜单
|
|
|
+ subMenus: [],
|
|
|
+});
|
|
|
+const contextmenuDom = ref<any>(null);
|
|
|
+const onContextMenu = async (e: MouseEvent, group: string, item: any) => {
|
|
|
+ e.preventDefault();
|
|
|
+ e.stopPropagation();
|
|
|
+
|
|
|
+ if (activedGroup.value !== '我的' || !item.component) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ contextmenu.group = group;
|
|
|
+ contextmenu.component = item;
|
|
|
+
|
|
|
+ if (document.body.clientHeight - e.clientY < 128) {
|
|
|
+ contextmenu.style = {
|
|
|
+ left: e.clientX + 'px',
|
|
|
+ bottom: '4px',
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ contextmenu.style = {
|
|
|
+ left: e.clientX + 'px',
|
|
|
+ top: e.clientY + 'px',
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ contextmenu.subMenus = [];
|
|
|
+ for (const elem of subGroups.value) {
|
|
|
+ if (elem === group || elem.name === '3D') {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ contextmenu.subMenus.push(elem);
|
|
|
+ }
|
|
|
+ contextmenu.visible = true;
|
|
|
+ setTimeout(() => {
|
|
|
+ if (contextmenuDom.value) {
|
|
|
+ contextmenuDom.value.focus();
|
|
|
+ }
|
|
|
+ }, 500);
|
|
|
+};
|
|
|
+
|
|
|
+const delDialog = reactive<any>({});
|
|
|
+
|
|
|
+const onMenu = async (val: string) => {
|
|
|
+ const id = contextmenu.component._id || contextmenu.component.id;
|
|
|
+
|
|
|
+ switch (val) {
|
|
|
+ case 'edit':
|
|
|
+ autoSave();
|
|
|
+ router.push({
|
|
|
+ path: '/',
|
|
|
+ query: {
|
|
|
+ id,
|
|
|
+ c: 1,
|
|
|
+ r: Date.now() + '',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ break;
|
|
|
+ case 'del':
|
|
|
+ delDialog.show = true;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ if (val.indexOf('move:')) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ val = val.replace('move:', '');
|
|
|
+ const group = contextmenu.subMenus.find(
|
|
|
+ (element: any) => element.name === val
|
|
|
+ );
|
|
|
+ if (!group) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 前端: 添加组件到目标文件夹
|
|
|
+ group.list.push(contextmenu.component);
|
|
|
+ // 前端:从源文件夹移出组件
|
|
|
+ contextmenu.group.list.forEach((item: any, index: number, arr: any[]) => {
|
|
|
+ if (id === item._id || id === item.id) {
|
|
|
+ arr.splice(index, 1);
|
|
|
}
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新后端组件信息
|
|
|
+ let ret = await updateCollection('le5leV-components', {
|
|
|
+ _id: id,
|
|
|
+ folder: val === '我的组件' ? '' : val,
|
|
|
+ });
|
|
|
+ if (!ret) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新后端源文件夹列表
|
|
|
+ if (contextmenu.group.name !== '我的组件') {
|
|
|
+ await axios.post('/api/data/folders/update', {
|
|
|
+ _id: contextmenu.group._id || contextmenu.group.id,
|
|
|
+ list: contextmenu.group.list,
|
|
|
+ });
|
|
|
}
|
|
|
+
|
|
|
+ // 更新后端目标文件夹列表
|
|
|
+ if (group.name !== '我的组件') {
|
|
|
+ await axios.post('/api/data/folders/update', {
|
|
|
+ _id: group._id || group.id,
|
|
|
+ list: group.list,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ contextmenu.visible = false;
|
|
|
+};
|
|
|
+
|
|
|
+const delComponet = async () => {
|
|
|
+ const id = contextmenu.component._id || contextmenu.component.id;
|
|
|
+ await axios.post(`/api/data/le5leV-components/delete`, {
|
|
|
+ id,
|
|
|
+ });
|
|
|
+
|
|
|
+ // 前端:从源文件夹移出组件
|
|
|
+ contextmenu.group.list.forEach((item: any, index: number, arr: any[]) => {
|
|
|
+ if (id === item._id || id === item.id) {
|
|
|
+ arr.splice(index, 1);
|
|
|
}
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新后端源文件夹列表
|
|
|
+ if (contextmenu.group.name !== '我的组件') {
|
|
|
+ await axios.post('/api/data/folders/update', {
|
|
|
+ _id: contextmenu.group._id || contextmenu.group.id,
|
|
|
+ list: contextmenu.group.list,
|
|
|
+ });
|
|
|
}
|
|
|
+
|
|
|
+ delDialog.show = false;
|
|
|
};
|
|
|
|
|
|
onMounted(() => {
|
|
@@ -417,6 +825,7 @@ onUnmounted(() => {
|
|
|
flex-grow: 1;
|
|
|
overflow-y: auto;
|
|
|
font-size: 12px;
|
|
|
+ z-index: 100;
|
|
|
|
|
|
.groups {
|
|
|
& > div {
|
|
@@ -455,6 +864,7 @@ onUnmounted(() => {
|
|
|
}
|
|
|
|
|
|
:deep(.t-collapse) {
|
|
|
+ min-height: 100vh;
|
|
|
border: none;
|
|
|
}
|
|
|
:deep(.t-collapse-panel__header) {
|
|
@@ -475,19 +885,42 @@ onUnmounted(() => {
|
|
|
padding: 4px 4px 20px 4px;
|
|
|
display: grid;
|
|
|
grid-template-columns: 1fr 1fr 1fr;
|
|
|
- grid-row-gap: 20px;
|
|
|
+ grid-row-gap: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.t-loading--center) {
|
|
|
+ width: 100px;
|
|
|
+ .t-loading__text {
|
|
|
+ margin-left: 8px;
|
|
|
+ height: 24px;
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ :deep(.t-image__error) {
|
|
|
+ .t-space-item:last-child {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.t-image__loading) {
|
|
|
+ .t-space-item:last-child {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
.graphic {
|
|
|
position: relative;
|
|
|
+ padding: 10px 0;
|
|
|
+ border-radius: 2px;
|
|
|
+ border: 1px solid transparent;
|
|
|
|
|
|
&:hover {
|
|
|
cursor: pointer;
|
|
|
- color: var(--color-primary-hover);
|
|
|
- svg {
|
|
|
- color: var(--color-primary-hover);
|
|
|
- }
|
|
|
+ border-color: var(--color-primary);
|
|
|
}
|
|
|
p {
|
|
|
+ margin-top: 10px;
|
|
|
+ padding: 0 10px;
|
|
|
text-align: center;
|
|
|
font-size: 12px;
|
|
|
height: 12px;
|
|
@@ -502,10 +935,7 @@ onUnmounted(() => {
|
|
|
.t-image__wrapper {
|
|
|
height: 32px;
|
|
|
width: 32px;
|
|
|
- background: #fff0;
|
|
|
- margin-left: calc(50% - 16px);
|
|
|
- margin-top: 10px;
|
|
|
- margin-bottom: 10px;
|
|
|
+ margin: auto;
|
|
|
:deep(.t-image) {
|
|
|
border-radius: 2px;
|
|
|
}
|
|
@@ -515,7 +945,7 @@ onUnmounted(() => {
|
|
|
color: var(--color);
|
|
|
height: 32px;
|
|
|
width: 100%;
|
|
|
- margin: 4px 0px;
|
|
|
+ margin: auto;
|
|
|
}
|
|
|
|
|
|
.svg-box {
|
|
@@ -549,23 +979,42 @@ onUnmounted(() => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- .two-list {
|
|
|
+ .two-columns {
|
|
|
:deep(.t-collapse-panel__content) {
|
|
|
- padding: 0 8px;
|
|
|
- grid-template-columns: 116px 116px;
|
|
|
+ grid-template-columns: 1fr 1fr;
|
|
|
}
|
|
|
.graphic {
|
|
|
- p {
|
|
|
- margin-top: 10px;
|
|
|
- margin-bottom: 12px;
|
|
|
- }
|
|
|
.t-image__wrapper {
|
|
|
- width: 88px;
|
|
|
+ width: 100px;
|
|
|
height: 88px;
|
|
|
- margin-left: 14px;
|
|
|
+ background-color: var(--color-background);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ .context-menu-box {
|
|
|
+ position: fixed;
|
|
|
+ z-index: 200;
|
|
|
+ & > div {
|
|
|
+ width: 140px !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.t-menu) {
|
|
|
+ .t-menu__item {
|
|
|
+ &.t-is-opened {
|
|
|
+ background-color: var(--color-background-popup-hover);
|
|
|
+ transition: none !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .t-fake-arrow {
|
|
|
+ transform: rotate(-90deg) !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ .t-fake-arrow--active {
|
|
|
+ transform: rotate(90deg) !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|