|
@@ -45,17 +45,14 @@
|
|
|
<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="activedPanels[activedGroup]"
|
|
|
- >
|
|
|
+ <t-collapse v-model="activedPanels[activedGroup]">
|
|
|
<t-collapse-panel
|
|
|
:value="item.name"
|
|
|
v-for="item in subGroups"
|
|
|
:key="item.name"
|
|
|
>
|
|
|
<template #header>
|
|
|
- <div class="flex middle mr-8">
|
|
|
+ <div class="flex middle">
|
|
|
<div v-if="item.edited" @click.stop>
|
|
|
<t-input
|
|
|
v-model="item.label"
|
|
@@ -63,28 +60,45 @@
|
|
|
@blur="createFoder"
|
|
|
@enter="createFoder"
|
|
|
@keyup="onKeyHeader"
|
|
|
+ :autofocus="true"
|
|
|
/>
|
|
|
</div>
|
|
|
- <div v-else class="ellipsis">
|
|
|
+ <div v-else>
|
|
|
{{ 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=""
|
|
|
+ <template #headerRightContent>
|
|
|
+ <t-space size="small" @click.stop tabindex="0">
|
|
|
+ <t-upload
|
|
|
+ v-if="item.canEdited || item.name === '我的组件'"
|
|
|
+ action="/api/image/upload"
|
|
|
+ accept="image/*"
|
|
|
+ :headers="headers"
|
|
|
+ :data="updataData"
|
|
|
+ :auto-upload="true"
|
|
|
+ :upload-all-files-in-one-request="false"
|
|
|
+ @selectChange="onSelectFiles(item)"
|
|
|
+ @success="fileSuccessed"
|
|
|
+ theme="custom"
|
|
|
>
|
|
|
- <t-icon name="delete" class="hover" />
|
|
|
- </t-popconfirm>
|
|
|
+ <t-icon name="image" class="hover" />
|
|
|
+ </t-upload>
|
|
|
+
|
|
|
+ <template v-if="item.canEdited">
|
|
|
+ <t-icon
|
|
|
+ name="edit"
|
|
|
+ class="hover"
|
|
|
+ @click="onEditHeader(item)"
|
|
|
+ />
|
|
|
+ <t-popconfirm
|
|
|
+ content="确认删除该文件夹吗"
|
|
|
+ placement="left"
|
|
|
+ @confirm="delFolder(item)"
|
|
|
+ >
|
|
|
+ <t-icon name="delete" class="hover" />
|
|
|
+ </t-popconfirm>
|
|
|
+ </template>
|
|
|
</t-space>
|
|
|
</template>
|
|
|
|
|
@@ -124,41 +138,6 @@
|
|
|
</div>
|
|
|
</t-collapse-panel>
|
|
|
</t-collapse>
|
|
|
- <div v-else class="t-collapse-panel__content" style="padding: 8px">
|
|
|
- <template v-for="elem in subGroups">
|
|
|
- <div
|
|
|
- class="graphic"
|
|
|
- :draggable="true"
|
|
|
- v-show="elem.visible !== false"
|
|
|
- @dragstart="dragStart($event, elem)"
|
|
|
- @click.stop="dragStart($event, elem)"
|
|
|
- @dblclick.stop="open(elem)"
|
|
|
- >
|
|
|
- <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>
|
|
|
- </template>
|
|
|
-
|
|
|
- <div
|
|
|
- v-if="!subGroups.length"
|
|
|
- class="gray center"
|
|
|
- style="white-space: nowrap; margin-left: 32px"
|
|
|
- >
|
|
|
- 暂无数据
|
|
|
- </div>
|
|
|
- </div>
|
|
|
</template>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -166,17 +145,17 @@
|
|
|
<div
|
|
|
class="context-menu-box"
|
|
|
ref="contextmenuDom"
|
|
|
- v-if="contextmenu.visible"
|
|
|
+ v-show="contextmenu.visible"
|
|
|
tabindex="0"
|
|
|
:style="contextmenu.style"
|
|
|
- @blur="contextmenu.visible = false"
|
|
|
+ @blur="hideContextmenu"
|
|
|
>
|
|
|
<t-menu class="context-menu" @change="onMenu" expandType="popup">
|
|
|
<t-submenu
|
|
|
value="move"
|
|
|
title="移动到"
|
|
|
v-if="contextmenu.subMenus.length"
|
|
|
- :disabled="!contextmenu.component.component"
|
|
|
+ :disabled="contextmenu.component['3d']"
|
|
|
>
|
|
|
<t-menu-item
|
|
|
v-for="subMenu in contextmenu.subMenus"
|
|
@@ -185,8 +164,10 @@
|
|
|
{{ subMenu.name }}
|
|
|
</t-menu-item>
|
|
|
</t-submenu>
|
|
|
- <t-menu-item value="edit"> 编辑 </t-menu-item>
|
|
|
- <t-menu-item value="del" :disabled="!contextmenu.component.component">
|
|
|
+ <t-menu-item value="edit" :disabled="!contextmenu.component.component">
|
|
|
+ 编辑
|
|
|
+ </t-menu-item>
|
|
|
+ <t-menu-item value="del" :disabled="contextmenu.component['3d']">
|
|
|
删除
|
|
|
</t-menu-item>
|
|
|
</t-menu>
|
|
@@ -247,23 +228,30 @@
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-import { onMounted, onUnmounted, reactive, ref, shallowRef } from 'vue';
|
|
|
+import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
|
|
import { useRouter } from 'vue-router';
|
|
|
import { MessagePlugin } from 'tdesign-vue-next';
|
|
|
import axios from 'axios';
|
|
|
+import { deepClone } from '@meta2d/core';
|
|
|
|
|
|
-import { cases, shapes, formComponents } from '@/services/defaults';
|
|
|
+import { cases, shapes, formComponents, templates } from '@/services/defaults';
|
|
|
import { charts } from '@/services/echarts';
|
|
|
import { getFolders, makeSvg } from '@/services/png';
|
|
|
-import { getComponentsList, getLe5leV, updateCollection } from '@/services/api';
|
|
|
+import {
|
|
|
+ addCollection,
|
|
|
+ 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 { debounce } from '@/services/debouce';
|
|
|
+import { debounce, throttle } from '@/services/debouce';
|
|
|
import { searchObjectPinyin } from '@/services/pinyin';
|
|
|
+import { getCookie } from '@/services/cookie';
|
|
|
|
|
|
import WechatPay from './WechatPay.vue';
|
|
|
+import { filename } from '@/services/file';
|
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
@@ -312,14 +300,13 @@ const groups = reactive([
|
|
|
key: '',
|
|
|
},
|
|
|
]);
|
|
|
-const subGroups = shallowRef<any[]>([]);
|
|
|
+const subGroups = ref<any[]>([]);
|
|
|
const groupType = ref(0);
|
|
|
const activedPanels = reactive<any>({});
|
|
|
|
|
|
const caseCaches = [];
|
|
|
-const templates = [];
|
|
|
+const templateCaches = [];
|
|
|
const materials = [];
|
|
|
-// 数量太大,禁用响应式
|
|
|
const pngs = [];
|
|
|
|
|
|
let dropped = false;
|
|
@@ -332,6 +319,11 @@ const search = ref('');
|
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
|
+const headers = {
|
|
|
+ Authorization: 'Bearer ' + (localStorage.token || getCookie('token') || ''),
|
|
|
+};
|
|
|
+const updataData = { directory: '/项目' };
|
|
|
+
|
|
|
const groupChange = async (name: string) => {
|
|
|
activedGroup.value = name;
|
|
|
groupType.value = 0;
|
|
@@ -356,10 +348,21 @@ const groupChange = async (name: string) => {
|
|
|
|
|
|
break;
|
|
|
case '模板':
|
|
|
- if (!templates.length) {
|
|
|
- templates.push(...(await getCaseProjects(name)));
|
|
|
+ if (!templateCaches.length) {
|
|
|
+ loading.value = true;
|
|
|
+ templateCaches.push(...(await getCaseProjects(name)));
|
|
|
+ for (const group of templates) {
|
|
|
+ group.list = [];
|
|
|
+ for (const item of templateCaches) {
|
|
|
+ if (item.case === group.name) {
|
|
|
+ group.list.push(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ loading.value = false;
|
|
|
}
|
|
|
- groupType.value = 2;
|
|
|
+
|
|
|
+ groupType.value = 1;
|
|
|
subGroups.value = templates;
|
|
|
break;
|
|
|
case '图表':
|
|
@@ -369,7 +372,7 @@ const groupChange = async (name: string) => {
|
|
|
subGroups.value = formComponents;
|
|
|
break;
|
|
|
case '素材':
|
|
|
- groupType.value = 2;
|
|
|
+ groupType.value = 1;
|
|
|
if (!materials.length) {
|
|
|
materials.push(...(await getFolders('material')));
|
|
|
}
|
|
@@ -652,6 +655,11 @@ const getPrivateGraphics = async () => {
|
|
|
|
|
|
const editedFolder = ref<any>(undefined);
|
|
|
const onCreateFolder = () => {
|
|
|
+ activedPanels[activedGroup.value].splice(
|
|
|
+ 0,
|
|
|
+ activedPanels[activedGroup.value].length
|
|
|
+ );
|
|
|
+
|
|
|
editedFolder.value = {
|
|
|
_id: '',
|
|
|
name: '',
|
|
@@ -721,7 +729,7 @@ const contextmenu = reactive<any>({
|
|
|
// 子分类
|
|
|
group: undefined,
|
|
|
// 组件图纸
|
|
|
- component: undefined,
|
|
|
+ component: {},
|
|
|
// 右键二级子菜单
|
|
|
subMenus: [],
|
|
|
});
|
|
@@ -768,7 +776,11 @@ const delDialog = reactive<any>({});
|
|
|
|
|
|
const onMenu = async (val: string) => {
|
|
|
const id = contextmenu.component._id || contextmenu.component.id;
|
|
|
-
|
|
|
+ setTimeout(() => {
|
|
|
+ contextmenu.group = '';
|
|
|
+ contextmenu.component = {};
|
|
|
+ contextmenu.subMenus = [];
|
|
|
+ }, 500);
|
|
|
switch (val) {
|
|
|
case 'edit':
|
|
|
if (contextmenu.component.component) {
|
|
@@ -840,7 +852,9 @@ const onMenu = async (val: string) => {
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
+};
|
|
|
|
|
|
+const hideContextmenu = () => {
|
|
|
contextmenu.visible = false;
|
|
|
};
|
|
|
|
|
@@ -909,155 +923,25 @@ const onSearch = () => {
|
|
|
};
|
|
|
|
|
|
const searchGraphics = async () => {
|
|
|
- switch (activedGroup.value) {
|
|
|
- case '场景':
|
|
|
- subGroups.value = [];
|
|
|
- if (search.value) {
|
|
|
- activedPanels[activedGroup.value] = [];
|
|
|
- for (const item of cases) {
|
|
|
- activedPanels[activedGroup.value].push(item.name);
|
|
|
- }
|
|
|
- }
|
|
|
- for (const item of caseCaches) {
|
|
|
- if (search.value) {
|
|
|
- item.visible = searchObjectPinyin(item, 'name', search.value);
|
|
|
- } else {
|
|
|
- item.visible = true;
|
|
|
- }
|
|
|
- }
|
|
|
- setTimeout(() => {
|
|
|
- subGroups.value = cases;
|
|
|
- });
|
|
|
- break;
|
|
|
- case '模板':
|
|
|
- subGroups.value = [];
|
|
|
- for (const item of templates) {
|
|
|
- if (search.value) {
|
|
|
- item.visible = searchObjectPinyin(item, 'name', search.value);
|
|
|
- } else {
|
|
|
- item.visible = true;
|
|
|
- }
|
|
|
- }
|
|
|
- setTimeout(() => {
|
|
|
- subGroups.value = templates;
|
|
|
- });
|
|
|
- break;
|
|
|
- case '图表':
|
|
|
- subGroups.value = [];
|
|
|
- if (search.value) {
|
|
|
- activedPanels[activedGroup.value] = [];
|
|
|
- }
|
|
|
- for (const group of charts) {
|
|
|
- if (search.value) {
|
|
|
- activedPanels[activedGroup.value].push(group.name);
|
|
|
- }
|
|
|
- for (const item of group.list) {
|
|
|
- if (search.value) {
|
|
|
- // @ts-ignore
|
|
|
- item.visible = searchObjectPinyin(item, 'name', search.value);
|
|
|
- } else {
|
|
|
- // @ts-ignore
|
|
|
- item.visible = true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- setTimeout(() => {
|
|
|
- subGroups.value = charts;
|
|
|
- });
|
|
|
- break;
|
|
|
- case '控件':
|
|
|
- subGroups.value = [];
|
|
|
- if (search.value) {
|
|
|
- activedPanels[activedGroup.value] = [];
|
|
|
- }
|
|
|
- for (const group of formComponents) {
|
|
|
- if (search.value) {
|
|
|
- activedPanels[activedGroup.value].push(group.name);
|
|
|
- }
|
|
|
- for (const item of group.list) {
|
|
|
- if (search.value) {
|
|
|
- // @ts-ignore
|
|
|
- item.visible = searchObjectPinyin(item, 'name', search.value);
|
|
|
- } else {
|
|
|
- // @ts-ignore
|
|
|
- item.visible = true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- setTimeout(() => {
|
|
|
- subGroups.value = formComponents;
|
|
|
- });
|
|
|
+ if (search.value) {
|
|
|
+ activedPanels[activedGroup.value].splice(
|
|
|
+ 0,
|
|
|
+ activedPanels[activedGroup.value].length
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
- break;
|
|
|
- case '素材':
|
|
|
- subGroups.value = [];
|
|
|
- for (const item of materials) {
|
|
|
- if (search.value) {
|
|
|
- item.visible = searchObjectPinyin(item, 'name', search.value);
|
|
|
- } else {
|
|
|
- item.visible = true;
|
|
|
- }
|
|
|
- }
|
|
|
- setTimeout(() => {
|
|
|
- subGroups.value = materials;
|
|
|
- });
|
|
|
- break;
|
|
|
- case '图元':
|
|
|
+ for (const group of subGroups.value) {
|
|
|
+ for (const item of group.list) {
|
|
|
if (search.value) {
|
|
|
- activedPanels[activedGroup.value] = [];
|
|
|
- for (const group of subGroups.value) {
|
|
|
- activedPanels[activedGroup.value].push(group.name);
|
|
|
- }
|
|
|
- }
|
|
|
- subGroups.value = [];
|
|
|
- for (const item of pngs) {
|
|
|
- for (const icon of item.list) {
|
|
|
- if (search.value) {
|
|
|
- icon.visible = searchObjectPinyin(icon, 'name', search.value);
|
|
|
- } else {
|
|
|
- icon.visible = true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- setTimeout(() => {
|
|
|
- subGroups.value = pngs;
|
|
|
- });
|
|
|
- break;
|
|
|
- case '图形':
|
|
|
- subGroups.value = [];
|
|
|
- if (search.value) {
|
|
|
- activedPanels[activedGroup.value] = [];
|
|
|
- }
|
|
|
- for (const group of shapes) {
|
|
|
- if (search.value) {
|
|
|
- activedPanels[activedGroup.value].push(group.name);
|
|
|
- }
|
|
|
- for (const item of group.list) {
|
|
|
- if (search.value) {
|
|
|
- // @ts-ignore
|
|
|
- item.visible = searchObjectPinyin(item, 'name', search.value);
|
|
|
- } else {
|
|
|
- // @ts-ignore
|
|
|
- item.visible = true;
|
|
|
- }
|
|
|
- }
|
|
|
+ item.visible = searchObjectPinyin(item, 'name', search.value);
|
|
|
+ } else {
|
|
|
+ item.visible = true;
|
|
|
}
|
|
|
- setTimeout(() => {
|
|
|
- subGroups.value = shapes;
|
|
|
- });
|
|
|
+ }
|
|
|
|
|
|
- break;
|
|
|
- case '我的':
|
|
|
- for (const item of subGroups.value) {
|
|
|
- for (const elem of item.list) {
|
|
|
- if (search.value) {
|
|
|
- elem.visible = searchObjectPinyin(elem, 'name', search.value);
|
|
|
- } else {
|
|
|
- elem.visible = true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- break;
|
|
|
+ if (search.value) {
|
|
|
+ activedPanels[activedGroup.value].push(group.name);
|
|
|
+ }
|
|
|
}
|
|
|
};
|
|
|
|
|
@@ -1079,6 +963,58 @@ const onFold = () => {
|
|
|
const loadImage = (elem: any) => {
|
|
|
if (elem.isSvg) {
|
|
|
makeSvg(elem);
|
|
|
+ if (activedGroup.value === '图元') {
|
|
|
+ throttle(renderPngGroup, 100);
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const renderPngGroup = () => {
|
|
|
+ subGroups.value = pngs;
|
|
|
+};
|
|
|
+
|
|
|
+let uploadGroup: any;
|
|
|
+const onSelectFiles = (item: any) => {
|
|
|
+ uploadGroup = item;
|
|
|
+};
|
|
|
+
|
|
|
+const fileSuccessed = async (content: any) => {
|
|
|
+ const c: any = {
|
|
|
+ name: filename(content.file.name),
|
|
|
+ image: content.response.url,
|
|
|
+ folder: uploadGroup.name === '我的组件' ? '' : uploadGroup.name,
|
|
|
+ };
|
|
|
+
|
|
|
+ const ret: any = await addCollection('le5leV-components', c);
|
|
|
+
|
|
|
+ if (ret && uploadGroup.name !== '我的组件') {
|
|
|
+ c._id = ret._id || ret.id;
|
|
|
+ if (!uploadGroup.list) {
|
|
|
+ uploadGroup.list = [];
|
|
|
+ }
|
|
|
+ uploadGroup.list.push(c);
|
|
|
+ await axios.post('/api/data/folders/update', {
|
|
|
+ _id: uploadGroup._id || uploadGroup.id,
|
|
|
+ list: uploadGroup.list,
|
|
|
+ });
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const delFolder = async (item: any) => {
|
|
|
+ if (item.list?.length) {
|
|
|
+ MessagePlugin.error('文件夹不为空!');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const id = item._id || item.id;
|
|
|
+ const ret: any = await axios.post('/api/data/folders/delete', {
|
|
|
+ id,
|
|
|
+ });
|
|
|
+ if (ret) {
|
|
|
+ const i = subGroups.value.findIndex(
|
|
|
+ (elem: any) => id === elem._id || id === elem.id
|
|
|
+ );
|
|
|
+ i >= 0 && subGroups.value.splice(i, 1);
|
|
|
}
|
|
|
};
|
|
|
|
|
@@ -1158,17 +1094,38 @@ onUnmounted(() => {
|
|
|
min-height: 100vh;
|
|
|
border: none;
|
|
|
}
|
|
|
+
|
|
|
:deep(.t-collapse-panel__header) {
|
|
|
border: none;
|
|
|
font-size: 12px;
|
|
|
font-weight: 400;
|
|
|
- padding: 8px 16px;
|
|
|
- &:hover {
|
|
|
+ padding: 8px 8px 8px 16px;
|
|
|
+
|
|
|
+ & > .t-space {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ & > .t-space:focus {
|
|
|
+ display: inline-flex;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:hover .t-collapse-panel__icon,
|
|
|
+ &:hover > .flex {
|
|
|
color: var(--color-primary);
|
|
|
}
|
|
|
|
|
|
- .ellipsis {
|
|
|
- width: 200px;
|
|
|
+ .t-collapse-panel__icon,
|
|
|
+ .t-collapse-panel__icon * {
|
|
|
+ transition: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .t-icon {
|
|
|
+ font-size: 13px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ :deep(.t-collapse-panel__wrapper:hover) {
|
|
|
+ .t-collapse-panel__header > .t-space {
|
|
|
+ display: inline-flex;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1296,6 +1253,7 @@ onUnmounted(() => {
|
|
|
z-index: 200;
|
|
|
& > div {
|
|
|
width: 140px !important;
|
|
|
+ position: static;
|
|
|
}
|
|
|
|
|
|
:deep(.t-menu) {
|