|
@@ -18,6 +18,9 @@
|
|
|
</div>
|
|
|
</div>
|
|
|
<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"
|
|
@@ -25,10 +28,41 @@
|
|
|
>
|
|
|
<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" @click.stop>
|
|
|
+ <t-input
|
|
|
+ v-if="item.edited"
|
|
|
+ v-model="item.label"
|
|
|
+ style="width: 140px"
|
|
|
+ @blur="createFoder"
|
|
|
+ @enter="createFoder"
|
|
|
+ @keyup="onKeyHeader"
|
|
|
+ />
|
|
|
+ <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="加载中..."
|
|
@@ -48,7 +82,12 @@
|
|
|
@dblclick.stop="open(elem)"
|
|
|
:title="elem.draggable === false ? '双击打开' : '拖拽到画布'"
|
|
|
>
|
|
|
- <t-image v-if="elem.image" :src="elem.image" :lazy="true" />
|
|
|
+ <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>
|
|
@@ -68,7 +107,39 @@
|
|
|
</template>
|
|
|
</t-collapse-panel>
|
|
|
</t-collapse>
|
|
|
- <template v-else></template>
|
|
|
+ <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>
|
|
@@ -86,6 +157,7 @@ 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();
|
|
|
|
|
@@ -108,11 +180,6 @@ const groups = reactive([
|
|
|
name: '图表',
|
|
|
key: 'chart',
|
|
|
},
|
|
|
- {
|
|
|
- icon: 'relativity',
|
|
|
- name: '控件',
|
|
|
- key: '',
|
|
|
- },
|
|
|
{
|
|
|
icon: 'image',
|
|
|
name: '素材',
|
|
@@ -123,6 +190,11 @@ const groups = reactive([
|
|
|
name: '图元',
|
|
|
key: '',
|
|
|
},
|
|
|
+ {
|
|
|
+ icon: 'relativity',
|
|
|
+ name: '控件',
|
|
|
+ key: '',
|
|
|
+ },
|
|
|
{
|
|
|
icon: 'chart-bubble',
|
|
|
name: '图形',
|
|
@@ -165,6 +237,7 @@ const groupChange = async (name: string) => {
|
|
|
case '模板':
|
|
|
groupType.value = 2;
|
|
|
if (!templates.value.length) {
|
|
|
+ templates.value = await getCaseProjects(name);
|
|
|
}
|
|
|
subGroups.value = templates.value;
|
|
|
break;
|
|
@@ -177,6 +250,7 @@ const groupChange = async (name: string) => {
|
|
|
case '素材':
|
|
|
groupType.value = 2;
|
|
|
if (!materials.value.length) {
|
|
|
+ materials.value = await getFiles('material/');
|
|
|
}
|
|
|
subGroups.value = materials.value;
|
|
|
break;
|
|
@@ -196,18 +270,23 @@ const groupChange = async (name: string) => {
|
|
|
subGroups.value = shapes;
|
|
|
break;
|
|
|
case '我的':
|
|
|
- subGroups.value = await getPrivateCommponents();
|
|
|
+ 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 },
|
|
|
}
|
|
@@ -222,40 +301,48 @@ const getCaseProjects = async (name: string, group: string) => {
|
|
|
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`,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ 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;
|
|
|
};
|
|
|
|
|
@@ -327,6 +414,9 @@ const dragend = (event: any) => {
|
|
|
};
|
|
|
|
|
|
const open = async (item: any) => {
|
|
|
+ if (item.draggable !== false) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
autoSave();
|
|
|
router.push({
|
|
|
path: '/',
|
|
@@ -375,18 +465,133 @@ const onChangeGroupPanel = async (val: string[]) => {
|
|
|
if (item.svg) {
|
|
|
item.list = await getIcons(item.folder);
|
|
|
} else {
|
|
|
- console.log(item.folder + item.name);
|
|
|
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) {
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
onMounted(() => {
|
|
|
groupChange('场景');
|
|
|
document.addEventListener('dragstart', dragstart, false);
|
|
@@ -474,7 +679,7 @@ 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) {
|
|
@@ -484,17 +689,32 @@ onUnmounted(() => {
|
|
|
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;
|
|
@@ -509,10 +729,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;
|
|
|
}
|
|
@@ -522,7 +739,7 @@ onUnmounted(() => {
|
|
|
color: var(--color);
|
|
|
height: 32px;
|
|
|
width: 100%;
|
|
|
- margin: 4px 0px;
|
|
|
+ margin: auto;
|
|
|
}
|
|
|
|
|
|
.svg-box {
|
|
@@ -558,18 +775,13 @@ onUnmounted(() => {
|
|
|
|
|
|
.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);
|
|
|
}
|
|
|
}
|
|
|
}
|