瀏覽代碼

feat:user-模板&方案

ananzhusen 1 年之前
父節點
當前提交
e49ad3e664

+ 12 - 0
src/services/api.ts

@@ -44,6 +44,14 @@ export async function updateCollection(collection: string, data: any) {
   return await axios.post(`/api/data/${collection}/update`, data);
 }
 
+export async function getCollectionList(
+  collection: string,
+  data: any,
+  config: any
+) {
+  return await axios.post(`/api/data/${collection}/list`, data, config);
+}
+
 export async function getLe5leV(id: string) {
   return await axios.post('/api/data/le5leV/get', {
     id,
@@ -65,3 +73,7 @@ export async function getLe5le3d(id: string) {
     id,
   });
 }
+
+export async function getProjectsList(data: any, config: any) {
+  return await axios.post('/api/data/le5leV/list', data, config);
+}

+ 40 - 5
src/services/common.ts

@@ -11,6 +11,7 @@ import { upload, dataURLtoBlob } from '@/services/file';
 import { delImage, addCollection, updateCollection } from '@/services/api';
 import { baseVer } from '@/services/upgrade';
 import { debounce } from './debouce';
+import { deepClone } from '@meta2d/core';
 
 const assets = reactive({
   home: 'https://le5le.com',
@@ -97,7 +98,7 @@ export enum SaveType {
 }
 export const save = async (
   type: SaveType = SaveType.Save,
-  component?: boolean,
+  vType?: string,
   notice?: boolean
 ) => {
   meta2d.stopAnimate();
@@ -109,7 +110,7 @@ export const save = async (
   }
   let componentActive = false;
   if (
-    component &&
+    vType === 'component' &&
     meta2d.store.active &&
     meta2d.store.active.length === 1 &&
     meta2d.store.active[0].name === 'combine'
@@ -201,7 +202,7 @@ export const save = async (
     (meta2d.store.data as Meta2dBackData).image = data.image;
   }
 
-  if (data.component || component) {
+  if (data.component || vType === 'component') {
     data.component = true;
     // pens 存储原数据用于二次编辑 ; componentDatas 组合后的数据,用于复用
     data.componentDatas = meta2d.toComponent(
@@ -221,8 +222,9 @@ export const save = async (
   }
   !data.version && (data.version = baseVer);
   if (!data.folder) {
-    data.folder = '';
+    data.folder = folder.name;
   }
+  let _temType = '';
   if (type === SaveType.SaveAs) {
     // 另存为一定走 新增 ,由于后端 未控制 userId 等属性,清空一下
     for (const k of delAttrs) {
@@ -238,13 +240,27 @@ export const save = async (
       ret = await updateCollection(collection, data);
     } else {
       ret = await addCollection(collection, data); // 新增
+      _temType = 'add';
     }
   }
 
   if (ret.error) {
     return;
   }
-
+  if (_temType === 'add') {
+    //文件夹
+    if (folder._id) {
+      folder.list.push({
+        image: data.image,
+        name: data.name,
+        _id: ret._id,
+      });
+      await axios.post('/api/data/folders/update', {
+        _id: folder._id,
+        list: folder.list,
+      });
+    }
+  }
   //  保存图纸之后的钩子函数
   globalThis.afterSaveMeta2d && (await globalThis.afterSaveMeta2d(ret));
   if (
@@ -558,3 +574,22 @@ export const typeOptions = [
     value: 'array',
   },
 ];
+
+let folder = reactive<any>({
+  _id: '',
+});
+
+export const useFolder = () => {
+  const getFolder = async () => {
+    return dot;
+  };
+  const setFolder = async (value: any) => {
+    folder = deepClone(value);
+  };
+
+  return {
+    dot,
+    getFolder,
+    setFolder,
+  };
+};

+ 19 - 7
src/services/defaults.ts

@@ -254,6 +254,17 @@ export const iframeCustom = [
     max: 1,
     placeholder: '范围0-1',
   },
+  {
+    key: 'blur',
+    label: '背景模糊',
+    type: 'number',
+    min: 0,
+  },
+  {
+    key: 'blurBackground',
+    label: '毛玻璃颜色',
+    type: 'color',
+  },
 ];
 
 export const shapes = [
@@ -2363,32 +2374,33 @@ export const formComponents = [
           width: 500,
           height: 40,
           hiddenText: true,
-          direction: 'vertical',//horizontal/vertical
+          direction: 'vertical', //horizontal/vertical
           labelAlign: '', //left/right/alternate/top/bottom
           // theme: 'dot', //dot
           current: 2,
           data: [
             {
               title: '已完成的步骤',
-              content: '这里是提示文字dsafsdfadfafadsfdfadfadsfdafdafdasfsdfdfadfdafasdffdfadds',
-              status:'finish' //default/process/finish/error
+              content:
+                '这里是提示文字dsafsdfadfafadsfdfadfadsfdafdafdasfsdfdfadfdafasdffdfadds',
+              status: 'finish', //default/process/finish/error
               // path: 'M71.3,61.9v2.21L56.66,66V63.82l3.27-.43.25-1-2.39.31V60.84l2.84-.37.21-.87-3.4.44V57.85l13.08-1.73v2.2l-6.85.9-.21.88,6-.79-.15,2.85ZM58.11,66.56,69.86,65v5.63L58.11,72.19Zm2.59,3.09,6.44-.84V67.52l-6.44.85ZM62.78,63l3.87-.51.07-.95L63,62.05Z',
             },
             {
               title: '出错的步骤',
               content: '这里是提示文字ssss',
-              status:'error' //default/process/finish/error
+              status: 'error', //default/process/finish/error
             },
             {
               title: '进行中的步骤',
               content: '这里是提示文字',
-              status:'process' //default/process/finish/error
+              status: 'process', //default/process/finish/error
             },
             {
               title: '未完成的步骤',
               content: '这里是提示文字',
-              status:'default' //default/process/finish/error
-            }
+              status: 'default', //default/process/finish/error
+            },
           ],
         },
       },

+ 2 - 2
src/styles/tdesign.css

@@ -385,8 +385,8 @@
 
     &.t-is-checked {
       border-color: var(--color-border-input);
-      /* background-color: var(--color-border-input); */
-      /* color: var(--td-text-color-primary) !important; */
+      background-color: var(--color-border-input);
+      color: var(--td-text-color-primary) !important;
     }
   }
 }

+ 1 - 1
src/styles/var.css

@@ -63,7 +63,7 @@
     var(--td-font-family);
   --td-border-level-1-color: var(--color-background-input);
   --td-text-color-placeholder: var(--color-desc);
-  --td-brand-color: var(--color-primary-hover);
+  --td-brand-color: var(--color-primary);
   --td-bg-color-component: var(--color-border-input);
   --td-brand-color-light: var(--color-border-input);
   --td-mask-disabled: var(--color-background);

+ 204 - 57
src/views/components/Graphics.vue

@@ -49,7 +49,12 @@
         </div>
         <template v-else>
           <div
-            v-if="activedGroup === '组件' || activedGroup === '图片'"
+            v-if="
+              activedGroup === '组件' ||
+              activedGroup === '图片' ||
+              (activeAssets === 'user' &&
+                (activedGroup === '方案' || activedGroup === '模板'))
+            "
             class="px-16 mt-12 mb-8 ml-4"
           >
             <a @click="onCreateFolder">+ 新建文件夹</a>
@@ -81,8 +86,8 @@
                 <t-space size="small" @click.stop tabindex="0">
                   <t-upload
                     v-if="
-                      item.canEdited ||
-                      (activedGroup === '组件' && item.name === '我的组件')
+                      item.canEdited &&
+                      (activedGroup === '组件' || activedGroup === '图片')
                     "
                     action="/api/image/upload"
                     :accept="activedGroup === '组件' ? '.svg' : 'image/*'"
@@ -103,8 +108,13 @@
                   >
                     <t-icon name="image" class="hover" />
                   </t-upload>
-
                   <template v-if="item.canEdited">
+                    <t-icon
+                      v-if="activedGroup === '方案' || activedGroup === '模板'"
+                      name="add"
+                      class="hover"
+                      @click="onAdd(item)"
+                    />
                     <t-icon
                       name="edit"
                       class="hover"
@@ -267,10 +277,11 @@ import {
   getComponentsList,
   getLe5leV,
   updateCollection,
+  getCollectionList,
 } from '@/services/api';
 import { convertPen } from '@/services/upgrade';
 import { isGif } from '@/services/utils';
-import { autoSave, delAttrs } from '@/services/common';
+import { autoSave, delAttrs, blank, useFolder } from '@/services/common';
 import { debounce, throttle } from '@/services/debouce';
 import { searchObjectPinyin } from '@/services/pinyin';
 import { getCookie } from '@/services/cookie';
@@ -281,6 +292,7 @@ import { useUser } from '@/services/user';
 import { iframeCustom } from '@/services/defaults';
 
 const { user } = useUser();
+const { setFolder } = useFolder();
 const router = useRouter();
 
 const activedGroup = ref('');
@@ -365,8 +377,6 @@ const activedPanels = reactive<any>({});
 
 const caseCaches = [];
 const templateCaches = [];
-const userCaseCaches = [];
-const userTemplateCaches = [];
 const materials = [];
 const pngs = [];
 
@@ -421,22 +431,9 @@ const groupChange = async (name: string) => {
         subGroups.value = cases;
         lastName = name;
       } else {
-        if (!userCaseCaches.length) {
-          loading.value = true;
-          userCaseCaches.push(...(await getCaseProjects('我的方案')));
-          for (const group of cases) {
-            group.list = [];
-            for (const item of userCaseCaches) {
-              if (item.case === group.name) {
-                group.list.push(item);
-              }
-            }
-          }
-          loading.value = false;
-        }
+        subGroups.value = await getUserProjects('le5leV');
         groupType.value = 1;
-        //TODO 场景选择
-        subGroups.value = cases;
+        await getPrivateProjects('le5leV');
         userLastName = name;
       }
       break;
@@ -460,22 +457,9 @@ const groupChange = async (name: string) => {
         subGroups.value = templates;
         lastName = name;
       } else {
-        if (!userTemplateCaches.length) {
-          loading.value = true;
-          userTemplateCaches.push(...(await getCaseProjects('我的模版')));
-          for (const group of templates) {
-            group.list = [];
-            for (const item of userTemplateCaches) {
-              if (item.case === group.name) {
-                group.list.push(item);
-              }
-            }
-          }
-          loading.value = false;
-        }
-
+        subGroups.value = await getUserProjects('le5leV-template');
         groupType.value = 1;
-        subGroups.value = templates;
+        await getPrivateProjects('le5leV-template');
         userLastName = name;
       }
       break;
@@ -636,6 +620,44 @@ const getCaseProjects = async (name: string, current = 1, pageSize = 1000) => {
   return ret.list;
 };
 
+// const getUserProjects = async (name: string, current = 1, pageSize = 1000) => {
+//   const query: any = { tags: name };
+
+//   const ret: any = await axios.post(
+//     '/api/data/le5leV/list',
+//     {
+//       query,
+//       projection: {
+//         id: 1,
+//         _id: 1,
+//         name: 1,
+//         image: 1,
+//         price: 1,
+//         case: 1,
+//         folder: 1,
+//       },
+//       sort: { createdAt: 1 },
+//     },
+//     {
+//       params: {
+//         current,
+//         pageSize,
+//       },
+//     }
+//   );
+
+//   if (!ret) {
+//     return [];
+//   }
+//   for (const item of ret.list) {
+//     if (!item.id) {
+//       item.id = item._id;
+//     }
+//     item.draggable = false;
+//   }
+//   return ret.list;
+// };
+
 const getPrivateGroups = async () => {
   const list = [
     {
@@ -722,6 +744,86 @@ const getUserComponents = async () => {
   return list;
 };
 
+const getUserProjects = async (type: string) => {
+  const list = [
+    {
+      name: '未分类',
+      list: [],
+    },
+  ];
+  const config = {
+    params: {
+      current: 1,
+      pageSize: 1000,
+    },
+  };
+  let ret: any = await axios.post(
+    '/api/data/folders/list',
+    {
+      projection: {
+        image: 1,
+        _id: 1,
+        name: 1,
+        list: 1,
+      },
+      query: {
+        type,
+      },
+      sort: { createdAt: 1 },
+    },
+    config
+  );
+  if (!ret) {
+    ret = { list: [] };
+  }
+  for (const item of ret.list) {
+    item.canEdited = true;
+  }
+  list.push(...ret.list);
+  return list;
+};
+
+const getPrivateProjects = async (type: string) => {
+  for (const item of subGroups.value) {
+    if (!item.list.length) {
+      item.loading = true;
+      //TODO 方案/模板分类
+      if (item.name === '未分类') {
+        const data = {
+          query: {
+            folder: '',
+            isTemplate: type === 'le5leV-template' ? true : undefined,
+          },
+          projection: {
+            image: 1,
+            _id: 1,
+            name: 1,
+            isTemplate: 1,
+          },
+        };
+        const config = {
+          params: {
+            current: 1,
+            pageSize: 1000,
+          },
+        };
+        const res: any = await getCollectionList('le5leV', data, config);
+        if (res?.list) {
+          // res.list.forEach((item) => {
+          //   item.draggable = false;
+          // })
+          item.list = res.list;
+          if (type === 'le5leV') {
+            //过滤模板
+            item.list = res.list.filter((item) => !item.isTemplate);
+          }
+        }
+      }
+      item.loading = false;
+    }
+  }
+};
+
 const dragStart = async (event: DragEvent | MouseEvent, item: any) => {
   event.stopPropagation();
   if (!item) {
@@ -734,7 +836,13 @@ const dragStart = async (event: DragEvent | MouseEvent, item: any) => {
   let data = null;
   const id = item._id || item.id;
   let isAsync: boolean;
-  if (item.draggable === false) {
+  if (
+    activeAssets.value === 'user' &&
+    ['方案', '模板'].includes(activedGroup.value)
+  ) {
+    item.draggable = false;
+    data = item.data || item;
+  } else if (item.draggable === false) {
     //方案
     data = item.data || item;
   } else if (item['3d']) {
@@ -745,9 +853,9 @@ const dragStart = async (event: DragEvent | MouseEvent, item: any) => {
       tags: ['meta3d'],
       zIndex: 1,
       operationalRect: {
-        x: 0.1,
+        x: 0.2,
         y: 0.2,
-        height: 0.9,
+        height: 0.8,
         width: 0.6,
       },
       props: {
@@ -804,7 +912,6 @@ const dragStart = async (event: DragEvent | MouseEvent, item: any) => {
   if (!Array.isArray(data)) {
     data = deepClone([data]);
   }
-
   !dropped && (meta2d.canvas.addCaches = data);
 
   if (event instanceof DragEvent) {
@@ -827,7 +934,6 @@ const open = async (item: any) => {
   if (!item || item.draggable !== false) {
     return;
   }
-
   const ret: any = await getLe5leV(item._id || item.id);
   if (!ret) {
     if (item.price > 0) {
@@ -837,20 +943,30 @@ const open = async (item: any) => {
     return;
   }
 
-  sessionStorage.setItem('opening', '1');
-  router.push({
-    path: '/',
-    query: {
-      r: Date.now() + '',
-    },
-  });
+  if (activeAssets.value === 'user') {
+    router.push({
+      path: '/',
+      query: {
+        r: Date.now() + '',
+        id: item._id,
+      },
+    });
+  } else {
+    sessionStorage.setItem('opening', '1');
+    router.push({
+      path: '/',
+      query: {
+        r: Date.now() + '',
+      },
+    });
 
-  for (const k of delAttrs) {
-    delete ret[k];
+    for (const k of delAttrs) {
+      delete ret[k];
+    }
+    autoSave();
+    meta2d.open(ret);
+    meta2d.fitView();
   }
-  autoSave();
-  meta2d.open(ret);
-  meta2d.fitView();
 };
 
 const getPrivateGraphics = async () => {
@@ -942,7 +1058,11 @@ const createFoder = async () => {
     MessagePlugin.error('已经存在相同名称文件夹');
     return;
   }
-  if (activedGroup.value === '组件') {
+  if (activeAssets.value !== 'user') {
+    return;
+  }
+
+  if (['组件', '方案', '模板'].includes(activedGroup.value)) {
     if (editedFolder.value._id) {
       const ret: any = await axios.post('/api/data/folders/update', {
         _id: editedFolder.value._id,
@@ -953,9 +1073,15 @@ const createFoder = async () => {
         editedFolder.value.edited = false;
       }
     } else {
+      let type =
+        activedGroup.value === '组件'
+          ? 'le5leV-components'
+          : activedGroup.value === '模板'
+          ? 'le5leV-template'
+          : 'le5leV';
       const ret: any = await axios.post('/api/data/folders/add', {
         name: editedFolder.value.label,
-        type: 'le5leV-components',
+        type,
         list: [],
       });
       if (ret) {
@@ -994,6 +1120,18 @@ const onEditHeader = (item: any) => {
   editedFolder.value = item;
 };
 
+const onAdd = (item: any) => {
+  blank();
+  setFolder(item);
+  router.push({
+    path: '/',
+    query: {
+      r: Date.now() + '',
+      folder: item.name,
+    },
+  });
+};
+
 const onKeyHeader = (text: string, event: any) => {
   if (event.e.key === 'Escape') {
     editedFolder.value.edited = false;
@@ -1369,7 +1507,10 @@ const delFolder = async (item: any) => {
 
   const id = item._id || item.id;
   let ret: any;
-  if (activedGroup.value === '组件') {
+  if (activeAssets.value !== 'user') {
+    return;
+  }
+  if (['组件', '方案', '模板'].includes(activedGroup.value)) {
     ret = await axios.post('/api/data/folders/delete', {
       id,
     });
@@ -1414,6 +1555,12 @@ onUnmounted(() => {
     justify-content: center;
     align-items: center;
     display: flex;
+    .t-radio-button {
+      &.t-is-checked {
+        background-color: var(--color-primary-hover) !important;
+        color: #fff !important;
+      }
+    }
   }
   .groups-panel {
     display: grid;

+ 8 - 1
src/views/components/PenProps.vue

@@ -925,7 +925,14 @@
                   >
                     <t-icon name="ellipsis" slot="icon"
                   /></t-button>
-
+                  <t-slider
+                    v-else-if="item.type === 'slider'"
+                    v-model="data.pen[item.key]"
+                    :min="0"
+                    :max="1"
+                    :step="0.01"
+                    @change="changeValue(item.key)"
+                  />
                   <t-input
                     class="w-full"
                     v-else

+ 36 - 10
src/views/components/View.vue

@@ -14,18 +14,33 @@
           >
             <t-icon
               name="save"
-              @click="!route.query.c && save(SaveType.Save, false, true)"
+              @click="!route.query.c && save(SaveType.Save, '', true)"
             />
           </t-badge>
         </a>
       </t-tooltip>
+      <!-- <t-tooltip content="保存为模板" placement="bottom">
+        <a>
+          <t-badge
+            :class="{ gray: route.query.c }"
+            dot
+            :showZero="false"
+            :count="dot ? 1 : 0"
+          >
+            <t-icon
+              name="save"
+              @click="!route.query.c && save(SaveType.Save, 'template', true)"
+            />
+          </t-badge>
+        </a>
+      </t-tooltip> -->
       <t-tooltip content="保存为我的组件" placement="bottom">
         <a :class="{ gray: route.query.id && !route.query.c }">
           <t-icon
             name="layers"
             @click="
               (!route.query.id || route.query.c) &&
-                save(SaveType.Save, true, true)
+                save(SaveType.Save, 'component', true)
             "
           />
         </a>
@@ -298,7 +313,7 @@
                   >
                     开启模拟数据
                   </t-checkbox>
-                  <label class="ml-8"> 时间间隔 </label>
+                  <label class="ml-8"> 轮询间隔 </label>
                   <t-input-number
                     style="width: 100px"
                     v-model="dataDialog.networkInterval"
@@ -1128,13 +1143,24 @@ const preview = async () => {
   if (!data._id) {
     await localforage.setItem(localStorageName, JSON.stringify(data));
   }
-  router.push({
-    path: '/preview',
-    query: {
-      r: Date.now() + '',
-      id: data._id,
-    },
-  });
+  // router.push({
+  //   path: '/preview',
+  //   query: {
+  //     r: Date.now() + '',
+  //     id: data._id,
+  //   },
+  // });
+  let screenWidth = window.screen.width;
+  let screenHeight = window.screen.height;
+  let url = `/preview?r=${Date.now() + ''}`;
+  if (data._id) {
+    url += `&id=${data._id}`;
+  }
+  window.open(
+    url,
+    '',
+    `height=${screenHeight},width=${screenWidth},top=0,left=0,toolbar=no,menubar=no, scrollbars=no,resizable=no,location=no, status=no`
+  );
 };
 
 const contextmenu = reactive<any>({