牛奶 1 月之前
父節點
當前提交
ff95b5689d

+ 10 - 0
src/router/index.ts

@@ -47,6 +47,16 @@ export const constantRoutes: RouteRecordRaw[] = [
     component: () => import('@/views/login.vue'),
     hidden: true
   },
+  {
+    path: '/diy/pcedit',
+    component: () => import('@/views/diy/pcedit.vue'),
+    hidden: true
+  },
+    {
+    path: '/diy/pcdiy',
+    component: () => import('@/views/diy/pcdiy.vue'),
+    hidden: true
+  },
   {
     path: '/register',
     component: () => import('@/views/register.vue'),

+ 31 - 0
src/store/modules/pcdiy.ts

@@ -547,6 +547,37 @@ const usePcdiyStore = defineStore('pcdiy', {
 
       console.log(this.componentList, '4564654');
     },
+    // 上移组件
+    moveUpComponent() {
+      if (this.currentIndex <= 0 || this.currentIndex >= this.componentList.length) {
+        return;
+      }
+      [this.componentList[this.currentIndex], this.componentList[this.currentIndex - 1]] = [
+        this.componentList[this.currentIndex - 1],
+        this.componentList[this.currentIndex]
+      ];
+    },
+    // 下移组件
+    moveDownComponent() {
+      if (this.currentIndex < 0 || this.currentIndex >= this.componentList.length - 1) {
+        return;
+      }
+      [this.componentList[this.currentIndex], this.componentList[this.currentIndex + 1]] = [
+        this.componentList[this.currentIndex + 1],
+        this.componentList[this.currentIndex]
+      ];
+    },
+    // 复制
+    copyComponent() {
+      // const datas = structuredClone(this.componentList[this.currentIndex]);
+      // const datas = JSON.parse(JSON.stringify(this.componentList[this.currentIndex]));
+      // // console.log(datas, '???????')
+      // this.componentList.splice(this.currentIndex + 1, 0, datas);
+    },
+    //删除
+    delComponent() {
+      this.componentList.splice(this.currentIndex, 1);
+    },
     //点击组件
     onComponent(item: any, index: any) {
       this.currentKey = item.itemKey;

+ 196 - 167
src/views/diy/pcEdit.vue

@@ -1,46 +1,45 @@
 <template>
-  <div class="pcEdit">
-    <div class="pcEdit-pages">
-      <el-header class="flex items-center h-[50px] bg-primary px-[20px]">
-        <div class="text-white cursor-pointer flex items-center" @click="goBack">
-          <el-icon size="14">
-            <ArrowLeft />
-          </el-icon>
-          <span class="pl-[5px] text-[14px]">返回</span>
-        </div>
-        <div class="text-white ml-[10px] mr-[20px] flex items-center">
-          <span class="mr-[5px] text-[rgba(255,255,255,.5)]">|</span>
-          <span class="mr-[5px] text-[14px]">正在装修:{{ query.title || '页面名字' }}</span>
-        </div>
-        <div class="flex-1"></div>
-        <el-button @click="preview()">保存并预览</el-button>
-        <el-button @click="save()">保存</el-button>
-      </el-header>
-      <div class="full-container flex flex-row flex-1 bg-page">
-        <div class="component-list w-[192px]">
-          <!-- 组件列表区域 -->
-          <el-collapse v-model="activeNames" @change="handleChange">
-            <el-collapse-item v-for="(item, key) in collapse" :key="key" :title="item.name" :name="key">
-              <ul class="flex flex-row flex-wrap">
-                <li
-                  v-for="(compItem, compKey) in item.list"
-                  :key="compKey"
-                  class="w-2/4 text-center cursor-pointer h-[65px]"
-                  :title="compItem.name"
-                  @click="diyStore.addComponent(compItem, compKey)"
-                >
-                  <icon v-if="compItem.icon" :name="compItem.icon" size="20px" class="inline-block mt-[3px]" />
-                  <icon v-else name="iconfont iconkaifazujian" size="20px" class="inline-block mt-[3px]" />
-                  <span class="block text-[12px] truncate">{{ compItem.name }}</span>
-                </li>
-              </ul>
-            </el-collapse-item>
-          </el-collapse>
-        </div>
-        <div class="preview-wrap">
+  <div class="pcEdit-pages">
+    <el-header class="flex items-center h-[50px] bg-primary px-[20px]">
+      <div class="text-white cursor-pointer flex items-center" @click="goBack">
+        <el-icon size="14">
+          <ArrowLeft />
+        </el-icon>
+        <span class="pl-[5px] text-[14px]">返回</span>
+      </div>
+      <div class="text-white ml-[10px] mr-[20px] flex items-center">
+        <span class="mr-[5px] text-[rgba(255,255,255,.5)]">|</span>
+        <span class="mr-[5px] text-[14px]">正在装修:{{ query.title || '页面名字' }}</span>
+      </div>
+      <div class="flex-1"></div>
+      <el-button @click="preview()">保存并预览</el-button>
+      <el-button @click="save()">保存</el-button>
+    </el-header>
+    <div class="full-container flex flex-row flex-1 bg-page">
+      <div class="component-list w-[192px]">
+        <!-- 组件列表区域 -->
+        <el-collapse v-model="activeNames" @change="handleChange">
+          <el-collapse-item v-for="(item, key) in collapse" :key="key" :title="item.name" :name="key">
+            <ul class="flex flex-row flex-wrap">
+              <li
+                v-for="(compItem, compKey) in item.list"
+                :key="compKey"
+                class="w-2/4 text-center cursor-pointer h-[65px]"
+                :title="compItem.name"
+                @click="diyStore.addComponent(compItem, compKey)"
+              >
+                <icon v-if="compItem.icon" :name="compItem.icon" size="20px" class="inline-block mt-[3px]" />
+                <icon v-else name="iconfont iconkaifazujian" size="20px" class="inline-block mt-[3px]" />
+                <span class="block text-[12px] truncate">{{ compItem.name }}</span>
+              </li>
+            </ul>
+          </el-collapse-item>
+        </el-collapse>
+      </div>
+      <div class="preview-wrap">
+        <div class="preview-box">
           <!-- 组件编辑区域 -->
           <div class="preview-pages shadow-lg">
-            <!-- @end="onDragEnd" -->
             <draggable v-model="diyStore.componentList" item-key="itemKey" class="drag-area">
               <template #item="{ element, index }">
                 <div @click="diyStore.onComponent(element, index)" class="component-bos">
@@ -50,118 +49,130 @@
               </template>
             </draggable>
           </div>
+          <div class="quick-action text-center w-[42px] rounded shadow-md">
+            <el-tooltip effect="light" content="上移" placement="right">
+              <icon name="iconfont iconjiantoushang" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.moveUpComponent" />
+            </el-tooltip>
+            <el-tooltip effect="light" content="下移" placement="right">
+              <icon name="iconfont iconjiantouxia" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.moveDownComponent" />
+            </el-tooltip>
+            <el-tooltip effect="light" content="复制" placement="right">
+              <icon name="iconfont iconcopy-line" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.copyComponent" />
+            </el-tooltip>
+            <el-tooltip effect="light" content="删除" placement="right">
+              <icon name="iconfont icondelete-line" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.delComponent" />
+            </el-tooltip>
+            <el-tooltip effect="light" content="重置" placement="right">
+              <icon name="iconfont iconloader-line" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.resetComponent" />
+            </el-tooltip>
+          </div>
         </div>
+      </div>
+
+      <!-- 编辑组件属性区域 -->
+      <div class="edit-attribute-wrap w-[400px]">
         <!-- 编辑组件属性区域 -->
-        <div class="edit-attribute-wrap w-[400px]">
-          <!-- 编辑组件属性区域 -->
-          <el-scrollbar>
-            <el-card class="box-card" shadow="never">
-              <template #header>
-                <div class="card-header flex justify-between items-center">
-                  <span class="title flex-1">{{ diyStore.currentIndex == -99 ? '页面设置' : diyStore.editComponent.name }}</span>
-                  <div class="tab-wrap flex rounded-[50px] bg-gray-100 text-[14px]">
-                    <span
-                      class="cursor-pointer rounded-[50px] py-[5px] px-[15px]"
-                      :class="{ 'bg-primary text-white': diyStore.editTab == 'content' }"
-                      @click="diyStore.editTab = 'content'"
-                      >内容</span
-                    >
-                    <span
-                      class="cursor-pointer rounded-[50px] py-[5px] px-[15px]"
-                      :class="{ 'bg-primary text-white': diyStore.editTab == 'style' }"
-                      @click="diyStore.editTab = 'style'"
-                      >样式</span
-                    >
-                  </div>
+        <el-scrollbar>
+          <el-card class="box-card" shadow="never">
+            <template #header>
+              <div class="card-header flex justify-between items-center">
+                <span class="title flex-1">{{ diyStore.currentIndex == -99 ? '页面设置' : diyStore.editComponent.name }}</span>
+                <div class="tab-wrap flex rounded-[50px] bg-gray-100 text-[14px]">
+                  <span
+                    class="cursor-pointer rounded-[50px] py-[5px] px-[15px]"
+                    :class="{ 'bg-primary text-white': diyStore.editTab == 'content' }"
+                    @click="diyStore.editTab = 'content'"
+                    >内容</span
+                  >
+                  <span
+                    class="cursor-pointer rounded-[50px] py-[5px] px-[15px]"
+                    :class="{ 'bg-primary text-white': diyStore.editTab == 'style' }"
+                    @click="diyStore.editTab = 'style'"
+                    >样式</span
+                  >
                 </div>
-              </template>
+              </div>
+            </template>
 
-              <div class="edit-component-wrap">
-                <component
-                  v-if="diyStore.currentKey"
-                  :is="diyStore.editComponent.edit"
-                  :key="diyStore.currentIndex"
-                  :value="diyStore.componentList[diyStore.currentIndex]"
-                >
-                  <template #style>
-                    <div class="edit-attr-item-wrap">
-                      <h3 class="mb-[10px]">组件样式</h3>
-                      <el-form label-width="90px" class="px-[10px]">
-                        <template v-if="diyStore.editComponent.ignore.indexOf('pageBgColor') == -1">
-                          <el-form-item label="底部背景">
-                            <el-color-picker v-model="diyStore.editComponent.pageStartBgColor" show-alpha :predefine="diyStore.predefineColors" />
-                            <icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
-                            <el-color-picker v-model="diyStore.editComponent.pageEndBgColor" show-alpha :predefine="diyStore.predefineColors" />
-                          </el-form-item>
-                          <div class="text-sm text-gray-400 ml-[90px] mb-[10px]">底部背景包含边距和圆角</div>
-                        </template>
-                        <el-form-item label="渐变角度" v-if="diyStore.editComponent.ignore.indexOf('pageBgColor') == -1">
-                          <el-radio-group v-model="diyStore.editComponent.pageGradientAngle">
-                            <el-radio value="to bottom">从上到下</el-radio>
-                            <el-radio value="to right">从左到右</el-radio>
-                          </el-radio-group>
-                        </el-form-item>
-                        <el-form-item label="组件背景色" v-if="diyStore.editComponent.ignore.indexOf('componentBgColor') == -1">
-                          <el-color-picker v-model="diyStore.editComponent.componentStartBgColor" show-alpha :predefine="diyStore.predefineColors" />
+            <div class="edit-component-wrap">
+              <component
+                v-if="diyStore.currentKey"
+                :is="diyStore.editComponent.edit"
+                :key="diyStore.currentIndex"
+                :value="diyStore.componentList[diyStore.currentIndex]"
+              >
+                <template #style>
+                  <div class="edit-attr-item-wrap">
+                    <h3 class="mb-[10px]">组件样式</h3>
+                    <el-form label-width="90px" class="px-[10px]">
+                      <template v-if="diyStore.editComponent.ignore.indexOf('pageBgColor') == -1">
+                        <el-form-item label="底部背景">
+                          <el-color-picker v-model="diyStore.editComponent.pageStartBgColor" show-alpha :predefine="diyStore.predefineColors" />
                           <icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
-                          <el-color-picker v-model="diyStore.editComponent.componentEndBgColor" show-alpha :predefine="diyStore.predefineColors" />
-                        </el-form-item>
-                        <el-form-item label="渐变角度" v-if="diyStore.editComponent.ignore.indexOf('componentBgColor') == -1">
-                          <el-radio-group v-model="diyStore.editComponent.componentGradientAngle">
-                            <el-radio value="to bottom">从上到下</el-radio>
-                            <el-radio value="to right">从左到右</el-radio>
-                          </el-radio-group>
-                        </el-form-item>
-                        <el-form-item label="上边距" v-if="diyStore.editComponent.ignore.indexOf('marginTop') == -1">
-                          <el-slider
-                            v-model="diyStore.editComponent.padding.top"
-                            show-input
-                            size="small"
-                            :min="-100"
-                            class="ml-[10px] diy-nav-slider"
-                          />
-                        </el-form-item>
-                        <el-form-item label="下边距" v-if="diyStore.editComponent.ignore.indexOf('marginBottom') == -1">
-                          <el-slider
-                            v-model="diyStore.editComponent.padding.bottom"
-                            show-input
-                            size="small"
-                            class="ml-[10px] diy-nav-slider"
-                            :min="-100"
-                          />
+                          <el-color-picker v-model="diyStore.editComponent.pageEndBgColor" show-alpha :predefine="diyStore.predefineColors" />
                         </el-form-item>
-                        <el-form-item label="左右边距" v-if="diyStore.editComponent.ignore.indexOf('marginBoth') == -1">
-                          <el-slider v-model="diyStore.editComponent.padding.both" show-input size="small" class="ml-[10px] diy-nav-slider" />
-                        </el-form-item>
-                        <el-form-item label="上圆角" v-if="diyStore.editComponent.ignore.indexOf('topRounded') == -1">
-                          <el-slider
-                            v-model="diyStore.editComponent.topRounded"
-                            show-input
-                            size="small"
-                            class="ml-[10px] diy-nav-slider"
-                            :max="100"
-                          />
-                        </el-form-item>
-                        <el-form-item label="下圆角" v-if="diyStore.editComponent.ignore.indexOf('bottomRounded') == -1">
-                          <el-slider
-                            v-model="diyStore.editComponent.bottomRounded"
-                            show-input
-                            size="small"
-                            class="ml-[10px] diy-nav-slider"
-                            :max="100"
-                          />
-                        </el-form-item>
-                      </el-form>
-                    </div>
-                  </template>
-                </component>
-              </div>
-            </el-card>
-          </el-scrollbar>
-          <!-- <div v-for="(item, index) in diyStore.componentList" :key="index">
+                        <div class="text-sm text-gray-400 ml-[90px] mb-[10px]">底部背景包含边距和圆角</div>
+                      </template>
+                      <el-form-item label="渐变角度" v-if="diyStore.editComponent.ignore.indexOf('pageBgColor') == -1">
+                        <el-radio-group v-model="diyStore.editComponent.pageGradientAngle">
+                          <el-radio value="to bottom">从上到下</el-radio>
+                          <el-radio value="to right">从左到右</el-radio>
+                        </el-radio-group>
+                      </el-form-item>
+                      <el-form-item label="组件背景色" v-if="diyStore.editComponent.ignore.indexOf('componentBgColor') == -1">
+                        <el-color-picker v-model="diyStore.editComponent.componentStartBgColor" show-alpha :predefine="diyStore.predefineColors" />
+                        <icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
+                        <el-color-picker v-model="diyStore.editComponent.componentEndBgColor" show-alpha :predefine="diyStore.predefineColors" />
+                      </el-form-item>
+                      <el-form-item label="渐变角度" v-if="diyStore.editComponent.ignore.indexOf('componentBgColor') == -1">
+                        <el-radio-group v-model="diyStore.editComponent.componentGradientAngle">
+                          <el-radio value="to bottom">从上到下</el-radio>
+                          <el-radio value="to right">从左到右</el-radio>
+                        </el-radio-group>
+                      </el-form-item>
+                      <el-form-item label="上边距" v-if="diyStore.editComponent.ignore.indexOf('marginTop') == -1">
+                        <el-slider
+                          v-model="diyStore.editComponent.padding.top"
+                          show-input
+                          size="small"
+                          :min="-100"
+                          class="ml-[10px] diy-nav-slider"
+                        />
+                      </el-form-item>
+                      <el-form-item label="下边距" v-if="diyStore.editComponent.ignore.indexOf('marginBottom') == -1">
+                        <el-slider
+                          v-model="diyStore.editComponent.padding.bottom"
+                          show-input
+                          size="small"
+                          class="ml-[10px] diy-nav-slider"
+                          :min="-100"
+                        />
+                      </el-form-item>
+                      <el-form-item label="左右边距" v-if="diyStore.editComponent.ignore.indexOf('marginBoth') == -1">
+                        <el-slider v-model="diyStore.editComponent.padding.both" show-input size="small" class="ml-[10px] diy-nav-slider" />
+                      </el-form-item>
+                      <el-form-item label="上圆角" v-if="diyStore.editComponent.ignore.indexOf('topRounded') == -1">
+                        <el-slider v-model="diyStore.editComponent.topRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="100" />
+                      </el-form-item>
+                      <el-form-item label="下圆角" v-if="diyStore.editComponent.ignore.indexOf('bottomRounded') == -1">
+                        <el-slider
+                          v-model="diyStore.editComponent.bottomRounded"
+                          show-input
+                          size="small"
+                          class="ml-[10px] diy-nav-slider"
+                          :max="100"
+                        />
+                      </el-form-item>
+                    </el-form>
+                  </div>
+                </template>
+              </component>
+            </div>
+          </el-card>
+        </el-scrollbar>
+        <!-- <div v-for="(item, index) in diyStore.componentList" :key="index">
           <component v-if="item.itemKey == diyStore.currentKey" :is="item.edit" :value="item"> </component>
         </div> -->
-        </div>
       </div>
     </div>
   </div>
@@ -196,6 +207,11 @@ import imageCube from '@/views/diy/pcPages/imageCube.vue';
 const imageCubeRef = shallowRef(imageCube);
 import imageCubeEdit from '@/views/diy/pcEdit/imageCube-edit.vue';
 const imageCubeEditRef = shallowRef(imageCubeEdit);
+//活动魔方
+import activity from '@/views/diy/pcPages/activity.vue';
+const activityRef = shallowRef(activity);
+import activityEdit from '@/views/diy/pcEdit/activity-edit.vue';
+const activityEditRef = shallowRef(activityEdit);
 //轮播图
 import carousel from '@/views/diy/pcPages/carousel.vue';
 const carouselRef = shallowRef(carousel);
@@ -241,6 +257,7 @@ const itemKey = ref<any>(0);
 //左边得组件
 const uniqueIdCounter = ref<any>(0);
 const activeNames = ref<any>([0]);
+
 const collapse = ref<any>([
   {
     name: '基础组件',
@@ -276,7 +293,9 @@ const collapse = ref<any>([
       {
         name: '活动魔方',
         icon: 'iconfont iconmofangpc',
-        id: 5
+        id: 5,
+        // components: markRaw(activityRef.value),
+        // edit: markRaw(activityEditRef.value)
       },
       {
         name: '轮播图',
@@ -373,16 +392,13 @@ const save = () => {
 </script>
 
 <style lang="scss" scoped>
-.pcEdit {
-  width: 100%;
-  overflow: auto;
-}
 .pcEdit-pages {
-  min-height: calc(100vh - 84px);
+  // min-height: calc(100vh - 84px);
   // min-width: 1900px;
+  height: calc(100vh);
 
   .full-container {
-    height: calc(100vh - 134px);
+    height: calc(100vh - 50px);
     background-color: #f2f2f2;
 
     .component-list {
@@ -403,32 +419,45 @@ const save = () => {
       display: flex;
       justify-content: center;
 
+      .preview-box {
+        width: 1350px;
+        position: relative;
+      }
+
       .preview-pages {
-        margin: 30px auto;
+        // margin: 30px auto;
         width: 1300px;
         background: var(--el-bg-color-page);
-        overflow: auto;
-        height: calc(130vh - 194px);
-        zoom: 0.7;
+        overflow-y: auto;
+        // height: calc(130vh - 194px);
+        // zoom: 0.7;
         // height: calc(100vh - 194px);
+        height: 100%;
 
         /* 为了兼容某些情况,可能还需要配合 display */
         display: inline-block;
-      }
 
-      .component-bos {
-        position: relative;
-        .component-box {
-          position: absolute;
-          width: 100%;
-          height: 100%;
-          top: 0;
-          left: 0;
-          border: 2px solid var(--el-color-primary);
-          z-index: 2;
-          cursor: move;
+        .component-bos {
+          position: relative;
+          .component-box {
+            position: absolute;
+            width: 100%;
+            height: 100%;
+            top: 0;
+            left: 0;
+            border: 2px solid var(--el-color-primary);
+            z-index: 2;
+            cursor: move;
+          }
         }
       }
+
+      .quick-action {
+        background: var(--el-bg-color);
+        position: absolute;
+        top: 20px;
+        right: 0px;
+      }
     }
 
     //编辑组件属性区域

+ 226 - 0
src/views/diy/pcEdit/activity-edit.vue

@@ -0,0 +1,226 @@
+<template>
+  <div class="pc-edit">
+    <div class="content-wrap" v-show="diyStore.editTab == 'content'">
+      <div class="edit-attr-item-wrap">
+        <h3 class="mb-[10px]">导航模式</h3>
+        <el-form label-width="80px" class="px-[10px]">
+          <el-form-item label="展示风格">
+            <el-radio-group v-model="diyStore.editComponent.styleType">
+              <el-radio :value="1">固定显示</el-radio>
+              <el-radio :value="2">单行滑动</el-radio>
+              <el-radio :value="3">分页滑动</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="每行数量">
+            <el-radio-group v-model="diyStore.editComponent.number">
+              <el-radio :value="3">3个</el-radio>
+              <el-radio :value="4">4个</el-radio>
+              <el-radio :value="5">5个</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="每页行数" v-if="diyStore.editComponent.styleType == 3">
+            <el-radio-group v-model="diyStore.editComponent.count">
+              <el-radio :value="1">1个</el-radio>
+              <el-radio :value="2">2个</el-radio>
+            </el-radio-group>
+          </el-form-item>
+        </el-form>
+      </div>
+      <div class="edit-attr-item-wrap">
+        <div class="edit-attr-title flex-row-between">
+          <div>
+            <span>导航设置</span>
+            <span class="title2">鼠标拖拽可以改变顺序</span>
+          </div>
+        </div>
+        <el-form label-width="86px" class="px-[10px]">
+          <draggable v-model="diyStore.editComponent.navlList" item-key="id">
+            <template #item="{ element, index }">
+              <div class="edit-attr-box">
+                <el-icon @click="onDel(index)" color="#F56C6C" size="18px" class="circleClose">
+                  <CircleCloseFilled />
+                </el-icon>
+                <el-form-item label="图片上传">
+                  <div class="flex-row-start">
+                    <upload-image v-model="element.imageUrl" :limit="1" />
+                    <div class="flex-column-between images-bos">
+                      <div class="annotation3">(建议上传尺寸相同图片,推荐尺寸150*150)</div>
+                      <div class="flex-row-between images-box">
+                        <div>缩放模式</div>
+                        <div class="flex-row-start" @click="openImageType(element, index)">
+                          <span style="margin-top: 2px" class="text-primary flex-1 cursor-pointer">{{
+                            element.imgType == 1 ? '拉伸' : element.imgType == 2 ? '缩放' : '填充'
+                          }}</span>
+                          <el-icon class="cursor-pointer">
+                            <ArrowRight />
+                          </el-icon>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
+                </el-form-item>
+                <el-form-item label="标题名称">
+                  <el-input v-model="element.title" placeholder="请输入标签名称" :maxlength="10" show-word-limit />
+                </el-form-item>
+                <el-form-item label="副标题名称">
+                  <el-input v-model="element.subtitle" placeholder="请输入标签名称" />
+                </el-form-item>
+                <el-form-item label="链接地址">
+                  <el-input v-model="element.url" placeholder="请输入链接地址" />
+                </el-form-item>
+              </div>
+            </template>
+          </draggable>
+          <el-button @click="onAdd" style="width: 100%; margin-top: 10px">新增导航</el-button>
+        </el-form>
+      </div>
+    </div>
+    <!-- 样式 -->
+    <div class="style-wrap" v-show="diyStore.editTab == 'style'">
+      <div class="edit-attr-item-wrap">
+        <h3 class="mb-[10px]">样式设置</h3>
+        <el-form label-width="80px" class="px-[10px]">
+          <el-form-item label="文字大小">
+            <el-slider size="small" v-model="diyStore.editComponent.titleSize" show-input :min="1" :max="50" />
+          </el-form-item>
+          <el-form-item label="文字加粗">
+            <el-radio-group size="small" v-model="diyStore.editComponent.titleWeight" fill="#409eff">
+              <el-radio-button label="加粗" :value="'bold'" />
+              <el-radio-button label="不加粗" :value="'normal'" />
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="文字颜色">
+            <span class="mr-[10px]">{{ diyStore.editComponent.titleColor }}</span>
+            <el-color-picker class="mr-[10px]" v-model="diyStore.editComponent.titleColor" />
+            <el-button @click="diyStore.editComponent.titleColor = '#101828'" size="small">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+      <div class="edit-attr-item-wrap">
+        <h3 class="mb-[10px]">副标题样式</h3>
+        <el-form label-width="80px" class="px-[10px]">
+          <el-form-item label="文字大小">
+            <el-slider size="small" v-model="diyStore.editComponent.subtitleSize" show-input :min="1" :max="50" />
+          </el-form-item>
+          <el-form-item label="文字颜色">
+            <span class="mr-[10px]">{{ diyStore.editComponent.subtitleColor }}</span>
+            <el-color-picker class="mr-[10px]" v-model="diyStore.editComponent.subtitleColor" />
+            <el-button @click="diyStore.editComponent.subtitleColor = '#101828'" size="small">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+      <div class="edit-attr-item-wrap">
+        <h3 class="mb-[10px]">图片设置</h3>
+        <el-form label-width="80px" class="px-[10px]">
+          <el-form-item label="图片圆角">
+            <el-slider size="small" v-model="diyStore.editComponent.imageRadius" show-input :min="1" :max="50" />
+          </el-form-item>
+        </el-form>
+      </div>
+      <!-- 组件样式 -->
+      <slot name="style"></slot>
+    </div>
+    <ImagesForm ref="ImagesFormRef" @confirmCallBack="confirmCallBack"></ImagesForm>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import draggable from 'vuedraggable';
+import usePcdiyStore from '@/store/modules/pcdiy';
+import uploadImage from '@/components/upload-image/index.vue';
+import ImagesForm from '@/components/ImagesForm/index.vue';
+const diyStore = usePcdiyStore();
+const ImagesFormRef = ref();
+
+// 打开图片类型
+const openImageType = (element: any, type: any) => {
+  const datas = JSON.parse(JSON.stringify(element));
+  ImagesFormRef.value.onOpen(datas, type);
+};
+//图片类型返回
+const confirmCallBack = (res: any, index: any) => {
+  diyStore.editComponent.navlList[index].imgType = res.imgType;
+};
+const onAdd = () => {
+  diyStore.editComponent.navlList.push({
+    imageUrl: '',
+    title: '标题名称',
+    subtitle: '副标题名称',
+    url: '',
+    imgType: 1,
+    id: Date.now()
+  });
+};
+
+const onDel = (index: any) => {
+  diyStore.editComponent.navlList.splice(index, 1);
+};
+</script>
+
+<style lang="scss" scoped>
+.pc-edit {
+  .edit-attr-item-wrap {
+    border-top: 2px solid var(--el-color-info-light-8);
+    padding-top: 20px;
+
+    &:first-of-type {
+      border-top: none;
+      padding-top: 0;
+    }
+
+    .edit-attr-title {
+      display: flex;
+
+      .title2 {
+        font-size: 12px;
+        color: #666;
+        margin-left: 6px;
+      }
+    }
+
+    .edit-attr-box {
+      padding: 18px 10px 0 10px;
+      border: 1px solid #e5e6eb;
+      border-radius: 4px;
+      position: relative;
+      margin-top: 18px;
+
+      .images-bos {
+        flex: 1;
+        height: 98px;
+        padding: 5px 0;
+      }
+
+      .images-box {
+        font-size: 13px;
+        color: #666;
+      }
+
+      .circleClose {
+        position: absolute;
+        top: -9px;
+        right: -9px;
+        cursor: pointer;
+      }
+    }
+  }
+
+  .annotation3 {
+    font-size: 12px;
+    color: #666;
+    line-height: 14px;
+  }
+
+  :deep(.el-form-item__label) {
+    font-weight: 400;
+  }
+
+  :deep(.file-selector) {
+    display: none;
+  }
+
+  // :deep(.el-radio){
+  //   margin-right: 10px;
+  // }
+}
+</style>

+ 173 - 0
src/views/diy/pcPages/activity.vue

@@ -0,0 +1,173 @@
+<template>
+  <div class="pcPages">
+    <div class="carousel-bos" :style="warpCss" v-if="componentData.styleType == 3">
+      <el-carousel :height="270 * componentData.count + (componentData.count == 2 ? 10 : 0) + 'px'" :autoplay="false" arrow="always">
+        <el-carousel-item v-for="(item1, index1) in dataList" :key="index1" class="w100% h100%">
+          <div class="carousel-list">
+            <div v-for="(item, index) in item1" :key="index" class="data-list flex-column-between" :style="boxCss">
+              <el-image
+                class="img"
+                :src="item.imageUrl ? item.imageUrl : figure"
+                :fit="item.imgType == 1 ? 'fill' : item.imgType == 2 ? 'contain' : 'cover'"
+                :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
+              />
+              <div :style="titleCss" class="title">{{ item.title || '' }}</div>
+              <div :style="subtitleCss" class="mt-[2px] mb-[12px] subtitle ellipsis">{{ item.subtitle || '' }}</div>
+            </div>
+          </div>
+        </el-carousel-item>
+      </el-carousel>
+    </div>
+    <div v-else :style="warpCss" class="data-bos">
+      <div v-for="(item, index) in componentData.navlList" :key="index" class="data-list flex-column-between" :style="boxCss">
+        <el-image
+          class="img"
+          :src="item.imageUrl ? item.imageUrl : figure"
+          :fit="item.imgType == 1 ? 'fill' : item.imgType == 2 ? 'contain' : 'cover'"
+          :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
+        />
+        <div :style="titleCss" class="title">{{ item.title || '' }}</div>
+        <div :style="subtitleCss" class="mt-[2px] mb-[12px] subtitle ellipsis">{{ item.subtitle || '' }}</div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import usePcdiyStore from '@/store/modules/pcdiy';
+import figure from '@/assets/images/figure.png';
+const diyStore = usePcdiyStore();
+const props = defineProps<{
+  index: number; // 确保声明 index 为可选属性
+}>();
+const componentData = diyStore.componentList[props.index];
+
+const dataList = computed(() => {
+  const chunkSize = componentData.number * componentData.count;
+  const result = [];
+  for (let i = 0; i < componentData.navlList.length; i += chunkSize) {
+    const chunk = componentData.navlList.slice(i, i + chunkSize);
+    result.push(chunk);
+  }
+  return result;
+});
+
+const warpCss = computed(() => {
+  let style = '';
+  style += 'position:relative;';
+  //背景颜色
+  if (componentData.pageStartBgColor) {
+    if (componentData.pageStartBgColor && componentData.pageEndBgColor)
+      style += `background:linear-gradient(${componentData.pageGradientAngle},${componentData.pageStartBgColor},${componentData.pageEndBgColor});`;
+    else if (componentData.pageStartBgColor) style += `background: ${componentData.pageStartBgColor};`;
+    else if (componentData.pageEndBgColor) style += `background: ${componentData.pageEndBgColor};`;
+  }
+  //边距
+  if (componentData.padding) {
+    if (componentData.padding.top > 0) {
+      style += 'padding-top:' + componentData.padding.top + 'px' + ';';
+    }
+    if (componentData.padding.bottom > 0) {
+      style += 'padding-bottom:' + componentData.padding.bottom + 'px' + ';';
+    }
+    style += 'padding-right:' + componentData.padding.both + 'px' + ';';
+    style += 'padding-left:' + componentData.padding.both + 'px' + ';';
+    if (componentData.styleType == 1) style += 'flex-wrap:wrap' + ';';
+  }
+  return style;
+});
+
+//组件样式
+const boxCss = computed(() => {
+  let style = '';
+  if (componentData.componentStartBgColor && componentData.componentEndBgColor)
+    style += `background:linear-gradient(${componentData.componentGradientAngle},${componentData.componentStartBgColor},${componentData.componentEndBgColor});`;
+  else if (componentData.componentStartBgColor) style += 'background-color:' + componentData.componentStartBgColor + ';';
+  else if (componentData.componentEndBgColor) style += 'background-color:' + componentData.componentEndBgColor + ';';
+  if (componentData.number) style += 'flex:' + `0 0 calc((100% - ${(componentData.number - 1) * 10}px) / ${componentData.number})` + ';';
+  //圆角
+  if (componentData.topRounded) style += 'border-top-left-radius:' + componentData.topRounded + 'px;';
+  if (componentData.topRounded) style += 'border-top-right-radius:' + componentData.topRounded + 'px;';
+  if (componentData.bottomRounded) style += 'border-bottom-left-radius:' + componentData.bottomRounded + 'px;';
+  if (componentData.bottomRounded) style += 'border-bottom-right-radius:' + componentData.bottomRounded + 'px;';
+  return style;
+});
+
+// 标题样式
+const titleCss = computed(() => {
+  let style = '';
+  if (componentData.titleColor) style += 'color:' + componentData.titleColor + ';';
+  if (componentData.titleSize) style += 'font-size:' + componentData.titleSize + 'px;';
+  if (componentData.titleWeight) style += 'font-weight:' + componentData.titleWeight + ';';
+  return style;
+});
+
+// 副标题样式
+const subtitleCss = computed(() => {
+  let style = '';
+  if (componentData.subtitleColor) style += 'color:' + componentData.subtitleColor + ';';
+  if (componentData.subtitleSize) style += 'font-size:' + componentData.subtitleSize + 'px;';
+  return style;
+});
+</script>
+
+<style lang="scss" scoped>
+.pcPages {
+  width: 1200px;
+  margin: 0 auto;
+  .data-bos {
+    display: flex;
+    gap: 10px;
+    width: 100%;
+    overflow-x: auto;
+    .data-list {
+      min-height: 270px;
+      width: 0;
+
+      .title {
+        text-align: center;
+        padding: 0 15px;
+      }
+
+      .subtitle {
+        text-align: center;
+        padding: 0 15px;
+      }
+
+      .img {
+        height: 200px;
+        width: 100%;
+      }
+    }
+  }
+
+  .carousel-bos {
+    .carousel-list {
+      width: 100%;
+      height: 100%;
+      display: flex;
+      gap: 10px;
+      flex-wrap: wrap;
+      .data-list {
+        height: 270px;
+        width: 0;
+
+        .title {
+          text-align: center;
+          padding: 0 15px;
+        }
+
+        .subtitle {
+          text-align: center;
+          padding: 0 15px;
+        }
+
+        .img {
+          height: 200px;
+          width: 100%;
+        }
+      }
+    }
+  }
+}
+</style>

+ 89 - 0
src/views/diy/pccomponents/index.ts

@@ -0,0 +1,89 @@
+// @/config/diy-components.ts
+import { defineAsyncComponent, type Component } from 'vue';
+
+// 辅助函数:自动导入展示组件和编辑组件
+const createComponentMap = (name: string) => ({
+  pages: defineAsyncComponent(() => import(`@/views/diy/pccomponents/pages/${name}.vue`)),
+  edit: defineAsyncComponent(() => import(`@/views/diy/pccomponents/edit/${name}-edit.vue`))
+});
+
+// 组件列表配置
+export const componentRegistry: any[] = [
+  {
+    name: '头部组件',
+    icon: 'iconfont iconfuwenbenpc',
+    id: 1,
+    ...createComponentMap('head')
+  },
+  {
+    name: '文本标题',
+    icon: 'iconfont iconbiaotipc',
+    id: 2,
+    ...createComponentMap('textTitle')
+  },
+  {
+    name: '图文导航',
+    icon: 'iconfont icontuwendaohangpc',
+    id: 3,
+    ...createComponentMap('navigation')
+  },
+  {
+    name: '图片魔方',
+    icon: 'iconfont iconmofangpc',
+    id: 4,
+    ...createComponentMap('imageCube')
+  },
+  {
+    name: '活动魔方',
+    icon: 'iconfont iconmofangpc',
+    id: 5
+  },
+  {
+    name: '轮播图',
+    icon: 'iconfont icona-tupianzhanbopc302',
+    id: 6,
+    ...createComponentMap('carousel')
+  },
+  {
+    name: '文章咨询',
+    icon: 'iconfont icongonggaopc',
+    id: 7,
+    ...createComponentMap('article')
+  },
+  {
+    name: '品牌组件',
+    icon: 'iconfont iconmiaoshashangpin',
+    id: 8,
+    ...createComponentMap('brand')
+  },
+  {
+    name: '图文广告',
+    icon: 'iconfont icontupiandaohangpc',
+    id: 9,
+    ...createComponentMap('advert')
+  },
+  {
+    name: '楼层组件',
+    icon: 'iconfont iconshangpinliebiaopc',
+    id: 10,
+    ...createComponentMap('floor')
+  },
+  {
+    name: '商品组件',
+    icon: 'iconfont icona-shangpintuijianpc30',
+    id: 11,
+    ...createComponentMap('goods')
+  },
+  {
+    name: '多商品组',
+    icon: 'iconfont iconduoshangpinzupc',
+    id: 12,
+    ...createComponentMap('goodsList')
+  },
+  {
+    name: '发现组件',
+    icon: 'iconfont iconrequpc',
+    id: 13,
+    ...createComponentMap('discover')
+  }
+];

+ 479 - 0
src/views/diy/pcdiy.vue

@@ -0,0 +1,479 @@
+<template>
+  <div class="pcEdit-pages">
+    <el-header class="flex items-center h-[50px] bg-primary px-[20px]">
+      <div class="text-white cursor-pointer flex items-center" @click="goBack">
+        <el-icon size="14">
+          <ArrowLeft />
+        </el-icon>
+        <span class="pl-[5px] text-[14px]">返回</span>
+      </div>
+      <div class="text-white ml-[10px] mr-[20px] flex items-center">
+        <span class="mr-[5px] text-[rgba(255,255,255,.5)]">|</span>
+        <span class="mr-[5px] text-[14px]">正在装修:{{ query.title || '页面名字' }}</span>
+      </div>
+      <div class="flex-1"></div>
+      <el-button @click="preview()">保存并预览</el-button>
+      <el-button @click="save()">保存</el-button>
+    </el-header>
+    <div class="full-container flex flex-row flex-1 bg-page">
+      <div class="component-list w-[192px]">
+        <!-- 组件列表区域 -->
+        <el-collapse v-model="activeNames" @change="handleChange">
+          <el-collapse-item v-for="(item, key) in collapse" :key="key" :title="item.name" :name="key">
+            <ul class="flex flex-row flex-wrap">
+              <li
+                v-for="(compItem, compKey) in item.list"
+                :key="compKey"
+                class="w-2/4 text-center cursor-pointer h-[65px]"
+                :title="compItem.name"
+                @click="diyStore.addComponent(compItem, compKey)"
+              >
+                <icon v-if="compItem.icon" :name="compItem.icon" size="20px" class="inline-block mt-[3px]" />
+                <icon v-else name="iconfont iconkaifazujian" size="20px" class="inline-block mt-[3px]" />
+                <span class="block text-[12px] truncate">{{ compItem.name }}</span>
+              </li>
+            </ul>
+          </el-collapse-item>
+        </el-collapse>
+      </div>
+      <div class="preview-wrap">
+        <div class="preview-box">
+          <!-- 组件编辑区域 -->
+          <div class="preview-pages shadow-lg">
+            <draggable v-model="diyStore.componentList" item-key="itemKey" class="drag-area">
+              <template #item="{ element, index }">
+                <div @click="diyStore.onComponent(element, index)" class="component-bos">
+                  <div class="component-box" :style="{ borderWidth: diyStore.currentIndex == index ? '2px' : '0px' }"></div>
+                  <component :is="element.components" :key="element.itemKey" :index="index"></component>
+                </div>
+              </template>
+            </draggable>
+          </div>
+          <div class="quick-action text-center w-[42px] rounded shadow-md">
+            <el-tooltip effect="light" content="上移" placement="right">
+              <icon name="iconfont iconjiantoushang" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.moveUpComponent" />
+            </el-tooltip>
+            <el-tooltip effect="light" content="下移" placement="right">
+              <icon name="iconfont iconjiantouxia" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.moveDownComponent" />
+            </el-tooltip>
+            <el-tooltip effect="light" content="复制" placement="right">
+              <icon name="iconfont iconcopy-line" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.copyComponent" />
+            </el-tooltip>
+            <el-tooltip effect="light" content="删除" placement="right">
+              <icon name="iconfont icondelete-line" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.delComponent" />
+            </el-tooltip>
+            <el-tooltip effect="light" content="重置" placement="right">
+              <icon name="iconfont iconloader-line" size="20px" class="block cursor-pointer leading-[40px]" @click="diyStore.resetComponent" />
+            </el-tooltip>
+          </div>
+        </div>
+      </div>
+
+      <!-- 编辑组件属性区域 -->
+      <div class="edit-attribute-wrap w-[400px]">
+        <!-- 编辑组件属性区域 -->
+        <el-scrollbar>
+          <el-card class="box-card" shadow="never">
+            <template #header>
+              <div class="card-header flex justify-between items-center">
+                <span class="title flex-1">{{ diyStore.currentIndex == -99 ? '页面设置' : diyStore.editComponent.name }}</span>
+                <div class="tab-wrap flex rounded-[50px] bg-gray-100 text-[14px]">
+                  <span
+                    class="cursor-pointer rounded-[50px] py-[5px] px-[15px]"
+                    :class="{ 'bg-primary text-white': diyStore.editTab == 'content' }"
+                    @click="diyStore.editTab = 'content'"
+                    >内容</span
+                  >
+                  <span
+                    class="cursor-pointer rounded-[50px] py-[5px] px-[15px]"
+                    :class="{ 'bg-primary text-white': diyStore.editTab == 'style' }"
+                    @click="diyStore.editTab = 'style'"
+                    >样式</span
+                  >
+                </div>
+              </div>
+            </template>
+
+            <div class="edit-component-wrap">
+              <component
+                v-if="diyStore.currentKey"
+                :is="diyStore.editComponent.edit"
+                :key="diyStore.currentIndex"
+                :value="diyStore.componentList[diyStore.currentIndex]"
+              >
+                <template #style>
+                  <div class="edit-attr-item-wrap">
+                    <h3 class="mb-[10px]">组件样式</h3>
+                    <el-form label-width="90px" class="px-[10px]">
+                      <template v-if="diyStore.editComponent.ignore.indexOf('pageBgColor') == -1">
+                        <el-form-item label="底部背景">
+                          <el-color-picker v-model="diyStore.editComponent.pageStartBgColor" show-alpha :predefine="diyStore.predefineColors" />
+                          <icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
+                          <el-color-picker v-model="diyStore.editComponent.pageEndBgColor" show-alpha :predefine="diyStore.predefineColors" />
+                        </el-form-item>
+                        <div class="text-sm text-gray-400 ml-[90px] mb-[10px]">底部背景包含边距和圆角</div>
+                      </template>
+                      <el-form-item label="渐变角度" v-if="diyStore.editComponent.ignore.indexOf('pageBgColor') == -1">
+                        <el-radio-group v-model="diyStore.editComponent.pageGradientAngle">
+                          <el-radio value="to bottom">从上到下</el-radio>
+                          <el-radio value="to right">从左到右</el-radio>
+                        </el-radio-group>
+                      </el-form-item>
+                      <el-form-item label="组件背景色" v-if="diyStore.editComponent.ignore.indexOf('componentBgColor') == -1">
+                        <el-color-picker v-model="diyStore.editComponent.componentStartBgColor" show-alpha :predefine="diyStore.predefineColors" />
+                        <icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
+                        <el-color-picker v-model="diyStore.editComponent.componentEndBgColor" show-alpha :predefine="diyStore.predefineColors" />
+                      </el-form-item>
+                      <el-form-item label="渐变角度" v-if="diyStore.editComponent.ignore.indexOf('componentBgColor') == -1">
+                        <el-radio-group v-model="diyStore.editComponent.componentGradientAngle">
+                          <el-radio value="to bottom">从上到下</el-radio>
+                          <el-radio value="to right">从左到右</el-radio>
+                        </el-radio-group>
+                      </el-form-item>
+                      <el-form-item label="上边距" v-if="diyStore.editComponent.ignore.indexOf('marginTop') == -1">
+                        <el-slider
+                          v-model="diyStore.editComponent.padding.top"
+                          show-input
+                          size="small"
+                          :min="-100"
+                          class="ml-[10px] diy-nav-slider"
+                        />
+                      </el-form-item>
+                      <el-form-item label="下边距" v-if="diyStore.editComponent.ignore.indexOf('marginBottom') == -1">
+                        <el-slider
+                          v-model="diyStore.editComponent.padding.bottom"
+                          show-input
+                          size="small"
+                          class="ml-[10px] diy-nav-slider"
+                          :min="-100"
+                        />
+                      </el-form-item>
+                      <el-form-item label="左右边距" v-if="diyStore.editComponent.ignore.indexOf('marginBoth') == -1">
+                        <el-slider v-model="diyStore.editComponent.padding.both" show-input size="small" class="ml-[10px] diy-nav-slider" />
+                      </el-form-item>
+                      <el-form-item label="上圆角" v-if="diyStore.editComponent.ignore.indexOf('topRounded') == -1">
+                        <el-slider v-model="diyStore.editComponent.topRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="100" />
+                      </el-form-item>
+                      <el-form-item label="下圆角" v-if="diyStore.editComponent.ignore.indexOf('bottomRounded') == -1">
+                        <el-slider
+                          v-model="diyStore.editComponent.bottomRounded"
+                          show-input
+                          size="small"
+                          class="ml-[10px] diy-nav-slider"
+                          :max="100"
+                        />
+                      </el-form-item>
+                    </el-form>
+                  </div>
+                </template>
+              </component>
+            </div>
+          </el-card>
+        </el-scrollbar>
+        <!-- <div v-for="(item, index) in diyStore.componentList" :key="index">
+          <component v-if="item.itemKey == diyStore.currentKey" :is="item.edit" :value="item"> </component>
+        </div> -->
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup name="Index" lang="ts">
+import { pcAddDiy } from '@/api/diy/index';
+import icon from '@/components/icon/index.vue';
+import draggable from 'vuedraggable';
+
+import usePcdiyStore from '@/store/modules/pcdiy';
+const diyStore = usePcdiyStore();
+const route = useRoute();
+const query = route.query;
+// 头部组件
+import head from '@/views/diy/pcPages/head.vue';
+const headRef = shallowRef(head);
+import headEdit from '@/views/diy/pcEdit/head-edit.vue';
+const headEditRef = shallowRef(headEdit);
+//文本标题
+import textTitle from '@/views/diy/pcPages/textTitle.vue';
+const ctextTitleRef = shallowRef(textTitle);
+import textTitleEdit from '@/views/diy/pcEdit/textTitle-edit.vue';
+const textTitleEditRef = shallowRef(textTitleEdit);
+//图文导航
+import navigation from '@/views/diy/pcPages/navigation.vue';
+const navigationRef = shallowRef(navigation);
+import navigationEdit from '@/views/diy/pcEdit/navigation-edit.vue';
+const navigationEditRef = shallowRef(navigationEdit);
+//图片魔方
+import imageCube from '@/views/diy/pcPages/imageCube.vue';
+const imageCubeRef = shallowRef(imageCube);
+import imageCubeEdit from '@/views/diy/pcEdit/imageCube-edit.vue';
+const imageCubeEditRef = shallowRef(imageCubeEdit);
+//轮播图
+import carousel from '@/views/diy/pcPages/carousel.vue';
+const carouselRef = shallowRef(carousel);
+import carouselEdit from '@/views/diy/pcEdit/carousel-edit.vue';
+const carouselEditRef = shallowRef(carouselEdit);
+//文章咨询
+import article from '@/views/diy/pcPages/article.vue';
+const articleRef = shallowRef(article);
+import articleEdit from '@/views/diy/pcEdit/article-edit.vue';
+const articleEditRef = shallowRef(articleEdit);
+//品牌组件
+import brand from '@/views/diy/pcPages/brand.vue';
+const brandeRef = shallowRef(brand);
+import brandEdit from '@/views/diy/pcEdit/brand-edit.vue';
+const brandEditRef = shallowRef(brandEdit);
+//图文广告
+import advert from '@/views/diy/pcPages/advert.vue';
+const advertRef = shallowRef(advert);
+import advertEdit from '@/views/diy/pcEdit/advert-edit.vue';
+const advertEditRef = shallowRef(advertEdit);
+//楼层组件
+import floor from '@/views/diy/pcPages/floor.vue';
+const floorRef = shallowRef(floor);
+import floorEdit from '@/views/diy/pcEdit/floor-edit.vue';
+const floorEditRef = shallowRef(floorEdit);
+//商品组件
+import goods from '@/views/diy/pcPages/goods.vue';
+const goodsRef = shallowRef(goods);
+import goodsEdit from '@/views/diy/pcEdit/goods-edit.vue';
+const goodsEditRef = shallowRef(goodsEdit);
+//多商品组
+import goodsList from '@/views/diy/pcPages/goodsList.vue';
+const goodsListRef = shallowRef(goodsList);
+import goodsListEdit from '@/views/diy/pcEdit/goodsList-edit.vue';
+const goodsListEditRef = shallowRef(goodsListEdit);
+//发现组件
+import discover from '@/views/diy/pcPages/discover.vue';
+const discoverRef = shallowRef(discover);
+import discoverEdit from '@/views/diy/pcEdit/discover-edit.vue';
+const discoverEditRef = shallowRef(discoverEdit);
+
+const itemKey = ref<any>(0);
+//左边得组件
+const uniqueIdCounter = ref<any>(0);
+const activeNames = ref<any>([0]);
+const collapse = ref<any>([
+  {
+    name: '基础组件',
+    list: [
+      {
+        name: '头部组件',
+        icon: 'iconfont iconfuwenbenpc',
+        id: 1,
+        components: markRaw(headRef.value),
+        edit: markRaw(headEditRef.value)
+      },
+      {
+        name: '文本标题',
+        icon: 'iconfont iconbiaotipc',
+        id: 2,
+        components: markRaw(ctextTitleRef.value),
+        edit: markRaw(textTitleEditRef.value)
+      },
+      {
+        name: '图文导航',
+        icon: 'iconfont icontuwendaohangpc',
+        id: 3,
+        components: markRaw(navigationRef.value),
+        edit: markRaw(navigationEditRef.value)
+      },
+      {
+        name: '图片魔方',
+        icon: 'iconfont iconmofangpc',
+        id: 4,
+        components: markRaw(imageCubeRef.value),
+        edit: markRaw(imageCubeEditRef.value)
+      },
+      {
+        name: '活动魔方',
+        icon: 'iconfont iconmofangpc',
+        id: 5
+      },
+      {
+        name: '轮播图',
+        icon: 'iconfont icona-tupianzhanbopc302',
+        id: 6,
+        components: markRaw(carouselRef.value),
+        edit: markRaw(carouselEditRef.value)
+      },
+      {
+        name: '文章咨询',
+        icon: 'iconfont icongonggaopc',
+        id: 7,
+        components: markRaw(articleRef.value),
+        edit: markRaw(articleEditRef.value)
+      },
+      {
+        name: '品牌组件',
+        icon: 'iconfont iconmiaoshashangpin',
+        id: 8,
+        components: markRaw(brandeRef.value),
+        edit: markRaw(brandEditRef.value)
+      },
+      {
+        name: '图文广告',
+        icon: 'iconfont icontupiandaohangpc',
+        id: 9,
+        components: markRaw(advertRef.value),
+        edit: markRaw(advertEditRef.value)
+      },
+      {
+        name: '楼层组件',
+        icon: 'iconfont iconshangpinliebiaopc',
+        id: 10,
+        components: markRaw(floorRef.value),
+        edit: markRaw(floorEditRef.value)
+      },
+      {
+        name: '商品组件',
+        icon: 'iconfont icona-shangpintuijianpc30',
+        id: 11,
+        components: markRaw(goodsRef.value),
+        edit: markRaw(goodsEditRef.value)
+      },
+      {
+        name: '多商品组',
+        icon: 'iconfont iconduoshangpinzupc',
+        id: 12,
+        components: markRaw(goodsListRef.value),
+        edit: markRaw(goodsListEditRef.value)
+      },
+      {
+        name: '发现组件',
+        icon: 'iconfont iconrequpc',
+        id: 13,
+        components: markRaw(discoverRef.value),
+        edit: markRaw(discoverEditRef.value)
+      }
+    ]
+  }
+]);
+
+const componentList = ref<any>([
+  // {
+  //   components: ctextTitleRef
+  // }
+]);
+const handleChange = (val: string[]) => {};
+// 返回上一页
+const goBack = () => {};
+
+// 预览
+const preview = () => {};
+
+// 保存
+const save = () => {
+  const datas = {
+    name: query.title,
+    siteId: '',
+    clientId: '',
+    type: query.type,
+    remark: '',
+    previewPicUrls: '',
+    property: JSON.stringify(diyStore.componentList),
+    isHome: 1
+  };
+  const api = pcAddDiy;
+  api(datas)
+    .then((res: any) => {
+      if (res.code == 200) {
+      }
+    })
+    .catch(() => {});
+};
+</script>
+
+<style lang="scss" scoped>
+.pcEdit-pages {
+  // min-height: calc(100vh - 84px);
+  // min-width: 1900px;
+  height: calc(100vh);
+
+  .full-container {
+    height: calc(100vh - 50px);
+    background-color: #f2f2f2;
+
+    .component-list {
+      height: 100%;
+      background-color: #ffffff;
+      padding: 0 10px;
+    }
+
+    .component-list ul li {
+      &:not(.disabled):hover {
+        color: var(--el-color-primary);
+        background: var(--el-color-primary-light-9);
+      }
+    }
+
+    .preview-wrap {
+      flex: 1;
+      display: flex;
+      justify-content: center;
+
+      .preview-box {
+        width: 1350px;
+        position: relative;
+      }
+
+      .preview-pages {
+        // margin: 30px auto;
+        width: 1300px;
+        background: var(--el-bg-color-page);
+        overflow-y: auto;
+        // height: calc(130vh - 194px);
+        // zoom: 0.7;
+        // height: calc(100vh - 194px);
+        height: 100%;
+
+        /* 为了兼容某些情况,可能还需要配合 display */
+        display: inline-block;
+
+        .component-bos {
+          position: relative;
+          .component-box {
+            position: absolute;
+            width: 100%;
+            height: 100%;
+            top: 0;
+            left: 0;
+            border: 2px solid var(--el-color-primary);
+            z-index: 2;
+            cursor: move;
+          }
+        }
+      }
+
+      .quick-action {
+        background: var(--el-bg-color);
+        position: absolute;
+        top: 20px;
+        right: 0px;
+      }
+    }
+
+    //编辑组件属性区域
+    .edit-attribute-wrap {
+      background: var(--el-bg-color);
+    }
+
+    .edit-attribute-wrap .box-card {
+      border: none;
+    }
+
+    .edit-attr-item-wrap {
+      border-top: 2px solid var(--el-color-info-light-8);
+      padding-top: 20px;
+
+      &:first-of-type {
+        border-top: none;
+        padding-top: 0;
+      }
+    }
+  }
+
+  :deep(.el-header) {
+    height: 50px;
+  }
+}
+</style>

+ 77 - 0
vite.config copy.ts

@@ -0,0 +1,77 @@
+import { defineConfig, loadEnv } from 'vite';
+import createPlugins from './vite/plugins';
+import autoprefixer from 'autoprefixer'; // css自动添加兼容性前缀
+import path from 'path';
+
+export default defineConfig(({ mode, command }) => {
+  const env = loadEnv(mode, process.cwd());
+  return {
+    // 部署生产环境和开发环境下的URL。
+    // 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上
+    // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
+    base: env.VITE_APP_CONTEXT_PATH,
+    resolve: {
+      alias: {
+        '@': path.resolve(__dirname, './src')
+      },
+      extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
+    },
+    // https://cn.vitejs.dev/config/#resolve-extensions
+    plugins: createPlugins(env, command === 'build'),
+    server: {
+      host: '0.0.0.0',
+      port: Number(env.VITE_APP_PORT),
+      open: true,
+      proxy: {
+        [env.VITE_APP_BASE_API]: {
+          // target: 'http://localhost:8080',
+          // target: 'http://192.168.1.52:8080',
+          //target: 'http://yp1.yingpaipay.com:9026',
+          target: 'http://119.97.180.88:8080',
+          // http://192.168.1.52:8080
+          changeOrigin: true,
+          ws: true,
+          rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')
+        }
+      }
+    },
+    css: {
+      preprocessorOptions: {
+        scss: {
+          // additionalData: '@use "@/assets/styles/variables.module.scss as *";'
+          // javascriptEnabled: true
+          api: 'modern-compiler'
+        }
+      },
+      postcss: {
+        plugins: [
+          // 浏览器兼容性
+          autoprefixer(),
+          {
+            postcssPlugin: 'internal:charset-removal',
+            AtRule: {
+              charset: (atRule) => {
+                atRule.remove();
+              }
+            }
+          }
+        ]
+      }
+    },
+    // 预编译
+    optimizeDeps: {
+      include: [
+        'vue',
+        'vue-router',
+        'pinia',
+        'axios',
+        '@vueuse/core',
+        'echarts',
+        'vue-i18n',
+        '@vueup/vue-quill',
+        'image-conversion',
+        'element-plus/es/components/**/css'
+      ]
+    }
+  };
+});