weixin_52219567 преди 1 месец
родител
ревизия
7010d5fd7f
променени са 30 файла, в които са добавени 1797 реда и са изтрити 661 реда
  1. 9 0
      src/api/diy/index.ts
  2. BIN
      src/assets/images/pcdiy/brand1.png
  3. BIN
      src/assets/images/pcdiy/code.png
  4. BIN
      src/assets/images/pcdiy/logo1.png
  5. BIN
      src/assets/images/pcdiy/logo2.png
  6. 655 0
      src/components/heat-map/index.vue
  7. 203 3
      src/store/modules/pcdiy.ts
  8. 28 0
      src/utils/common.ts
  9. 99 26
      src/views/diy/pcEdit.vue
  10. 8 1
      src/views/diy/pcList.vue
  11. 2 2
      src/views/diy/pccomponents/edit/advert-edit.vue
  12. 5 5
      src/views/diy/pccomponents/edit/brand-edit.vue
  13. 12 2
      src/views/diy/pccomponents/edit/carousel-edit.vue
  14. 39 9
      src/views/diy/pccomponents/edit/head-edit.vue
  15. 67 0
      src/views/diy/pccomponents/edit/hot-edit copy.vue
  16. 92 0
      src/views/diy/pccomponents/edit/hot-edit.vue
  17. 1 1
      src/views/diy/pccomponents/edit/imageCube-edit.vue
  18. 2 2
      src/views/diy/pccomponents/edit/navigation-edit.vue
  19. 86 0
      src/views/diy/pccomponents/edit/pages-edit.vue
  20. 13 3
      src/views/diy/pccomponents/index.ts
  21. 19 9
      src/views/diy/pccomponents/pages/advert.vue
  22. 26 12
      src/views/diy/pccomponents/pages/article.vue
  23. 25 11
      src/views/diy/pccomponents/pages/brand.vue
  24. 25 3
      src/views/diy/pccomponents/pages/carousel.vue
  25. 187 91
      src/views/diy/pccomponents/pages/head.vue
  26. 89 0
      src/views/diy/pccomponents/pages/hot.vue
  27. 13 2
      src/views/diy/pccomponents/pages/imageCube.vue
  28. 16 5
      src/views/diy/pccomponents/pages/navigation.vue
  29. 28 17
      src/views/diy/pccomponents/pages/textTitle.vue
  30. 48 457
      src/views/diy/pcdiy.vue

+ 9 - 0
src/api/diy/index.ts

@@ -63,6 +63,15 @@ export function pcAddDiy(data: any) {
   });
 }
 
+// pc编辑自定义页面
+export function pcEditDiy(data: any) {
+  return request({
+    url: '/mall/diyPcPage',
+    method: 'put',
+    data: data
+  });
+}
+
 // pc删除自定义页面
 export function pcDelDiy(id: any) {
   return request({

BIN
src/assets/images/pcdiy/brand1.png


BIN
src/assets/images/pcdiy/code.png


BIN
src/assets/images/pcdiy/logo1.png


BIN
src/assets/images/pcdiy/logo2.png


+ 655 - 0
src/components/heat-map/index.vue

@@ -0,0 +1,655 @@
+<template>
+  <div>
+    <div @click="show">
+      <slot>
+        <div v-if="value.heatMapData.length" class="cursor-pointer">
+          已选<span class="text-primary p-[4px]">{{ value.heatMapData.length }}</span
+          >个热区
+        </div>
+        <div v-else class="cursor-pointer">添加热区</div>
+      </slot>
+    </div>
+    <el-dialog v-model="showDialog" title="热区设置" width="810px" :destroy-on-close="true" :close-on-click-modal="false">
+      <div class="flex">
+        <div
+          class="hot-area-img-wrap content-box relative bg-gray-100 border border-dashed border-gray-500 bg-no-repeat"
+          :style="{ backgroundImage: 'url(' + img(value.imageUrl) + ')', width: contentBoxWidth + 'px', height: contentBoxHeight + 'px' }"
+        >
+          <div
+            v-for="(item, index) in dragBoxArr"
+            :id="'box_' + index"
+            :key="index"
+            class="area-box border border-solid border-[#ccc] w-[100px] h-[100px] absolute top-0 left-0 select-none p-[5px]"
+            :style="{ left: item.left + item.unit, top: item.top + item.unit, width: item.width + item.unit, height: item.height + item.unit }"
+            @mousedown="mouseDown($event, index)"
+          >
+            <span>{{ Number(index) + 1 }}</span>
+            <template v-if="item.link">
+              <span class="p-[4px]">|</span>
+              <span>{{ item.link }}</span>
+            </template>
+            <span class="box1" @mousedown.stop="resizeMouseDown($event, index)"></span>
+            <span class="box2" @mousedown.stop="resizeMouseDown($event, index)"></span>
+            <span class="box3" @mousedown.stop="resizeMouseDown($event, index)"></span>
+            <span class="box4" @mousedown.stop="resizeMouseDown($event, index)"></span>
+          </div>
+        </div>
+
+        <el-form label-width="80px" class="pl-[20px]">
+          <h3 class="mb-[10px] text-lg text-black">热区管理</h3>
+          <el-button type="primary" plain size="small" class="mb-[10px]" @click="addArea">添加热区</el-button>
+          <div class="overflow-y-auto h-[300px]">
+            <template v-for="(item, index) in dragBoxArr" :key="index">
+              <div class="mb-[16px]" v-if="item">
+                <el-form-item :label="'热区' + (Number(index) + 1)">
+                  <div class="flex items-center">
+                    <div style="width: 230px">
+                      <WebLinkInput v-model="item.link" placeholder="请输入或选择链接" />
+                    </div>
+                    <icon
+                      class="del cursor-pointer mx-[10px]"
+                      name="element CircleCloseFilled"
+                      color="#bbb"
+                      size="20px"
+                      @click="dragBoxArr.splice(index, 1)"
+                    />
+                  </div>
+                </el-form-item>
+              </div>
+            </template>
+          </div>
+        </el-form>
+      </div>
+
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="showDialog = false">返回</el-button>
+          <el-button type="primary" @click="save">确定</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, computed } from 'vue';
+import { ElMessage } from 'element-plus';
+import { img, deepClone } from '@/utils/common';
+
+const prop = defineProps({
+  modelValue: {
+    type: String,
+    default: ''
+  }
+});
+
+const emit = defineEmits(['update:modelValue']);
+
+const value: any = computed({
+  get() {
+    return prop.modelValue;
+  },
+  set(value) {
+    emit('update:modelValue', value);
+  }
+});
+
+/**
+ * 公式:
+ * 宽度:400
+ * 比例:原图高/原图宽,示例:466/698=0.66
+ * 高度:宽度*比例,示例:400*0.66=264
+ */
+
+const showDialog = ref(false);
+const contentBoxWidth = ref(400);
+const contentBoxHeight = ref(400);
+const dragBoxArr: any = reactive([]);
+
+const imgRatio = ref(1); // 图片比例
+
+// 热区尺寸
+const areaRadio = ref(0.25); // 占位图比例
+const areaWidth = ref(100);
+const areaHeight = ref(100);
+const areaNum = ref(4); // 每行显示的数量
+
+// 添加热区
+const addArea = () => {
+  let left = (dragBoxArr.length % areaNum.value) * areaWidth.value;
+  let top = Math.floor(dragBoxArr.length / areaNum.value) * areaHeight.value;
+  const edgeHeight = top + areaHeight.value / 2;
+  if (top >= contentBoxHeight.value || edgeHeight >= contentBoxHeight.value) {
+    top = 0;
+    left = 0;
+  }
+
+  dragBoxArr.push({
+    left,
+    top,
+    width: areaWidth.value,
+    height: areaHeight.value,
+    unit: 'px',
+    link: ''
+  });
+};
+
+// 移动事件
+const mouseDown = (e: any, index: any) => {
+  const box: any = document.getElementById('box_' + index);
+  const disX = e.clientX - box.offsetLeft;
+  const disY = e.clientY - box.offsetTop;
+
+  // 鼠标移动时
+  document.onmousemove = function (e) {
+    box.style.left = e.clientX - disX + 'px';
+    box.style.top = e.clientY - disY + 'px';
+
+    // 边界判断
+    if (e.clientX - disX < 0) {
+      box.style.left = 0;
+    }
+
+    if (e.clientX - disX > contentBoxWidth.value - box.offsetWidth) {
+      box.style.left = contentBoxWidth.value - box.offsetWidth + 'px';
+    }
+
+    if (e.clientY - disY < 0) {
+      box.style.top = 0;
+    }
+
+    if (e.clientY - disY > contentBoxHeight.value - box.offsetHeight) {
+      box.style.top = contentBoxHeight.value - box.offsetHeight + 'px';
+    }
+
+    dragBoxArr[index].left = box.offsetLeft;
+    dragBoxArr[index].top = box.offsetTop;
+    dragBoxArr[index].width = box.offsetWidth;
+    dragBoxArr[index].height = box.offsetHeight;
+    dragBoxArr[index].unit = 'px';
+  };
+
+  // 鼠标抬起时
+  document.onmouseup = function (e) {
+    document.onmousemove = null;
+  };
+};
+
+// 拖拽大小事件
+const resizeMouseDown = (e: any, index: any) => {
+  const oEv = e;
+  oEv.stopPropagation();
+  const box: any = document.getElementById('box_' + index);
+  const className = e.target.className;
+
+  // 获取移动前盒子的宽高,
+  const oldWidth = box.offsetWidth;
+  const oldHeight = box.offsetHeight;
+
+  // 获取鼠标距离屏幕的left和top值
+  const oldX = oEv.clientX;
+  const oldY = oEv.clientY;
+
+  // 元素相对于最近的父级定位
+  const oldLeft = box.offsetLeft;
+  const oldTop = box.offsetTop;
+
+  // 设置最小的宽度
+  const minWidth = 50;
+  const minHeight = 50;
+
+  document.onmousemove = function (e) {
+    const oEv = e;
+    // console.log('move', "width:" + oldWidth,
+    //     ',oldLeft: ' + oldLeft, ',oldTop: ' + oldTop,
+    //     ',oldX:clientX-- ' + oldX + ':' + oEv.clientX,
+    //     ',oldY:clientY-- ' + oldY + ':' + oEv.clientY,
+    // )
+
+    // 左上角
+    if (className == 'box1') {
+      let width = oldWidth - (oEv.clientX - oldX);
+      const maxWidth = contentBoxWidth.value;
+
+      let height = oldHeight - (oEv.clientY - oldY);
+      const maxHeight = contentBoxHeight.value - oldTop;
+
+      let left = oldLeft + (oEv.clientX - oldX);
+      let top = oldTop + (oEv.clientY - oldY);
+
+      if (width < minWidth) {
+        width = minWidth;
+      }
+      if (width > maxWidth) {
+        width = maxWidth;
+      }
+
+      if (height < minHeight) {
+        height = minHeight;
+      }
+      if (height > maxHeight) {
+        height = maxHeight;
+      }
+
+      if (oldLeft == 0 && oldTop == 0) {
+        // 坐标:left = 0,top = 0
+
+        if (width == minWidth && height == minHeight) {
+          // 宽高 = 最小值,left = 最小宽度,top = 最小高度
+          left = minWidth;
+          top = minHeight;
+        } else if (width == minWidth && height > minHeight) {
+          // 宽 = 最小值,高 > 最小值,left = 最小宽度,top = 不予处理
+          left = minWidth;
+        } else if (width > minWidth && height == minHeight) {
+          // 宽 > 最小值,高 = 最小值,left = 不予处理,top = 最小高度
+          top = minHeight;
+        } else if (width > minWidth && height > minHeight) {
+          // 宽 > 最小值,高 > 最小值,left = 不予处理,top = 不予处理
+        }
+      } else if (oldLeft == 0 && oldTop > 0) {
+        // 坐标:left = 0,top > 0
+
+        if (width == minWidth && height == minHeight) {
+          // 宽高 = 最小值,left = 最小宽度,top = 元素上偏移位置
+          left = minWidth;
+          top = box.offsetTop;
+        } else if (width == minWidth && height > minHeight) {
+          // 宽 = 最小值,高 > 最小值,left = 最小宽度,top = 元素上偏移位置
+          left = minWidth;
+          top = box.offsetTop;
+        } else if (width > minWidth && height == minHeight) {
+          // 宽 > 最小值,高 = 最小值,left = 不予处理,top = 元素上偏移位置
+          top = box.offsetTop;
+        } else if (width > minWidth && height > minHeight) {
+          // 宽 > 最小值,高 > 最小值,left = 不予处理,top = 不予处理
+        }
+      } else if (oldLeft > 0 && oldTop == 0) {
+        // 坐标:left > 0,top = 0
+
+        if (width == minWidth && height == minHeight) {
+          // 宽高 = 最小值,left = 元素左偏移位置,top = 元素上偏移位置
+          left = box.offsetLeft;
+          top = box.offsetTop;
+        } else if (width == minWidth && height > minHeight) {
+          // 宽 = 最小值,高 > 最小值,left = 元素左偏移位置,top = 0
+          left = box.offsetLeft;
+          top = 0;
+        } else if (width > minWidth && height == minHeight) {
+          // 宽 > 最小值,高 = 最小值,left = 不予处理,top = 元素上偏移位置
+          top = box.offsetTop;
+        } else if (width > minWidth && height > minHeight) {
+          // 宽 > 最小值,高 > 最小值,left = 不予处理,top = 不予处理
+        }
+      } else if (oldLeft > 0 && oldTop > 0) {
+        // 坐标:left > 0,top > 0
+
+        if (width == minWidth && height == minHeight) {
+          // 宽高 = 最小值,left = 元素左偏移位置,top = 元素上偏移位置
+          left = box.offsetLeft;
+          top = box.offsetTop;
+        } else if (width == minWidth && height > minHeight) {
+          // 宽 = 最小值,高 > 最小值,left = 元素左偏移位置,top = 元素上偏移位置
+          left = box.offsetLeft;
+          top = box.offsetTop;
+        } else if (width > minWidth && height == minHeight) {
+          // 宽 > 最小值,高 = 最小值,left = 不予处理,top = 元素上偏移位置
+          top = box.offsetTop;
+        } else if (width > minWidth && height > minHeight) {
+          // 宽 > 最小值,高 > 最小值,left = 不予处理,top = 不予处理
+        }
+      }
+
+      // 左上宽
+      if (left < 0) {
+        left = 0;
+        width = oldWidth - (oEv.clientX - oldX) + (oldLeft + (oEv.clientX - oldX));
+      }
+
+      // 左上 高
+      if (top < 0) {
+        top = 0;
+        height = oldTop + (oEv.clientY - oldY) + (oldHeight - (oEv.clientY - oldY));
+      }
+
+      box.style.width = width + 'px';
+      box.style.height = height + 'px';
+      box.style.left = left + 'px';
+      box.style.top = top + 'px';
+    } else if (className == 'box2') {
+      // 右上角
+
+      let width = oldWidth + (oEv.clientX - oldX);
+      const maxWidth = contentBoxWidth.value - oldLeft;
+
+      let height = oldHeight - (oEv.clientY - oldY);
+      const maxHeight = contentBoxHeight.value - oldTop;
+
+      let top = oldTop + (oEv.clientY - oldY);
+
+      if (width < minWidth) {
+        width = minWidth;
+      }
+      if (width > maxWidth) {
+        width = maxWidth;
+      }
+
+      if (height < minHeight) {
+        height = minHeight;
+      }
+      if (height > maxHeight) {
+        height = maxHeight;
+      }
+
+      if (oldLeft == 0 && oldTop == 0) {
+        // 坐标:left = 0,top = 0
+
+        if (width == minWidth && height == minHeight) {
+          // 宽高 = 最小值,top = 最小高度
+          top = minHeight;
+        } else if (width == minWidth && height > minHeight) {
+          // 宽 = 最小值,高 > 最小值,不予处理
+        } else if (width > minWidth && height == minHeight) {
+          // 宽 > 最小值,高 = 最小值,top = 最小高度
+          top = minHeight;
+        } else if (width > minWidth && height > minHeight) {
+          // 宽 > 最小值,高 > 最小值,不予处理
+        }
+      } else if (oldLeft == 0 && oldTop > 0) {
+        // 坐标:left = 0,top > 0
+
+        if (width == minWidth && height == minHeight) {
+          // 宽高 = 最小值,top = 元素上偏移位置
+          top = box.offsetTop;
+        } else if (width == minWidth && height > minHeight) {
+          // 宽 = 最小值,高 > 最小值,top = 元素上偏移位置
+          top = box.offsetTop;
+        } else if (width > minWidth && height == minHeight) {
+          // 宽 > 最小值,高 = 最小值,top = 元素上偏移位置
+          top = box.offsetTop;
+        } else if (width > minWidth && height > minHeight) {
+          // 宽 > 最小值,高 > 最小值,不予处理
+        }
+      } else if (oldLeft > 0 && oldTop == 0) {
+        // 坐标:left = 0,top = 0
+
+        if (width == minWidth && height == minHeight) {
+          // 宽高 = 最小值,top = 元素上偏移位置
+          top = box.offsetTop;
+        } else if (width == minWidth && height > minHeight) {
+          // 宽 = 最小值,高 > 最小值,top = 0
+          top = 0;
+        } else if (width > minWidth && height == minHeight) {
+          // 宽 > 最小值,高 = 最小值,top = 元素上偏移位置
+          top = box.offsetTop;
+        } else if (width > minWidth && height > minHeight) {
+          // 宽 > 最小值,高 > 最小值,不予处理
+        }
+      } else if (oldLeft > 0 && oldTop > 0) {
+        // 坐标:left > 0,top > 0
+
+        if (width == minWidth && height == minHeight) {
+          // 宽高 = 最小值,top = 元素上偏移位置
+          top = box.offsetTop;
+        } else if (width == minWidth && height > minHeight) {
+          // 宽 = 最小值,高 > 最小值,top = 元素上偏移位置
+          top = box.offsetTop;
+        } else if (width > minWidth && height == minHeight) {
+          // 宽 > 最小值,高 = 最小值,top = 元素上偏移位置
+          top = box.offsetTop;
+        } else if (width > minWidth && height > minHeight) {
+          // 宽 > 最小值,高 > 最小值,不予处理
+        }
+      }
+
+      // 右上高
+      if (top < 0) {
+        top = 0;
+        height = oldTop + (oEv.clientY - oldY) + (oldHeight - (oEv.clientY - oldY));
+      }
+
+      box.style.width = width + 'px';
+      box.style.height = height + 'px';
+      box.style.top = top + 'px';
+    } else if (className == 'box3') {
+      // 左下角
+
+      let width = oldWidth - (oEv.clientX - oldX);
+      const maxWidth = contentBoxWidth.value;
+
+      let height = oldHeight + (oEv.clientY - oldY);
+      const maxHeight = contentBoxHeight.value - oldTop;
+
+      let left = oldLeft + (oEv.clientX - oldX);
+
+      if (width < minWidth) {
+        width = minWidth;
+      }
+      if (width > maxWidth) {
+        width = maxWidth;
+      }
+
+      if (height < minHeight) {
+        height = minHeight;
+      }
+      if (height > maxHeight) {
+        height = maxHeight;
+      }
+
+      if (oldLeft == 0 && oldTop == 0) {
+        // 坐标:left = 0,top = 0
+
+        if (width == minWidth && height == minHeight) {
+          // 宽高 = 最小值,left = 最小宽度
+          left = minWidth;
+        } else if (width == minWidth && height > minHeight) {
+          // 宽 = 最小值,高 > 最小值,left = 最小宽度
+          left = minWidth;
+        } else if (width > minWidth && height == minHeight) {
+          // 宽 > 最小值,高 = 最小值,不予处理
+        } else if (width > minWidth && height > minHeight) {
+          // 宽 > 最小值,高 > 最小值,不予处理
+        }
+      } else if (oldLeft == 0 && oldTop > 0) {
+        // 坐标:left = 0,top > 0
+
+        if (width == minWidth && height == minHeight) {
+          // 宽高 = 最小值,left = 最小宽度
+          left = minWidth;
+        } else if (width == minWidth && height > minHeight) {
+          // 宽 = 最小值,高 > 最小值,left = 最小宽度
+          left = minWidth;
+        } else if (width > minWidth && height == minHeight) {
+          // 宽 > 最小值,高 = 最小值,不予处理
+        } else if (width > minWidth && height > minHeight) {
+          // 宽 > 最小值,高 > 最小值,不予处理
+        }
+      } else if (oldLeft > 0 && oldTop == 0) {
+        // 坐标:left > 0,top = 0
+
+        if (width == minWidth && height == minHeight) {
+          // 宽高 = 最小值,left = 元素左偏移位置
+          left = box.offsetLeft;
+        } else if (width == minWidth && height > minHeight) {
+          // 宽 = 最小值,高 > 最小值,left = 元素左偏移位置
+          left = box.offsetLeft;
+        } else if (width > minWidth && height == minHeight) {
+          // 宽 > 最小值,高 = 最小值,不予处理
+        } else if (width > minWidth && height > minHeight) {
+          // 宽 > 最小值,高 > 最小值,不予处理
+        }
+      } else if (oldLeft > 0 && oldTop > 0) {
+        // 坐标:left > 0,top > 0
+
+        if (width == minWidth && height == minHeight) {
+          // 宽高 = 最小值,left = 元素左偏移位置
+          left = box.offsetLeft;
+        } else if (width == minWidth && height > minHeight) {
+          // 宽 = 最小值,高 > 最小值,left = 元素左偏移位置
+          left = box.offsetLeft;
+        } else if (width > minWidth && height == minHeight) {
+          // 宽 > 最小值,高 = 最小值,不予处理
+        } else if (width > minWidth && height > minHeight) {
+          // 宽 > 最小值,高 > 最小值,不予处理
+        }
+      }
+
+      if (left < 0) {
+        left = 0;
+        width = oldWidth - (oEv.clientX - oldX) + (oldLeft + (oEv.clientX - oldX));
+      }
+
+      box.style.width = width + 'px';
+      box.style.height = height + 'px';
+      box.style.left = left + 'px';
+    } else if (className == 'box4') {
+      // 右下角
+
+      let width = oldWidth + (oEv.clientX - oldX);
+      const maxWidth = contentBoxWidth.value - oldLeft;
+
+      let height = oldHeight + (oEv.clientY - oldY);
+      const maxHeight = contentBoxHeight.value - oldTop;
+
+      if (width < minWidth) {
+        width = minWidth;
+      }
+      if (width > maxWidth) {
+        width = maxWidth;
+      }
+
+      if (height < minHeight) {
+        height = minHeight;
+      }
+      if (height > maxHeight) {
+        height = maxHeight;
+      }
+
+      box.style.width = width + 'px';
+      box.style.height = height + 'px';
+    }
+
+    dragBoxArr[index].left = box.offsetLeft;
+    dragBoxArr[index].top = box.offsetTop;
+    dragBoxArr[index].width = box.offsetWidth;
+    dragBoxArr[index].height = box.offsetHeight;
+    dragBoxArr[index].unit = 'px';
+  };
+
+  // 鼠标抬起时
+  document.onmouseup = function () {
+    document.onmousemove = null;
+    document.onmouseup = null;
+  };
+};
+
+const show = () => {
+  // 每次打开时赋值
+  if (!value.value.imageUrl) {
+    ElMessage({
+      type: 'warning',
+      message: '请上传图片'
+    });
+    return;
+  }
+
+  // 计算图片比例
+  imgRatio.value = value.value.imgHeight / value.value.imgWidth;
+
+  // 根据图片比例,调整图片高度
+  contentBoxHeight.value = Math.floor(contentBoxWidth.value * imgRatio.value);
+
+  areaWidth.value = Math.floor(areaRadio.value * contentBoxHeight.value);
+  areaHeight.value = areaWidth.value;
+  areaNum.value = Math.floor(contentBoxWidth.value / areaWidth.value);
+
+  if (Object.keys(value.value.heatMapData).length) {
+    dragBoxArr.splice(0, dragBoxArr.length, ...value.value.heatMapData);
+  } else {
+    dragBoxArr.splice(0, dragBoxArr.length);
+    addArea();
+  }
+  showDialog.value = true;
+};
+
+const save = () => {
+  let isOk = true;
+  for (let i = 0; i < dragBoxArr.length; i++) {
+    if (!dragBoxArr[i].link) {
+      ElMessage({
+        type: 'warning',
+        message: '请选择热区' + (i + 1) + '的链接地址'
+      });
+      isOk = false;
+      break;
+    }
+  }
+
+  if (!isOk) return;
+
+  dragBoxArr.forEach((item: any, index: number) => {
+    const box: any = document.getElementById('box_' + index);
+    item.width = Number(((box.offsetWidth / contentBoxWidth.value) * 100).toFixed(2));
+    item.height = Number(((box.offsetHeight / contentBoxHeight.value) * 100).toFixed(2));
+    item.left = Number(((box.offsetLeft / contentBoxWidth.value) * 100).toFixed(2));
+    item.top = Number(((box.offsetTop / contentBoxHeight.value) * 100).toFixed(2));
+    item.unit = '%';
+  });
+
+  value.value.heatMapData = deepClone(dragBoxArr);
+
+  showDialog.value = false;
+};
+
+defineExpose({
+  showDialog
+});
+</script>
+
+<style lang="scss" scoped>
+.area-box {
+  background-color: rgba(255, 255, 255, 0.7);
+}
+
+.hot-area-img-wrap {
+  // width: 1200px;
+
+  background-size: 100%;
+}
+
+.box1,
+.box2,
+.box3,
+.box4 {
+  width: 10px;
+  height: 10px;
+  background-color: #fff;
+  position: absolute;
+  border-radius: 50%;
+  border: 1px solid #333;
+}
+
+.box1 {
+  top: -5px;
+  left: -5px;
+  cursor: nw-resize;
+}
+
+.box2 {
+  top: -5px;
+  right: -5px;
+  cursor: ne-resize;
+}
+
+.box3 {
+  left: -5px;
+  bottom: -5px;
+  cursor: sw-resize;
+}
+
+.box4 {
+  bottom: -5px;
+  right: -5px;
+  cursor: se-resize;
+}
+</style>

+ 203 - 3
src/store/modules/pcdiy.ts

@@ -4,9 +4,13 @@ import { ElMessage, ElMessageBox } from 'element-plus';
 import { cloneDeep } from 'lodash-es';
 import { fa } from 'element-plus/es/locale/index.mjs';
 
-const usePcdiyStore = defineStore('pcdiy', {
+const usePcdiyStore: any = defineStore('pcdiy', {
   state: () => {
     return {
+      name: '',
+      type: 1,
+      backgroundColor: '#f2f2f2',
+      hoverColor: '#E7000B',
       componentList: [],
       uniqueIdCounter: 0,
       currentIndex: -99, // 当前正在编辑的组件下标
@@ -53,6 +57,10 @@ const usePcdiyStore = defineStore('pcdiy', {
             bottom: 0, // 下边距
             both: 0 // 左右边距
           },
+          margin: {
+            top: 0, // 上边距
+            bottom: 0 // 下边距
+          },
           isHidden: false // 是否隐藏该组件 true:是,false:否,增加问号说明:勾选后该组件将隐藏,适用于你不希望看到该组件字段又不希望删除的情况;
         }
       }
@@ -70,6 +78,13 @@ const usePcdiyStore = defineStore('pcdiy', {
   actions: {
     // 添加组件
     addComponent(item: any, key: any) {
+      if (this.componentList.length > 0 && item.id == 1) {
+        const headBoo = this.componentList.some((item2) => item2.id == 1);
+        if (headBoo) {
+          ElMessage.error('头部组件只能添加一次~');
+          return;
+        }
+      }
       if (this.componentList.length == 0) {
         this.uniqueIdCounter = 0;
       }
@@ -112,9 +127,12 @@ const usePcdiyStore = defineStore('pcdiy', {
         newItem.leftColor2 = '#364153';
 
         newItem.carouselStyle = false;
-        newItem.carouselType = 0;
+        newItem.carouselType = 1;
         newItem.carouselRadius = 10;
         newItem.carouselInterval = 3000;
+        newItem.carouselPosition = 2;
+        newItem.carouselStyleType = 1;
+        newItem.carouselStyleColor = '#ffffff';
 
         newItem.centreType = 1;
         newItem.carouselList = [
@@ -162,6 +180,41 @@ const usePcdiyStore = defineStore('pcdiy', {
         newItem.realNumber = 5;
         newItem.realIds = [];
         newItem.navlList = [
+          {
+            imageUrl: '',
+            title: '导航名称',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '导航名称',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '导航名称',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '导航名称',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '导航名称',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
           {
             imageUrl: '',
             title: '导航名称',
@@ -171,6 +224,7 @@ const usePcdiyStore = defineStore('pcdiy', {
           }
         ];
         newItem.rightRadius = 5;
+        newItem.login = 1;
       } else if (item.id == 2) {
         // 文本标题
         newItem.styleType = 1;
@@ -202,6 +256,22 @@ const usePcdiyStore = defineStore('pcdiy', {
         newItem.number = 3;
         newItem.count = 1;
         newItem.navlList = [
+          {
+            imageUrl: '',
+            title: '标题名称',
+            subtitle: '副标题名称',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '标题名称',
+            subtitle: '副标题名称',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
           {
             imageUrl: '',
             title: '标题名称',
@@ -267,13 +337,15 @@ const usePcdiyStore = defineStore('pcdiy', {
           {
             imageUrl: '',
             imgType: 1,
-            id: Date.now()
+            id: Date.now(),
+            url: ''
           }
         ];
         newItem.interval = 3000;
         newItem.styleType = 1;
         newItem.position = 2;
         newItem.imageRadius = 10;
+        newItem.carouselStyleColor = '#ffffff';
         newItem.ignore = ['componentBgColor', 'topRounded', 'bottomRounded'];
       } else if (item.id == 7) {
         //文章咨询
@@ -292,6 +364,94 @@ const usePcdiyStore = defineStore('pcdiy', {
         newItem.url = '';
         newItem.imgType = 1;
         newItem.brandList = [
+          {
+            imageUrl: '',
+            title: '品牌名称',
+            subtitle: '品牌介绍',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '品牌名称',
+            subtitle: '品牌介绍',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '品牌名称',
+            subtitle: '品牌介绍',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '品牌名称',
+            subtitle: '品牌介绍',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '品牌名称',
+            subtitle: '品牌介绍',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '品牌名称',
+            subtitle: '品牌介绍',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '品牌名称',
+            subtitle: '品牌介绍',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '品牌名称',
+            subtitle: '品牌介绍',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '品牌名称',
+            subtitle: '品牌介绍',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '品牌名称',
+            subtitle: '品牌介绍',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '品牌名称',
+            subtitle: '品牌介绍',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
           {
             imageUrl: '',
             title: '品牌名称',
@@ -314,6 +474,22 @@ const usePcdiyStore = defineStore('pcdiy', {
         newItem.number = 3;
         newItem.count = 1;
         newItem.navlList = [
+          {
+            imageUrl: '',
+            title: '标题名称',
+            subtitle: '副标题名称',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            title: '标题名称',
+            subtitle: '副标题名称',
+            url: '',
+            imgType: 1,
+            id: Date.now()
+          },
           {
             imageUrl: '',
             title: '标题名称',
@@ -539,6 +715,13 @@ const usePcdiyStore = defineStore('pcdiy', {
           }
         ];
         newItem.goodsIds = [];
+      } else if (item.id == 14) {
+        // 热区
+        newItem.title = '456';
+        newItem.imageUrl = '';
+        newItem.imgWidth = 0;
+        newItem.imgHeight = 0;
+        newItem.heatMapData = [];
       }
 
       this.componentList.push(newItem);
@@ -552,6 +735,14 @@ const usePcdiyStore = defineStore('pcdiy', {
       if (this.currentIndex <= 0 || this.currentIndex >= this.componentList.length) {
         return;
       }
+      if (this.componentList[this.currentIndex].id == 1) {
+        ElMessage.error('头部组件不可移动~');
+        return;
+      }
+      if (this.currentIndex == 1 && this.componentList[0].id == 1) {
+        ElMessage.error('头部组件不可移动~');
+        return;
+      }
       [this.componentList[this.currentIndex], this.componentList[this.currentIndex - 1]] = [
         this.componentList[this.currentIndex - 1],
         this.componentList[this.currentIndex]
@@ -562,6 +753,10 @@ const usePcdiyStore = defineStore('pcdiy', {
       if (this.currentIndex < 0 || this.currentIndex >= this.componentList.length - 1) {
         return;
       }
+      if (this.componentList[this.currentIndex].id == 1) {
+        ElMessage.error('头部组件不可移动~');
+        return;
+      }
       [this.componentList[this.currentIndex], this.componentList[this.currentIndex + 1]] = [
         this.componentList[this.currentIndex + 1],
         this.componentList[this.currentIndex]
@@ -577,11 +772,16 @@ const usePcdiyStore = defineStore('pcdiy', {
     //删除
     delComponent() {
       this.componentList.splice(this.currentIndex, 1);
+      this.currentIndex = -99;
     },
     //点击组件
     onComponent(item: any, index: any) {
       this.currentKey = item.itemKey;
       this.currentIndex = index;
+    },
+    // 修改index值
+    onIndex(index: any) {
+      this.currentIndex = index;
     }
   }
 });

+ 28 - 0
src/utils/common.ts

@@ -7,6 +7,13 @@ export function isUrl(str: string): boolean {
   return str.indexOf('http://') != -1 || str.indexOf('https://') != -1;
 }
 
+const isArray = (value: any) => {
+  if (typeof Array.isArray === 'function') {
+    return Array.isArray(value);
+  }
+  return Object.prototype.toString.call(value) === '[object Array]';
+};
+
 /**
  * 图片输出
  * @param path
@@ -21,3 +28,24 @@ export function img(path: string): string {
     return isUrl(path) ? path : `${imgDomain}/${path}`;
   }
 }
+
+/**
+ * @description 深度克隆
+ * @param {object} obj 需要深度克隆的对象
+ * @returns {*} 克隆后的对象或者原值(不是对象)
+ */
+export function deepClone(obj: any) {
+  // 对常见的“非”值,直接返回原来值
+  if ([null, undefined, NaN, false].includes(obj)) return obj;
+  if (typeof obj !== 'object' && typeof obj !== 'function') {
+    // 原始类型直接返回
+    return obj;
+  }
+  const o = isArray(obj) ? [] : {};
+  for (const i in obj) {
+    if (obj.hasOwnProperty(i)) {
+      o[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i];
+    }
+  }
+  return o;
+}

+ 99 - 26
src/views/diy/pcEdit.vue

@@ -9,11 +9,11 @@
       </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>
+        <span class="mr-[5px] text-[14px]">正在装修:{{ diyStore.name || '页面名字' }}</span>
       </div>
       <div class="flex-1"></div>
       <el-button @click="preview()">保存并预览</el-button>
-      <el-button @click="save()">保存</el-button>
+      <el-button @click="save()" :loading="loading">保存</el-button>
     </el-header>
     <div class="full-container flex flex-row flex-1 bg-page">
       <div class="component-list w-[192px]">
@@ -39,11 +39,11 @@
       <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">
+          <div class="preview-pages shadow-lg" :style="{ backgroundColor: diyStore.backgroundColor }">
+            <draggable v-model="diyStore.componentList" item-key="itemKey" class="drag-area" :move="onDragMove">
               <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>
+                  <!-- <div class="component-box" :style="{ borderWidth: diyStore.currentIndex == index ? '2px' : '0px' }"></div> -->
                   <component :is="element.components" :key="element.itemKey" :index="index"></component>
                 </div>
               </template>
@@ -96,7 +96,7 @@
 
             <div class="edit-component-wrap">
               <component
-                v-if="diyStore.currentKey"
+                v-if="diyStore.currentKey && diyStore.currentIndex != -99"
                 :is="diyStore.editComponent.edit"
                 :key="diyStore.currentIndex"
                 :value="diyStore.componentList[diyStore.currentIndex]"
@@ -107,9 +107,9 @@
                     <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" />
+                          <el-color-picker v-model="diyStore.editComponent.pageStartBgColor" :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-color-picker v-model="diyStore.editComponent.pageEndBgColor" :predefine="diyStore.predefineColors" />
                         </el-form-item>
                         <div class="text-sm text-gray-400 ml-[90px] mb-[10px]">底部背景包含边距和圆角</div>
                       </template>
@@ -120,9 +120,9 @@
                         </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" />
+                        <el-color-picker v-model="diyStore.editComponent.componentStartBgColor" :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-color-picker v-model="diyStore.editComponent.componentEndBgColor" :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">
@@ -131,13 +131,7 @@
                         </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-slider v-model="diyStore.editComponent.padding.top" show-input size="small" :min="0" class="ml-[10px] diy-nav-slider" />
                       </el-form-item>
                       <el-form-item label="下边距" v-if="diyStore.editComponent.ignore.indexOf('marginBottom') == -1">
                         <el-slider
@@ -145,7 +139,7 @@
                           show-input
                           size="small"
                           class="ml-[10px] diy-nav-slider"
-                          :min="-100"
+                          :min="0"
                         />
                       </el-form-item>
                       <el-form-item label="左右边距" v-if="diyStore.editComponent.ignore.indexOf('marginBoth') == -1">
@@ -163,10 +157,17 @@
                           :max="100"
                         />
                       </el-form-item>
+                      <el-form-item label="上间距">
+                        <el-slider v-model="diyStore.editComponent.margin.top" show-input size="small" :min="0" class="ml-[10px] diy-nav-slider" />
+                      </el-form-item>
+                      <el-form-item label="下间距">
+                        <el-slider v-model="diyStore.editComponent.margin.bottom" show-input size="small" class="ml-[10px] diy-nav-slider" :min="0" />
+                      </el-form-item>
                     </el-form>
                   </div>
                 </template>
               </component>
+              <pagesEdit v-if="diyStore.currentIndex == -99"></pagesEdit>
             </div>
           </el-card>
         </el-scrollbar>
@@ -179,31 +180,96 @@
 </template>
 
 <script setup name="Index" lang="ts">
-import { pcAddDiy } from '@/api/diy/index';
+import { pcAddDiy, pcEditDiy, pcLookDiy } from '@/api/diy/index';
 import icon from '@/components/icon/index.vue';
 import draggable from 'vuedraggable';
 import { loadDiyComponents, getComponentGroups } from '@/views/diy/pccomponents/index';
-
 import usePcdiyStore from '@/store/modules/pcdiy';
+
+import pagesEdit from '@/views/diy/pccomponents/edit/pages-edit.vue';
+// const pagesEditRef = shallowRef(pagesEdit);
+
 const diyStore = usePcdiyStore();
 const route = useRoute();
 const query = route.query;
+const loading = ref<any>(false);
+const id = ref<any>(null);
+const activeNames = ref<any>([0]);
+const collapse = ref<any>([]);
 
 onMounted(() => {
   loadComponents();
+  if (query.id) {
+    id.value = query.id;
+    diyStore.backgroundColor = '#f2f2f2';
+    diyStore.hoverColor = '#E7000B';
+  } else {
+    id.value = null;
+    diyStore.type = query.type;
+    diyStore.name = query.title;
+    diyStore.backgroundColor = '#f2f2f2';
+    diyStore.hoverColor = '#E7000B';
+  }
 });
 
 // 加载组件
 const loadComponents = async () => {
   const components = await loadDiyComponents();
   collapse.value = getComponentGroups(components);
+  if (id.value) {
+    pcLookDiy(id.value).then((res) => {
+      if (res.code == 200) {
+        diyStore.name = res.data.name;
+        diyStore.type = res.data.type;
+        diyStore.currentIndex = -99;
+
+        const datas = JSON.parse(res.data.property);
+        collapse.value.forEach((item1: any) => {
+          datas.forEach((item2: any) => {
+            if (item1.id == item2.fid) {
+              item1.list.forEach((item3: any) => {
+                if (item2.id == item3.id) {
+                  item2.components = item3.components;
+                  item2.edit = item3.edit;
+                }
+              });
+            }
+          });
+        });
+        diyStore.componentList = datas;
+      }
+    });
+  }
+};
+
+const onDragMove = (evt: any) => {
+  const { willInsertAfter } = evt;
+
+  // 获取被拖动元素的数据 (注意:不同版本 vuedraggable 获取数据的方式可能略有不同,通常是 dragged.context.element)
+  const draggedElement = evt.draggedContext.element;
+  // console.log(draggedElement, '456465');
+  // 获取目标位置元素的数据
+  const relatedElement = evt.relatedContext.element;
+
+  // 规则 1: 如果正在拖动的是 ID 为 1 的头部组件 -> 禁止拖动
+  if (draggedElement.id === 1) {
+    return false;
+  }
+
+  // 规则 2: 如果试图将组件移动到 ID 为 1 的头部组件 之前 (即插队到第 0 位)
+  // willInsertAfter: true 表示要插在 related 后面,false 表示插在前面
+  if (relatedElement.id === 1 && !willInsertAfter) {
+    return false; // 阻止插队到头部前面
+  }
+
+  // 允许其他所有移动
+  return true;
 };
 
 const itemKey = ref<any>(0);
 //左边得组件
 const uniqueIdCounter = ref<any>(0);
-const activeNames = ref<any>([0]);
-const collapse = ref<any>([]);
+
 const handleChange = (val: string[]) => {};
 // 返回上一页
 const goBack = () => {};
@@ -213,19 +279,24 @@ const preview = () => {};
 
 // 保存
 const save = () => {
-  const datas = {
-    name: query.title,
+  const datas: any = {
+    name: diyStore.name,
     siteId: '',
     clientId: '',
-    type: query.type,
+    type: diyStore.type,
     remark: '',
     previewPicUrls: '',
     property: JSON.stringify(diyStore.componentList),
     isHome: 1
   };
-  const api = pcAddDiy;
+  loading.value = true;
+  const api = id.value ? pcEditDiy : pcAddDiy;
+  if (id.value) {
+    datas.id = id.value;
+  }
   api(datas)
     .then((res: any) => {
+      loading.value = false;
       if (res.code == 200) {
         ElMessage({
           message: '保存成功~',
@@ -282,9 +353,11 @@ const save = () => {
 
         /* 为了兼容某些情况,可能还需要配合 display */
         display: inline-block;
+        padding-bottom: 30px;
 
         .component-bos {
           position: relative;
+
           .component-box {
             position: absolute;
             width: 100%;

+ 8 - 1
src/views/diy/pcList.vue

@@ -168,7 +168,14 @@ const handleAdd = () => {
   // window.open(url.href);
 };
 /** 修改按钮操作 */
-const handleUpdate = async (row?: any) => {};
+const handleUpdate = async (row?: any) => {
+  const query = { id: row.id };
+  const url = router.resolve({
+    path: '/diy/pcedit',
+    query
+  });
+  window.open(url.href);
+};
 /** 删除按钮操作 */
 const handleDelete = async (row?: any) => {
   await proxy?.$modal.confirm('是否确认删除"' + row.name + '"的diy数据项?').finally(() => (loading.value = false));

+ 2 - 2
src/views/diy/pccomponents/edit/advert-edit.vue

@@ -37,7 +37,7 @@
           <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">
+                <el-icon @click="onDel(index)" color="#F56C6C" size="18px" class="circleClose" v-if="diyStore.editComponent.navlList.length > 3">
                   <CircleCloseFilled />
                 </el-icon>
                 <el-form-item label="图片上传">
@@ -66,7 +66,7 @@
                   <el-input v-model="element.subtitle" placeholder="请输入标签名称" />
                 </el-form-item>
                 <el-form-item label="链接地址">
-                  <el-input v-model="element.url" placeholder="请输入链接地址" />
+                  <WebLinkInput v-model="element.url" placeholder="请输入或选择链接" />
                 </el-form-item>
               </div>
             </template>

+ 5 - 5
src/views/diy/pccomponents/edit/brand-edit.vue

@@ -38,7 +38,7 @@
                 </div>
               </el-form-item>
               <el-form-item label="链接地址">
-                <el-input v-model="diyStore.editComponent.url" placeholder="请输入链接地址" />
+                <WebLinkInput v-model="diyStore.editComponent.url" placeholder="请输入或选择链接" />
               </el-form-item>
             </div>
           </el-form>
@@ -56,14 +56,14 @@
             <template #item="{ element, index }">
               <el-form label-width="90px" class="px-[10px]">
                 <div class="edit-attr-box">
-                  <el-icon @click="onDel(index)" color="#F56C6C" size="18px" class="circleClose">
+                  <el-icon @click="onDel(index)" color="#F56C6C" size="18px" class="circleClose" v-if="diyStore.editComponent.brandList.length > 1">
                     <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="annotation3">(建议上传尺寸相同图片,推荐尺寸120*50)</div>
                         <div class="flex-row-between images-box">
                           <div>缩放模式</div>
                           <div class="flex-row-start" @click="openImageType(element, 2)">
@@ -85,13 +85,13 @@
                     <el-input v-model="element.subtitle" placeholder="请输入副标题内容" />
                   </el-form-item>
                   <el-form-item label="链接地址">
-                    <el-input v-model="element.url" placeholder="请输入链接地址" />
+                    <WebLinkInput v-model="element.url" placeholder="请输入或选择链接" />
                   </el-form-item>
                 </div>
               </el-form>
             </template>
           </draggable>
-          <el-button @click="onAdd" style="width: 100%; margin-top: 10px">新增品牌</el-button>
+          <el-button v-if="diyStore.editComponent.brandList.length < 10" @click="onAdd" style="width: 100%; margin-top: 10px">新增品牌</el-button>
         </div>
       </template>
     </div>

+ 12 - 2
src/views/diy/pccomponents/edit/carousel-edit.vue

@@ -24,7 +24,7 @@
         <draggable v-model="diyStore.editComponent.imagelList" item-key="id">
           <template #item="{ element, index }">
             <div class="edit-attr-box">
-              <el-icon @click="onDel(index)" color="#F56C6C" size="18px" class="circleClose">
+              <el-icon @click="onDel(index)" color="#F56C6C" size="18px" class="circleClose" v-if="diyStore.editComponent.imagelList.length > 1">
                 <CircleCloseFilled />
               </el-icon>
               <el-form-item label="图片上传">
@@ -32,7 +32,9 @@
                   <upload-image v-model="element.imageUrl" :limit="1" />
                   <div class="flex-column-between images-bos">
                     <div class="annotation3">
-                      (建议上传尺寸相同图片,推荐尺寸{{ Math.floor(1200 / diyStore.editComponent.number) }}*{{ diyStore.editComponent.imageHeight }})
+                      (建议上传尺寸相同图片,推荐尺寸{{ Math.floor(1200 / diyStore.editComponent.imagelList.length) }}*{{
+                        diyStore.editComponent.imageHeight
+                      }})
                     </div>
                     <div class="flex-row-between images-box">
                       <div>缩放模式</div>
@@ -48,6 +50,9 @@
                   </div>
                 </div>
               </el-form-item>
+              <el-form-item label="链接地址">
+                <WebLinkInput v-model="element.url" placeholder="请输入或选择链接" />
+              </el-form-item>
             </div>
           </template>
         </draggable>
@@ -75,6 +80,11 @@
               <el-radio :value="3">居右</el-radio>
             </el-radio-group>
           </el-form-item>
+          <el-form-item label="颜色">
+            <span class="mr-[10px]">{{ diyStore.editComponent.carouselStyleColor }}</span>
+            <el-color-picker class="mr-[10px]" v-model="diyStore.editComponent.carouselStyleColor" />
+            <el-button @click="diyStore.editComponent.carouselStyleColor = '#ffffff'" size="small">重置</el-button>
+          </el-form-item>
         </el-form>
       </div>
       <div class="edit-attr-item-wrap">

+ 39 - 9
src/views/diy/pccomponents/edit/head-edit.vue

@@ -34,7 +34,7 @@
             <el-form-item label="logo">
               <div>
                 <upload-image v-model="diyStore.editComponent.logo" :limit="1" />
-                <div class="annotation">建议尺寸:宽度{{ diyStore.editComponent.topStyle == 1 ? '185' : '390' }}px*90px</div>
+                <div class="annotation">建议尺寸:宽度{{ diyStore.editComponent.topStyle == 1 ? '170' : '370' }}px*88px</div>
               </div>
             </el-form-item>
             <el-form-item label="二维码">
@@ -72,7 +72,7 @@
                       <el-input v-model="element.title" placeholder="请输入导航名称" />
                     </el-form-item>
                     <el-form-item label="链接地址">
-                      <el-input v-model="element.url" placeholder="请输入链接地址" />
+                      <WebLinkInput v-model="element.url" placeholder="请输入或选择链接" />
                     </el-form-item>
                   </div>
                 </template>
@@ -90,7 +90,7 @@
                       <el-input v-model="element.title" placeholder="请输入标签名称" />
                     </el-form-item>
                     <el-form-item label="链接地址">
-                      <el-input v-model="element.url" placeholder="请输入链接地址" />
+                      <WebLinkInput v-model="element.url" placeholder="请输入或选择链接" />
                     </el-form-item>
                   </div>
                 </template>
@@ -144,7 +144,13 @@
             <draggable v-model="diyStore.editComponent.carouselList" item-key="id">
               <template #item="{ element, index }">
                 <div class="edit-attr-box">
-                  <el-icon @click="onDel(3, index)" color="#F56C6C" size="18px" class="circleClose">
+                  <el-icon
+                    @click="onDel(3, index)"
+                    color="#F56C6C"
+                    size="18px"
+                    class="circleClose"
+                    v-if="diyStore.editComponent.carouselList.length > 1"
+                  >
                     <CircleCloseFilled />
                   </el-icon>
                   <el-form-item label="图片上传">
@@ -167,7 +173,7 @@
                     </div>
                   </el-form-item>
                   <el-form-item label="链接地址">
-                    <el-input v-model="element.url" placeholder="请输入链接地址" />
+                    <WebLinkInput v-model="element.url" placeholder="请输入或选择链接" />
                   </el-form-item>
                   <el-form-item label="是否显示">
                     <el-switch v-model="element.show" />
@@ -212,7 +218,7 @@
                     </div>
                   </el-form-item>
                   <el-form-item label="链接地址">
-                    <el-input v-model="element.url" placeholder="请输入链接地址" />
+                    <WebLinkInput v-model="element.url" placeholder="请输入或选择链接" />
                   </el-form-item>
                 </div>
               </template>
@@ -224,12 +230,18 @@
         <div class="edit-attr-item-wrap">
           <h3 class="mb-[10px]">右侧设置</h3>
           <el-form label-width="80px" class="px-[10px]">
-            <el-form-item label="资讯位置">
+            <el-form-item label="登录状态">
+              <el-radio-group v-model="diyStore.editComponent.login">
+                <el-radio :value="1">未登录</el-radio>
+                <el-radio :value="2">已登录</el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <!-- <el-form-item label="资讯位置">
               <el-radio-group v-model="diyStore.editComponent.realType">
                 <el-radio :value="1">在上</el-radio>
                 <el-radio :value="2">在下</el-radio>
               </el-radio-group>
-            </el-form-item>
+            </el-form-item> -->
             <el-form-item label="数据来源">
               <el-radio-group v-model="diyStore.editComponent.realDataType">
                 <el-radio :value="1">默认</el-radio>
@@ -285,7 +297,7 @@
                     <el-input v-model="element.title" placeholder="请输入标签名称" :maxlength="4" show-word-limit />
                   </el-form-item>
                   <el-form-item label="链接地址">
-                    <el-input v-model="element.url" placeholder="请输入链接地址" />
+                    <WebLinkInput v-model="element.url" placeholder="请输入或选择链接" />
                   </el-form-item>
                 </div>
               </template>
@@ -306,6 +318,24 @@
           <el-form-item label="图片圆角">
             <el-slider v-model="diyStore.editComponent.carouselRadius" show-input :min="0" :max="100" />
           </el-form-item>
+          <el-form-item label="样式">
+            <el-radio-group v-model="diyStore.editComponent.carouselStyleType">
+              <el-radio :value="1">直线</el-radio>
+              <el-radio :value="2">圆点</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="位置">
+            <el-radio-group v-model="diyStore.editComponent.carouselPosition">
+              <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="颜色">
+            <span class="mr-[10px]">{{ diyStore.editComponent.carouselStyleColor }}</span>
+            <el-color-picker class="mr-[10px]" v-model="diyStore.editComponent.carouselStyleColor" />
+            <el-button @click="diyStore.editComponent.carouselStyleColor = '#ffffff'" size="small">重置</el-button>
+          </el-form-item>
         </el-form>
       </div>
       <div class="edit-attr-item-wrap">

+ 67 - 0
src/views/diy/pccomponents/edit/hot-edit copy.vue

@@ -0,0 +1,67 @@
+<template>
+  <div>
+    <!-- 内容 -->
+    <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]">
+          <div ref="imageBoxRef">
+            <div class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]">
+              <el-form-item label="热区背景">
+                <upload-image v-model="diyStore.editComponent.imageUrl" :limit="1" @change="selectImg" />
+              </el-form-item>
+
+              <el-form-item label="热区设置">
+                <!-- <heat-map v-model="diyStore.editComponent" /> -->
+              </el-form-item>
+            </div>
+          </div>
+        </el-form>
+      </div>
+    </div>
+
+    <!-- 样式 -->
+    <div class="style-wrap" v-show="diyStore.editTab == 'style'">
+      <!-- 组件样式 -->
+      <slot name="style"></slot>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import useDiyStore from '@/store/modules/diy';
+import { img } from '@/utils/common';
+
+const diyStore = useDiyStore();
+console.log(diyStore.editComponent, 88888888888888888888);
+diyStore.editComponent.ignore = ['componentBgUrl']; // 忽略公共属性
+
+// 组件验证
+diyStore.editComponent.verify = (index: number) => {
+  const res = { code: true, message: '' };
+  if (diyStore.value[index].imageUrl === '') {
+    res.code = false;
+    res.message = '请上传图片';
+    return res;
+  }
+  return res;
+};
+
+const selectImg = (url: string) => {
+  handleHeight();
+};
+
+// 处理高度
+const handleHeight = () => {
+  const image = new Image();
+  image.src = img(diyStore.editComponent.imageUrl);
+  image.onload = async () => {
+    diyStore.editComponent.imgWidth = image.width;
+    diyStore.editComponent.imgHeight = image.height;
+  };
+};
+
+defineExpose({});
+</script>
+
+<style lang="scss" scoped></style>

+ 92 - 0
src/views/diy/pccomponents/edit/hot-edit.vue

@@ -0,0 +1,92 @@
+<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="90px" class="px-[10px]">
+          <el-form-item label="热区背景">
+            <upload-image v-model="diyStore.editComponent.imageUrl" :limit="1" @change="selectImg" />
+          </el-form-item>
+          <el-form-item label="热区设置">
+            <heat-map v-model="diyStore.editComponent" />
+          </el-form-item>
+        </el-form>
+      </div>
+    </div>
+    <!-- 样式 -->
+    <div class="style-wrap" v-show="diyStore.editTab == 'style'">
+      <!-- 组件样式 -->
+      <slot name="style"></slot>
+    </div>
+  </div>
+</template>
+
+<script setup name="Index" lang="ts">
+import usePcdiyStore from '@/store/modules/pcdiy';
+
+const diyStore = usePcdiyStore();
+
+console.log(diyStore.editComponent, '5555');
+
+// 组件验证
+diyStore.editComponent.verify = (index: number) => {
+  const res = { code: true, message: '' };
+  if (diyStore.value[index].imageUrl === '') {
+    res.code = false;
+    res.message = '请上传图片';
+    return res;
+  }
+  return res;
+};
+
+const selectImg = (url: string) => {
+  handleHeight();
+};
+
+// 处理高度
+const handleHeight = () => {
+  const image = new Image();
+  image.src = diyStore.editComponent.imageUrl;
+  image.onload = async () => {
+    diyStore.editComponent.imgWidth = image.width;
+    diyStore.editComponent.imgHeight = image.height;
+  };
+};
+
+defineExpose({});
+</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;
+    }
+  }
+
+  .data-bos {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 0 12px;
+    .data-list {
+      background-color: #f9fafb;
+      border: 1px solid #e5e7eb;
+      &.border-primary {
+        border-color: var(--el-color-primary);
+      }
+      img {
+        width: 100%;
+      }
+    }
+  }
+
+  :deep(.el-form-item__label) {
+    font-weight: 400;
+  }
+}
+</style>

+ 1 - 1
src/views/diy/pccomponents/edit/imageCube-edit.vue

@@ -55,7 +55,7 @@
                 </div>
               </el-form-item>
               <el-form-item label="链接地址">
-                <el-input v-model="element.url" placeholder="请输入链接地址" />
+                <WebLinkInput v-model="element.url" placeholder="请输入或选择链接" />
               </el-form-item>
             </div>
           </template>

+ 2 - 2
src/views/diy/pccomponents/edit/navigation-edit.vue

@@ -37,7 +37,7 @@
           <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">
+                <el-icon @click="onDel(index)" color="#F56C6C" size="18px" class="circleClose" v-if="diyStore.editComponent.navlList.length > 3">
                   <CircleCloseFilled />
                 </el-icon>
                 <el-form-item label="图片上传">
@@ -66,7 +66,7 @@
                   <el-input v-model="element.subtitle" placeholder="请输入标签名称" />
                 </el-form-item>
                 <el-form-item label="链接地址">
-                  <el-input v-model="element.url" placeholder="请输入链接地址" />
+                  <WebLinkInput v-model="element.url" placeholder="请输入或选择链接" />
                 </el-form-item>
               </div>
             </template>

+ 86 - 0
src/views/diy/pccomponents/edit/pages-edit.vue

@@ -0,0 +1,86 @@
+<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="90px" class="px-[10px]">
+          <el-form-item label="模板名称">
+            <el-input v-model="diyStore.name" placeholder="请输入模板名称" />
+          </el-form-item>
+        </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="背景颜色">
+            <span class="mr-[10px]">{{ diyStore.backgroundColor }}</span>
+            <el-color-picker class="mr-[10px]" v-model="diyStore.backgroundColor" />
+            <el-button @click="diyStore.backgroundColor = '#f2f2f2'" size="small">重置</el-button>
+          </el-form-item>
+          <el-form-item label="悬停颜色">
+            <span class="mr-[10px]">{{ diyStore.hoverColor }}</span>
+            <el-color-picker class="mr-[10px]" v-model="diyStore.hoverColor" />
+            <el-button @click="diyStore.hoverColor = '#E7000B'" size="small">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+      <!-- 组件样式 -->
+      <slot name="style"></slot>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import usePcdiyStore from '@/store/modules/pcdiy';
+const diyStore = usePcdiyStore();
+
+
+</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;
+      }
+    }
+
+    .data-num {
+      width: 100%;
+      font-size: 14px;
+      display: flex;
+      align-items: center;
+      justify-content: flex-end;
+      color: var(--el-color-primary);
+      cursor: pointer;
+    }
+  }
+
+  .selected {
+    line-height: 32px;
+    position: absolute;
+    left: 0px;
+  }
+
+  :deep(.el-form-item__label) {
+    font-weight: 400;
+  }
+}
+</style>

+ 13 - 3
src/views/diy/pccomponents/index.ts

@@ -8,7 +8,8 @@ import { markRaw } from 'vue';
 
 const createComponentMap = (name: string) => ({
   pagePath: `./pages/${name}.vue`,
-  editPath: `./edit/${name}-edit.vue`
+  editPath: `./edit/${name}-edit.vue`,
+  fid: 1
 });
 
 // 组件列表配置
@@ -98,10 +99,17 @@ export const componentImportConfigs: any = [
   },
   {
     name: '发现组件',
-    icon: 'iconfont iconrequpc',
+    icon: 'iconfont iconjifenshangpinpc',
     enabled: true,
     id: 13,
     ...createComponentMap('discover')
+  },
+  {
+    name: '热区',
+    icon: 'iconfont iconrequpc',
+    enabled: true,
+    id: 14,
+    ...createComponentMap('hot')
   }
 ];
 
@@ -120,6 +128,7 @@ export async function loadDiyComponents(): Promise<any[]> {
         return {
           name: config.name,
           icon: config.icon,
+          fid: config.fid,
           id: config.id,
           components: markRaw(pageModule.default),
           edit: markRaw(editModule.default),
@@ -144,7 +153,8 @@ export function getComponentGroups(components: any[]): any[] {
   return [
     {
       name: '基础组件',
-      list: components
+      list: components,
+      id: 1
     }
     // 后续可扩展多个分组,如:营销组件、商品组件等
   ];

+ 19 - 9
src/views/diy/pccomponents/pages/advert.vue

@@ -1,32 +1,32 @@
 <template>
-  <div class="pcPages">
-    <div class="carousel-bos" :style="warpCss" v-if="componentData.styleType == 3">
+  <div class="pcPages" :style="warpCss">
+    <div class="carousel-bos" 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">
+            <div v-for="(item, index) in item1" :key="index" class="data-list hover-color 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="titleCss" class="title zi-hover">{{ 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">
+    <div v-else class="data-bos">
+      <div v-for="(item, index) in componentData.navlList" :key="index" class="data-list hover-color 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'"
+          :fit="item.imageUrl ? (item.imgType == 1 ? 'fill' : item.imgType == 2 ? 'contain' : 'cover') : 'cover'"
           :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
         />
-        <div :style="titleCss" class="title">{{ item.title || '' }}</div>
+        <div :style="titleCss" class="title zi-hover">{{ item.title || '' }}</div>
         <div :style="subtitleCss" class="mt-[2px] mb-[12px] subtitle ellipsis">{{ item.subtitle || '' }}</div>
       </div>
     </div>
@@ -39,8 +39,9 @@ import figure from '@/assets/images/figure.png';
 const diyStore = usePcdiyStore();
 const props = defineProps<{
   index: number; // 确保声明 index 为可选属性
+  row?: any;
 }>();
-const componentData = diyStore.componentList[props.index];
+const componentData = props.row ? props.row : diyStore.componentList[props.index];
 
 const dataList = computed(() => {
   const chunkSize = componentData.number * componentData.count;
@@ -74,6 +75,15 @@ const warpCss = computed(() => {
     style += 'padding-left:' + componentData.padding.both + 'px' + ';';
     if (componentData.styleType == 1) style += 'flex-wrap:wrap' + ';';
   }
+  //间距
+  if (componentData.margin) {
+    if (componentData.margin.top > 0) {
+      style += 'margin-top:' + componentData.margin.top + 'px' + ';';
+    }
+    if (componentData.margin.bottom > 0) {
+      style += 'margin-bottom:' + componentData.margin.bottom + 'px' + ';';
+    }
+  }
   return style;
 });
 

+ 26 - 12
src/views/diy/pccomponents/pages/article.vue

@@ -2,9 +2,13 @@
   <div class="pcPages" :style="warpCss">
     <div class="article-bos" :style="boxCss">
       <template v-for="(item, index) in dataList" :key="index">
-        <div class="article-list" :style="dataCss" v-if="componentData.dataType == 2 ? true : index < componentData.dataNumber">
+        <div
+          class="article-list hover-color"
+          :style="dataCss"
+          v-if="componentData.dataType == 2 && componentData.dataIds.length > 0 ? true : index < componentData.dataNumber"
+        >
           <img :src="item.caseImage ? item.caseImage : figure" alt="" />
-          <div class="caseTitle">{{ item.caseTitle }}</div>
+          <div class="caseTitle zi-hover">{{ item.caseTitle }}</div>
           <div class="projectBrief">{{ item.projectBrief }}</div>
         </div>
       </template>
@@ -20,31 +24,31 @@ const diyStore = usePcdiyStore();
 
 const props = defineProps<{
   index: number; // 确保声明 index 为可选属性
+  row?: any;
 }>();
-const componentData = diyStore.componentList[props.index];
+const componentData = props.row ? props.row : diyStore.componentList[props.index];
 const dataList = ref<any>([]);
 
 onMounted(() => {
   getDataList();
 });
 const getDataList = () => {
-  dataList.value = [];
   // 默认
   if (componentData.dataType == 1) {
-    listServiceCase({ pageSize: 16 }).then((res) => {
+    listServiceCase({ pageSize: 20 }).then((res) => {
       if (res.code == 200) {
         dataList.value = res.rows;
       }
     });
   } else {
     //手动选择
-    listServiceCase({ pageSize: 16 }).then((res) => {
-      if (res.code == 200) {
-        const result = res.rows.filter((item: any) => componentData.dataIds.includes(item.id));
-        dataList.value = result;
-        console.log('result', result);
-      }
-    });
+    if (componentData.dataIds.length > 0) {
+      listServiceCase({ pageNum: 1, pageSize: 20, ids: componentData.dataIds.join(',') }).then((res) => {
+        if (res.code == 200) {
+          dataList.value = res.rows;
+        }
+      });
+    }
   }
 };
 
@@ -95,6 +99,15 @@ const warpCss = computed(() => {
   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;';
+  //间距
+  if (componentData.margin) {
+    if (componentData.margin.top > 0) {
+      style += 'margin-top:' + componentData.margin.top + 'px' + ';';
+    }
+    if (componentData.margin.bottom > 0) {
+      style += 'margin-bottom:' + componentData.margin.bottom + 'px' + ';';
+    }
+  }
 
   return style;
 });
@@ -142,6 +155,7 @@ const dataCss = computed(() => {
       flex: 0 0 calc((100% - 45px) / 4);
       width: 0;
       overflow: hidden;
+      cursor: pointer;
 
       .caseTitle {
         font-size: 14px;

+ 25 - 11
src/views/diy/pccomponents/pages/brand.vue

@@ -4,23 +4,23 @@
       <el-image
         class="bigBrand-one"
         :src="componentData.imageUrl ? componentData.imageUrl : figure"
-        :fit="componentData.imgType == 1 ? 'fill' : componentData.imgType == 2 ? 'contain' : 'cover'"
+        :fit="componentData.imageUrl ? (componentData.imgType == 1 ? 'fill' : componentData.imgType == 2 ? 'contain' : 'cover') : 'cover'"
         :style="componentData.boxRadius ? { borderRadius: componentData.boxRadius + 'px' } : {}"
       />
       <div class="bigBrand-bos">
         <template v-for="(item, index) in componentData.brandList" :key="index">
           <div
-            class="bigBrand-list"
+            class="bigBrand-list hover-color"
             v-if="Number(index) < 10"
             :style="componentData.boxRadius ? { borderRadius: componentData.boxRadius + 'px' } : {}"
           >
             <el-image
               class="img"
-              :src="item.imageUrl ? item.imageUrl : figure"
+              :src="item.imageUrl ? item.imageUrl : brand1"
               :fit="item.imgType == 1 ? 'fill' : item.imgType == 2 ? 'contain' : 'cover'"
               :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
             />
-            <div :style="titleCss" class="bigBrand1">{{ item.title || '' }}</div>
+            <div :style="titleCss" class="bigBrand1 zi-hover">{{ item.title || '' }}</div>
             <div :style="subtitleCss" class="bigBrand2">
               {{ item.subtitle || '' }}
             </div>
@@ -33,13 +33,15 @@
 
 <script lang="ts" setup>
 import figure from '@/assets/images/figure.png';
+import brand1 from '@/assets/images/pcdiy/brand1.png';
 import usePcdiyStore from '@/store/modules/pcdiy';
 const diyStore = usePcdiyStore();
 
 const props = defineProps<{
   index: number; // 确保声明 index 为可选属性
+  row?: any;
 }>();
-const componentData = diyStore.componentList[props.index];
+const componentData = props.row ? props.row : diyStore.componentList[props.index];
 
 const warpCss = computed(() => {
   let style = '';
@@ -73,6 +75,15 @@ const warpCss = computed(() => {
   if (componentData.bottomRounded) style += 'border-bottom-left-radius:' + componentData.bottomRounded + 'px;';
   if (componentData.bottomRounded) style += 'border-bottom-right-radius:' + componentData.bottomRounded + 'px;';
 
+  //间距
+  if (componentData.margin) {
+    if (componentData.margin.top > 0) {
+      style += 'margin-top:' + componentData.margin.top + 'px' + ';';
+    }
+    if (componentData.margin.bottom > 0) {
+      style += 'margin-bottom:' + componentData.margin.bottom + 'px' + ';';
+    }
+  }
   return style;
 });
 
@@ -111,7 +122,7 @@ const subtitleCss = computed(() => {
   margin: 0 auto;
   // 大牌推荐
   .big-brand {
-    height: 334px;
+    height: 340px;
     display: flex;
     gap: 10px;
     width: 100%;
@@ -127,27 +138,28 @@ const subtitleCss = computed(() => {
       flex-wrap: wrap;
       gap: 10px;
       overflow: hidden;
+      height: 340px;
 
       .bigBrand-list {
         flex: 0 0 calc((100% - 40px) / 5);
-        height: 162px;
+        height: 165px;
         background: #ffffff;
         display: flex;
         flex-direction: column;
         align-items: center;
-        padding: 32px 20px 0 20px;
+        padding: 20px 20px 0 20px;
         width: 0;
 
         .img {
-          width: 150px;
-          height: 30px;
+          width: 120px;
+          height: 50px;
         }
 
         .bigBrand1 {
           font-weight: 600;
           font-size: 14px;
           color: #101828;
-          margin: 10px 0 4px 0;
+          margin: 5px 0 4px 0;
         }
 
         .bigBrand2 {
@@ -160,6 +172,8 @@ const subtitleCss = computed(() => {
           -webkit-box-orient: vertical;
           overflow: hidden;
           text-overflow: ellipsis;
+          text-align: left;
+          width: 100%;
         }
       }
     }

+ 25 - 3
src/views/diy/pccomponents/pages/carousel.vue

@@ -1,11 +1,11 @@
 <template>
   <div class="pcPages" :style="warpCss" :class="['position' + componentData.position, 'styleType' + componentData.styleType]">
-    <el-carousel :interval="componentData.interval" :height="componentData.imageHeight + 'px'" class="carousel-bos" :style="boxCss">
+    <el-carousel arrow="always" :interval="componentData.interval" :height="componentData.imageHeight + 'px'" class="carousel-bos" :style="boxCss">
       <el-carousel-item v-for="(item, index) in componentData.imagelList" :key="index" class="carousel-item">
         <el-image
           class="img"
           :src="item.imageUrl ? item.imageUrl : figure"
-          :fit="item.imgType == 1 ? 'fill' : item.imgType == 2 ? 'contain' : 'cover'"
+          :fit="item.imageUrl ? (item.imgType == 1 ? 'fill' : item.imgType == 2 ? 'contain' : 'cover') : 'cover'"
         />
       </el-carousel-item>
     </el-carousel>
@@ -18,8 +18,10 @@ const diyStore = usePcdiyStore();
 
 const props = defineProps<{
   index: number; // 确保声明 index 为可选属性
+  row?: any;
 }>();
-const componentData = diyStore.componentList[props.index];
+const componentData = props.row ? props.row : diyStore.componentList[props.index];
+document.documentElement.style.setProperty('--carousel-color2', componentData.carouselStyleColor ? componentData.carouselStyleColor : '#ffffff');
 import figure from '@/assets/images/figure.png';
 
 const warpCss = computed(() => {
@@ -43,6 +45,15 @@ const warpCss = computed(() => {
     style += 'padding-right:' + componentData.padding.both + 'px' + ';';
     style += 'padding-left:' + componentData.padding.both + 'px' + ';';
   }
+  //间距
+  if (componentData.margin) {
+    if (componentData.margin.top > 0) {
+      style += 'margin-top:' + componentData.margin.top + 'px' + ';';
+    }
+    if (componentData.margin.bottom > 0) {
+      style += 'margin-bottom:' + componentData.margin.bottom + 'px' + ';';
+    }
+  }
 
   return style;
 });
@@ -54,6 +65,13 @@ const boxCss = computed(() => {
   if (componentData.imageRadius) style += 'border-radius:' + componentData.imageRadius + 'px;';
   return style;
 });
+
+watch(
+  () => componentData.carouselStyleColor,
+  () => {
+    document.documentElement.style.setProperty('--carousel-color2', componentData.carouselStyleColor ? componentData.carouselStyleColor : '#ffffff');
+  }
+);
 </script>
 
 <style lang="scss" scoped>
@@ -72,6 +90,7 @@ const boxCss = computed(() => {
       .img {
         width: 100%;
         height: 100%;
+        cursor: pointer;
       }
     }
   }
@@ -98,5 +117,8 @@ const boxCss = computed(() => {
       border-radius: 10px;
     }
   }
+  :deep(.el-carousel__button) {
+    background-color: var(--carousel-color2);
+  }
 }
 </style>

+ 187 - 91
src/views/diy/pccomponents/pages/head.vue

@@ -1,40 +1,43 @@
 <template>
-  <div class="head-bos" :style="warpCss">
+  <div class="head-all" :style="warpCss">
     <!-- 搜索组件 -->
     <div class="search-bos">
-      <img
-        class="logo"
-        :src="componentData.logo ? componentData.logo : figure"
-        alt=""
-        :style="{ 'width': componentData.topStyle == 1 ? '185px' : '390px' }"
-      />
-      <div class="search-box">
-        <div class="search-div flex-row-start">
-          <div class="search-input flex-row-center">
-            <el-input class="el-input" readonly placeholder="搜索商品、品牌、分类..." />
-            <div class="bnt flex-row-center">
-              <el-icon color="#ffffff" size="20">
-                <Search />
-              </el-icon>
+      <div class="search-content">
+        <img
+          class="logo"
+          :src="componentData.logo ? componentData.logo : logo1"
+          alt=""
+          :style="{ 'width': componentData.topStyle == 1 ? '170px' : '370px' }"
+        />
+        <div class="search-box">
+          <div class="search-div flex-row-start">
+            <div class="search-input flex-row-center">
+              <el-input class="el-input" readonly placeholder="搜索商品、品牌、分类..." />
+              <div class="bnt flex-row-center">
+                <el-icon color="#ffffff" size="20">
+                  <Search />
+                </el-icon>
+              </div>
             </div>
-          </div>
-          <el-badge :value="5">
+
             <div class="cat-bos flex-row-center">
-              <img src="@/assets/images/pcdiy/layout4.png" alt="" />
-              <span>我的购物车</span>
+              <el-badge :value="5">
+                <img src="@/assets/images/pcdiy/layout4.png" alt="" />
+              </el-badge>
+              <span class="ml-[15px]">我的购物车</span>
             </div>
-          </el-badge>
-        </div>
-        <div class="search-text">
-          <div v-for="(item, index) in componentData.toplabel" :key="index">{{ item.title }}</div>
+          </div>
+          <div class="search-text">
+            <div v-for="(item, index) in componentData.toplabel" :key="index">{{ item.title }}</div>
+          </div>
         </div>
+        <img class="code" :src="componentData.code ? componentData.code : code" alt="" />
       </div>
-      <img class="code" :src="componentData.code ? componentData.code : figure" alt="" />
     </div>
     <!-- 分类 -->
     <div class="nav-pages">
       <div class="nav-bos">
-        <div class="nav-all flex-row-center" v-if="componentData.classifyShow">
+        <div class="nav-all flex-row-start" v-if="componentData.classifyShow">
           <img src="@/assets/images/pcdiy/layout2.png" alt="" />
           <div>全部商品分类</div>
         </div>
@@ -58,9 +61,9 @@
           }"
         >
           <div class="classify-list" v-for="(item, index) in classifyList" :key="index">
-            <div :style="{ 'color': componentData.leftColor1 }" class="label ellipsis">{{ item.label }}</div>
-            <div :style="{ 'color': componentData.leftColor2 }" class="info info1 ellipsis">{{ item.oneLable1 }}</div>
-            <div :style="{ 'color': componentData.leftColor2 }" class="info ellipsis">{{ item.oneLable2 }}</div>
+            <div :style="{ 'color': componentData.leftColor1 }" class="label ellipsis hover-color">{{ item.label }}55</div>
+            <div :style="{ 'color': componentData.leftColor2 }" class="info info1 ellipsis hover-color">{{ item.oneLable1 }}</div>
+            <div :style="{ 'color': componentData.leftColor2 }" class="info ellipsis hover-color">{{ item.oneLable2 }}</div>
           </div>
         </div>
         <div class="classify2" v-else>
@@ -77,13 +80,17 @@
         <div class="head-bos">
           <div
             class="carousel"
-            :class="{ 'carousel-type': componentData.carouselType == 2 }"
+            :class="[
+              { 'carousel-type': componentData.carouselType == 2 },
+              'position' + componentData.carouselPosition,
+              'styleType' + componentData.carouselStyleType
+            ]"
             :style="{
               height: componentData.advertNum == 0 ? '540px' : '400px',
               borderRadius: componentData.carouselRadius + 'px'
             }"
           >
-            <el-carousel :autoplay="false" trigger="click" :height="componentData.advertNum == 0 ? '540px' : '400px'" arrow="always">
+            <el-carousel :height="componentData.advertNum == 0 ? '540px' : '400px'" arrow="always">
               <template v-if="componentData.carouselList && componentData.carouselList.length > 0">
                 <el-carousel-item v-for="item in componentData.carouselList" :key="item">
                   <el-image
@@ -95,7 +102,7 @@
               </template>
               <template v-else>
                 <el-carousel-item>
-                  <img :src="figure" alt="" />
+                  <el-image style="width: 100%; height: 100%" :src="figure" fit="cover" />
                 </el-carousel-item>
               </template>
             </el-carousel>
@@ -106,7 +113,7 @@
                 <el-image
                   style="width: 100%; height: 100%"
                   :src="item.imageUrl ? item.imageUrl : figure"
-                  :fit="item.imgType == 1 ? 'fill' : item.imgType == 2 ? 'contain' : 'cover'"
+                  :fit="item.imageUrl ? (item.imgType == 1 ? 'fill' : item.imgType == 2 ? 'contain' : 'cover') : 'cover'"
                 />
               </div>
             </template>
@@ -119,9 +126,29 @@
             borderRadius: componentData.rightRadius + 'px'
           }"
         >
-          <div class="login-bos">
+          <div v-if="componentData.login == 2" class="order-bos flex-column-between">
+            <div class="flex-row-start">
+              <img :src="logo2" alt="" />
+              <div class="ellipsis">中国南方电网有限责任公司</div>
+            </div>
+            <div class="flex-row-start">
+              <div class="flex-1 flex-column-center">
+                <div>待付款</div>
+                <div class="order-num">0</div>
+              </div>
+              <div class="flex-1 flex-column-center">
+                <div>待付款</div>
+                <div class="order-num">0</div>
+              </div>
+              <div class="flex-1 flex-column-center">
+                <div>待付款</div>
+                <div class="order-num">0</div>
+              </div>
+            </div>
+          </div>
+          <div class="login-bos" v-else>
             <div class="login-box">
-              <img :src="figure" alt="" />
+              <img :src="logo2" alt="" />
               <div>
                 <div class="login1">您好,欢迎来到优易达</div>
                 <div class="login2">请先登录</div>
@@ -136,14 +163,14 @@
             <div class="real-title flex-row-between">
               <div class="real1">优易资讯</div>
               <div class="real2 flex-row-start">
-                <div>更多</div>
+                <div class="hover-color">更多</div>
                 <el-icon :size="13" color="#83899F">
                   <ArrowRight />
                 </el-icon>
               </div>
             </div>
             <template v-for="(item, index) in realList" :key="index">
-              <div class="real-list ellipsis" v-if="componentData.realDataType == 2 ? true : Number(index) < componentData.realNumber">
+              <div class="real-list ellipsis hover-color" v-if="componentData.realDataType == 2 ? true : Number(index) < componentData.realNumber">
                 {{ item.announcementTitle }}
               </div>
             </template>
@@ -157,7 +184,7 @@
                   :src="item.imageUrl ? item.imageUrl : figure"
                   :fit="item.imgType == 1 ? 'fill' : item.imgType == 2 ? 'contain' : 'cover'"
                 />
-                <div style="height: 18px">{{ item.title }}</div>
+                <div style="height: 18px" class="hover-color">{{ item.title }}</div>
               </div>
             </div>
           </div>
@@ -169,13 +196,18 @@
 
 <script setup name="Index" lang="ts">
 import figure from '@/assets/images/figure.png';
+import logo1 from '@/assets/images/pcdiy/logo1.png';
+import logo2 from '@/assets/images/pcdiy/logo2.png';
+import code from '@/assets/images/pcdiy/code.png';
 import usePcdiyStore from '@/store/modules/pcdiy';
 import { listAnnouncement } from '@/api/system/announcement';
 const diyStore = usePcdiyStore();
 const props = defineProps<{
   index: number; // 确保声明 index 为可选属性
+  row?: any;
 }>();
-const componentData = diyStore.componentList[props.index];
+const componentData = props.row ? props.row : diyStore.componentList[props.index];
+document.documentElement.style.setProperty('--carousel-color', componentData.carouselStyleColor ? componentData.carouselStyleColor : '#ffffff');
 onMounted(() => {
   getDataList();
 });
@@ -217,6 +249,13 @@ watch(
   { deep: true } // 5. 数组变化需要 deep 监听
 );
 
+watch(
+  () => componentData.carouselStyleColor,
+  () => {
+    document.documentElement.style.setProperty('--carousel-color', componentData.carouselStyleColor ? componentData.carouselStyleColor : '#ffffff');
+  }
+);
+
 //左侧分类
 const classifyList = ref<any>([
   {
@@ -403,15 +442,24 @@ const warpCss = computed(() => {
   let style = '';
   style += 'position:relative;';
   //边距
-  if (diyStore.componentList[props.index].padding) {
-    if (diyStore.componentList[props.index].padding.top > 0) {
-      style += 'padding-top:' + diyStore.componentList[props.index].padding.top + 'px' + ';';
+  if (componentData.padding) {
+    if (componentData.padding.top > 0) {
+      style += 'padding-top:' + componentData.padding.top + 'px' + ';';
     }
-    if (diyStore.componentList[props.index].padding.bottom > 0) {
-      style += 'padding-bottom:' + diyStore.componentList[props.index].padding.bottom + '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.margin) {
+    if (componentData.margin.top > 0) {
+      style += 'margin-top:' + componentData.margin.top + 'px' + ';';
+    }
+    if (componentData.margin.bottom > 0) {
+      style += 'margin-bottom:' + componentData.margin.bottom + 'px' + ';';
     }
-    style += 'padding-right:' + diyStore.componentList[props.index].padding.both + 'px' + ';';
-    style += 'padding-left:' + diyStore.componentList[props.index].padding.both + 'px' + ';';
   }
 
   return style;
@@ -419,39 +467,45 @@ const warpCss = computed(() => {
 </script>
 
 <style lang="scss" scoped>
-.head-bos {
-  width: 1300px;
+.head-all {
+  width: 100%;
 
   // 搜索栏
   .search-bos {
-    margin: 0 auto;
-    width: 1300px;
-    display: flex;
+    width: 100%;
     background-color: #ffffff;
-    padding: 0 50px;
+
+    .search-content {
+      margin: 0 auto;
+      width: 1200px;
+      display: flex;
+      padding: 0 30px;
+    }
 
     .logo {
-      width: 185px;
-      height: 90px;
+      width: 170px;
+      height: 88px;
       border-radius: 4px;
-      margin-top: 15px;
+      margin-top: 10px;
       margin-right: 30px;
     }
 
     .search-box {
       flex: 1;
-      padding-top: 57px;
+      height: 88px;
+      padding-top: 40px;
 
       .search-div {
         .search-input {
           flex: 1;
-          height: 48px;
-          border-radius: 10px;
+          height: 38px;
+          border-radius: 3px;
           border: 2px solid #fb2c36;
           padding-right: 4px;
+          font-size: 14px;
 
           .el-input {
-            height: 40px;
+            height: 30px;
             width: 100%;
             font-size: 16px;
 
@@ -464,10 +518,10 @@ const warpCss = computed(() => {
           }
 
           .bnt {
-            width: 68px;
-            height: 40px;
+            width: 50px;
+            height: 30px;
             background: #e7000b;
-            border-radius: 8px;
+            border-radius: 3px;
             font-weight: bold;
             cursor: pointer;
           }
@@ -475,29 +529,28 @@ const warpCss = computed(() => {
 
         .cat-bos {
           width: 143px;
-          height: 48px;
+          height: 38px;
           background: #ffffff;
           border-radius: 10px;
           border: 1px solid #e5e7eb;
           margin-left: 24px;
-          font-size: 16px;
+          font-size: 14px;
           color: #e7000b;
           cursor: pointer;
 
           img {
-            width: 16px;
-            height: 16px;
-            margin-right: 8px;
+            width: 14px;
+            height: 14px;
             margin-top: 2px;
           }
         }
       }
 
       .search-text {
-        font-size: 14px;
+        font-size: 12px;
         color: #e7000b;
         display: flex;
-        margin-top: 6px;
+        margin-top: 4px;
 
         div {
           margin-left: 10px;
@@ -508,16 +561,17 @@ const warpCss = computed(() => {
     .code {
       height: 90px;
       width: 90px;
-      margin-top: 30px;
+      margin-top: 10px;
       margin-left: 70px;
-      border-radius: 4px;
+      // border-radius: 4px;
     }
   }
 
   //分类
   .nav-pages {
-    width: 1300px;
+    width: 100%;
     background-color: #ffffff;
+
     .nav-bos {
       margin: 0 auto;
       width: 1200px;
@@ -527,10 +581,10 @@ const warpCss = computed(() => {
 
       .nav-all {
         width: 234px;
-        height: 48px;
+        height: 40px;
         background: #e7000b;
         padding: 0 10px;
-        font-size: 16px;
+        font-size: 15px;
         color: #ffffff;
         cursor: pointer;
 
@@ -542,8 +596,8 @@ const warpCss = computed(() => {
       }
 
       .nav-list {
-        line-height: 48px;
-        font-size: 16px;
+        line-height: 40px;
+        font-size: 15px;
         color: #364153;
         margin: 0 16px;
         text-align: center;
@@ -552,6 +606,7 @@ const warpCss = computed(() => {
         &.hig {
           color: #e7000b;
           position: relative;
+
           &::before {
             content: '';
             position: absolute;
@@ -574,8 +629,8 @@ const warpCss = computed(() => {
 
   // 头部
   .head-pages {
-    width: 1300px;
-
+    width: 1200px;
+    margin: 0 auto;
     background-size: 100% 100%;
     background-repeat: no-repeat;
     position: relative;
@@ -713,22 +768,50 @@ const warpCss = computed(() => {
 
       // 头部中间
       .head-bos {
-        width: 756px;
+        flex: 1;
 
         .carousel {
           overflow: hidden;
-          width: 756px;
+          width: 100%;
           background: #ffffff;
+          cursor: pointer;
+
+          :deep(.el-carousel__button) {
+            background-color: var(--carousel-color);
+          }
 
           &.carousel-type {
             :deep(.el-carousel__arrow) {
               border-radius: 0;
             }
           }
+
+          &.position1 {
+            :deep(.el-carousel__indicators--horizontal) {
+              left: 5%;
+            }
+          }
+          &.position2 {
+            :deep(.el-carousel__indicators--horizontal) {
+              left: 50%;
+            }
+          }
+          &.position3 {
+            :deep(.el-carousel__indicators--horizontal) {
+              left: 95%;
+            }
+          }
+          &.styleType2 {
+            :deep(.el-carousel__button) {
+              height: 10px;
+              width: 10px;
+              border-radius: 10px;
+            }
+          }
         }
 
         .head-box {
-          width: 756px;
+          width: 740px;
           height: 130px;
           margin-top: 10px;
           display: flex;
@@ -754,12 +837,29 @@ const warpCss = computed(() => {
 
       //右边
       .head-right {
-        flex: 1;
-        width: 0;
+        width: 210px;
         height: 540px;
         background: #ffffff;
         display: flex;
         flex-direction: column;
+        font-size: 14px;
+        .order-bos {
+          width: calc(100% - 20px);
+          height: 110px;
+          font-size: 14px;
+          padding: 16px 0;
+          border-bottom: 1px solid #e5e7eb;
+          margin: 0 10px;
+          img {
+            width: 28px;
+            height: 28px;
+          }
+          .order-num {
+            color: #e7000b;
+            font-size: 14px;
+            margin-top: 2px;
+          }
+        }
 
         .login-bos {
           width: calc(100% - 20px);
@@ -796,6 +896,7 @@ const warpCss = computed(() => {
 
           .loginBtn-bos {
             width: 100%;
+
             .login-bnt1 {
               width: 64px;
               height: 24px;
@@ -803,7 +904,9 @@ const warpCss = computed(() => {
               color: #ffffff;
               border-radius: 24px;
               font-size: 12px;
+              cursor: pointer;
             }
+
             .login-bnt2 {
               width: 64px;
               height: 24px;
@@ -812,6 +915,7 @@ const warpCss = computed(() => {
               border-radius: 24px;
               font-size: 12px;
               margin-left: 10px;
+              cursor: pointer;
             }
           }
         }
@@ -853,14 +957,10 @@ const warpCss = computed(() => {
 
           .real-list {
             width: 100%;
-            font-size: 14px;
+            font-size: 12px;
             color: #1d2129;
             margin-bottom: 12px;
             cursor: pointer;
-
-            &:hover {
-              color: #e7000b;
-            }
           }
         }
 
@@ -899,10 +999,6 @@ const warpCss = computed(() => {
               margin-top: 15px;
               cursor: pointer;
 
-              &:hover {
-                color: #e7000b;
-              }
-
               .img {
                 width: 34px;
                 height: 34px;

+ 89 - 0
src/views/diy/pccomponents/pages/hot.vue

@@ -0,0 +1,89 @@
+<template>
+  <div class="pcPages" :style="warpCss">
+    <el-image class="hot-image" :src="componentData.imageUrl ? componentData.imageUrl : figure" fit="fill" />
+    <div
+      @click="onClick(mapItem)"
+      class="absolute hot-aaaaaa"
+      v-for="(mapItem, mapIndex) in componentData.heatMapData"
+      :key="mapIndex"
+      :style="{ width: mapItem.width + '%', height: mapItem.height + '%', left: mapItem.left + '%', top: mapItem.top + '%' }"
+    ></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 为可选属性
+  row?: any;
+}>();
+
+const onClick = (mapItem: any) => {};
+const componentData = props.row ? props.row : diyStore.componentList[props.index];
+
+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.componentBgUrl) {
+    style += `background-image:url('${componentData.componentBgUrl}');`;
+    style += 'background-size: cover;background-repeat: no-repeat;';
+  }
+  //边距
+  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.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;';
+
+  //间距
+  if (componentData.margin) {
+    if (componentData.margin.top > 0) {
+      style += 'margin-top:' + componentData.margin.top + 'px' + ';';
+    }
+    if (componentData.margin.bottom > 0) {
+      style += 'margin-bottom:' + componentData.margin.bottom + 'px' + ';';
+    }
+  }
+  return style;
+});
+</script>
+
+<style lang="scss" scoped>
+.pcPages {
+  width: 1200px;
+  margin: 0 auto;
+  position: relative;
+
+  .hot-image {
+    width: 100%;
+
+
+  }
+
+  .hot-aaaaaa {
+      border: 1px red solid;
+      z-index: 10;
+    }
+}
+</style>

+ 13 - 2
src/views/diy/pccomponents/pages/imageCube.vue

@@ -6,7 +6,7 @@
           <el-image
             class="img"
             :src="item.imageUrl ? item.imageUrl : figure"
-            :fit="item.imgType == 1 ? 'fill' : item.imgType == 2 ? 'contain' : 'cover'"
+            :fit="item.imageUrl ? (item.imgType == 1 ? 'fill' : item.imgType == 2 ? 'contain' : 'cover') : 'cover'"
             :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
           />
         </div>
@@ -21,8 +21,9 @@ import figure from '@/assets/images/figure.png';
 const diyStore = usePcdiyStore();
 const props = defineProps<{
   index: number; // 确保声明 index 为可选属性
+  row?: any;
 }>();
-const componentData = diyStore.componentList[props.index];
+const componentData = props.row ? props.row : diyStore.componentList[props.index];
 
 const warpCss = computed(() => {
   let style = '';
@@ -50,6 +51,15 @@ const warpCss = computed(() => {
   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;';
+  //间距
+  if (componentData.margin) {
+    if (componentData.margin.top > 0) {
+      style += 'margin-top:' + componentData.margin.top + 'px' + ';';
+    }
+    if (componentData.margin.bottom > 0) {
+      style += 'margin-bottom:' + componentData.margin.bottom + 'px' + ';';
+    }
+  }
 
   return style;
 });
@@ -86,6 +96,7 @@ const boxCss = computed(() => {
       .img {
         width: 100%;
         height: 100%;
+        cursor: pointer;
       }
     }
   }

+ 16 - 5
src/views/diy/pccomponents/pages/navigation.vue

@@ -4,8 +4,8 @@
       <el-carousel :height="170 * 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-center" :style="boxCss">
-              <div :style="titleCss">{{ item.title || '' }}</div>
+            <div v-for="(item, index) in item1" :key="index" class="data-list flex-column-center hover-color" :style="boxCss">
+              <div :style="titleCss" class="zi-hover">{{ item.title || '' }}</div>
               <div :style="subtitleCss" class="mt-[2px] mb-[12px]">{{ item.subtitle || '' }}</div>
               <el-image
                 class="img"
@@ -19,8 +19,8 @@
       </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-center" :style="boxCss">
-        <div :style="titleCss">{{ item.title || '' }}</div>
+      <div v-for="(item, index) in componentData.navlList" :key="index" class="data-list flex-column-center hover-color" :style="boxCss">
+        <div :style="titleCss" class="zi-hover">{{ item.title || '' }}</div>
         <div :style="subtitleCss" class="mt-[2px] mb-[12px]">{{ item.subtitle || '' }}</div>
         <el-image
           class="img"
@@ -39,8 +39,9 @@ import figure from '@/assets/images/figure.png';
 const diyStore = usePcdiyStore();
 const props = defineProps<{
   index: number; // 确保声明 index 为可选属性
+  row?: any;
 }>();
-const componentData = diyStore.componentList[props.index];
+const componentData = props.row ? props.row : diyStore.componentList[props.index];
 
 const dataList = computed(() => {
   const chunkSize = componentData.number * componentData.count;
@@ -74,6 +75,15 @@ const warpCss = computed(() => {
     style += 'padding-left:' + componentData.padding.both + 'px' + ';';
     if (componentData.styleType == 1) style += 'flex-wrap:wrap' + ';';
   }
+  //间距
+  if (componentData.margin) {
+    if (componentData.margin.top > 0) {
+      style += 'margin-top:' + componentData.margin.top + 'px' + ';';
+    }
+    if (componentData.margin.bottom > 0) {
+      style += 'margin-bottom:' + componentData.margin.bottom + 'px' + ';';
+    }
+  }
   return style;
 });
 
@@ -122,6 +132,7 @@ const subtitleCss = computed(() => {
     overflow-x: auto;
     .data-list {
       min-height: 170px;
+      cursor: pointer;
 
       .img {
         height: 80px;

+ 28 - 17
src/views/diy/pccomponents/pages/textTitle.vue

@@ -3,29 +3,29 @@
     <div :style="warpCss">
       <!-- 风格1 -->
       <div :style="boxCss" v-if="componentData.styleType == 1">
-        <div :style="titleCss">{{ componentData.title }}{{ componentData.titleUrl }}</div>
+        <div class="hover-color" :style="titleCss">{{ componentData.title }}{{ componentData.titleUrl }}</div>
       </div>
       <!-- 风格2 -->
       <div :style="boxCss" class="style2 flex-row-center" v-else-if="componentData.styleType == 2">
         <div class="style2-border" :style="{ backgroundColor: componentData.titleColor }"></div>
-        <div class="style2-text" :style="titleCss">{{ componentData.title }}</div>
+        <div class="style2-text hover-color" :style="titleCss">{{ componentData.title }}</div>
         <div class="style2-border" :style="{ backgroundColor: componentData.titleColor }"></div>
       </div>
       <!-- 风格3 -->
       <div :style="boxCss" class="style3 flex-column-center" v-else-if="componentData.styleType == 3">
-        <div :style="titleCss" class="mb-[4px]">{{ componentData.title }}</div>
+        <div :style="titleCss" class="mb-[4px] hover-color">{{ componentData.title }}</div>
         <div class="style3-border" :style="{ backgroundColor: componentData.titleColor }"></div>
         <div class="style3-jiao" :style="{ borderColor: componentData.titleColor }"></div>
       </div>
       <!-- 风格4 -->
       <div :style="boxCss" class="style4 flex-column-center" v-else-if="componentData.styleType == 4">
-        <div :style="titleCss" class="mb-[4px]">{{ componentData.title }}</div>
+        <div :style="titleCss" class="mb-[4px] hover-color">{{ componentData.title }}</div>
         <div class="style4-border1" :style="{ backgroundColor: componentData.titleColor }"></div>
         <div class="style4-border2" :style="{ backgroundColor: componentData.titleColor }"></div>
       </div>
       <!-- 风格5 -->
       <div :style="boxCss" class="style5 flex-column-center" v-else-if="componentData.styleType == 5">
-        <div :style="titleCss" class="mb-[4px]">{{ componentData.title }}</div>
+        <div :style="titleCss" class="mb-[4px] hover-color">{{ componentData.title }}</div>
         <div class="flex-row-center">
           <div class="style5-border" :style="{ backgroundColor: componentData.titleColor }"></div>
           <div class="style5-box" :style="{ borderColor: componentData.titleColor }"></div>
@@ -36,34 +36,34 @@
       <div :style="boxCss" class="style6 flex-column-center" v-else-if="componentData.styleType == 6">
         <div class="style6-bos flex-row-center" :style="{ borderColor: componentData.titleColor }">
           <div class="style6-border style6-border1" :style="{ backgroundColor: componentData.titleColor }"></div>
-          <div :style="titleCss" class="style6-box">{{ componentData.title }}</div>
+          <div :style="titleCss" class="style6-box hover-color">{{ componentData.title }}</div>
           <div class="style6-border style6-border2" :style="{ backgroundColor: componentData.titleColor }"></div>
         </div>
       </div>
       <!-- 风格7 -->
       <div :style="boxCss" class="style7 flex-column-center" v-else-if="componentData.styleType == 7">
         <div class="style7-bos">
-          <div class="style7-text" :style="titleCss">{{ componentData.title }}</div>
+          <div class="style7-text hover-color" :style="titleCss">{{ componentData.title }}</div>
           <div class="style7-line" :style="{ borderColor: componentData.titleColor }"></div>
         </div>
       </div>
       <!-- 风格8 -->
       <div :style="boxCss" class="style8 flex-column-center" v-else-if="componentData.styleType == 8">
         <div class="style8-bos">
-          <div class="style8-text" :style="titleCss">{{ componentData.title }}</div>
+          <div class="style8-text hover-color" :style="titleCss">{{ componentData.title }}</div>
           <div class="style8-line" :style="{ borderColor: componentData.titleColor }"></div>
         </div>
       </div>
       <!-- 风格9 -->
       <div :style="boxCss" class="style9 flex-row-start" v-else-if="componentData.styleType == 9">
         <div class="style9-border" :style="{ backgroundColor: componentData.titleColor }"></div>
-        <div class="pl-[10px]" :style="titleCss">{{ componentData.title }}</div>
+        <div class="pl-[10px] hover-color" :style="titleCss">{{ componentData.title }}</div>
       </div>
       <!-- 风格10 -->
       <div :style="boxCss" class="style10 flex-column-center" v-else-if="componentData.styleType == 10">
         <div class="style10-bos flex-row-start">
           <img class="style10-img1" src="@/assets/images/pcdiy/style10-1.png" alt="" />
-          <div :style="titleCss">{{ componentData.title }}</div>
+          <div :style="titleCss" class="hover-color">{{ componentData.title }}</div>
           <img class="style10-img2" src="@/assets/images/pcdiy/style10-2.png" alt="" />
         </div>
         <div class="mt-[2px]" :style="subtitleCss">
@@ -74,7 +74,7 @@
       <div :style="boxCss" class="style11 flex-column-center" v-else-if="componentData.styleType == 11">
         <div class="style11-bos flex-row-start">
           <img class="style11-img style11-img1" src="@/assets/images/pcdiy/style11-1.png" alt="" />
-          <div :style="titleCss" class="style11-text">{{ componentData.title }}</div>
+          <div :style="titleCss" class="style11-text hover-color">{{ componentData.title }}</div>
           <img class="style11-img style11-img2" src="@/assets/images/pcdiy/style11-2.png" alt="" />
           <img class="style11-img3" src="@/assets/images/pcdiy/style11-3.png" alt="" />
         </div>
@@ -85,10 +85,10 @@
       <!-- 风格12 -->
       <div :style="boxCss" class="style12 flex-row-between" v-else-if="componentData.styleType == 12">
         <div class="flex-row-start">
-          <div :style="titleCss">{{ componentData.title }}</div>
+          <div :style="titleCss" class="hover-color">{{ componentData.title }}</div>
           <div :style="subtitleCss" class="ml-[5px]">{{ componentData.subtitle }}</div>
         </div>
-        <div v-if="componentData.moreShow" class="flex-row-start" :style="moreCss">
+        <div v-if="componentData.moreShow" class="flex-row-start hover-color" :style="moreCss">
           <div>{{ componentData.more }}</div>
           <el-icon><ArrowRight /></el-icon>
         </div>
@@ -97,17 +97,17 @@
       <div :style="boxCss" class="style13 flex-column-center" v-else-if="componentData.styleType == 13">
         <div class="style13-bos flex-row-start">
           <img class="style13-img1" src="@/assets/images/pcdiy/style13-1.png" alt="" />
-          <div :style="titleCss">{{ componentData.title }}</div>
+          <div :style="titleCss" class="hover-color">{{ componentData.title }}</div>
           <img class="style13-img2" src="@/assets/images/pcdiy/style13-2.png" alt="" />
         </div>
       </div>
       <!-- 风格14 -->
       <div :style="boxCss" class="style14 flex-row-between" v-else-if="componentData.styleType == 14">
         <div>
-          <div :style="titleCss">{{ componentData.title }}</div>
+          <div :style="titleCss" class="hover-color">{{ componentData.title }}</div>
           <div :style="subtitleCss">{{ componentData.subtitle }}</div>
         </div>
-        <div v-if="componentData.moreShow" class="flex-row-start" :style="moreCss">
+        <div v-if="componentData.moreShow" class="flex-row-start hover-color" :style="moreCss">
           <div>{{ componentData.more }}</div>
           <el-icon><ArrowRight /></el-icon>
         </div>
@@ -122,8 +122,9 @@ const diyStore = usePcdiyStore();
 
 const props = defineProps<{
   index: number; // 确保声明 index 为可选属性
+  row?: any;
 }>();
-const componentData = diyStore.componentList[props.index];
+const componentData = props.row ? props.row : diyStore.componentList[props.index];
 
 const warpCss = computed(() => {
   let style = '';
@@ -157,6 +158,16 @@ const warpCss = computed(() => {
   if (componentData.bottomRounded) style += 'border-bottom-left-radius:' + componentData.bottomRounded + 'px;';
   if (componentData.bottomRounded) style += 'border-bottom-right-radius:' + componentData.bottomRounded + 'px;';
 
+  //间距
+  if (componentData.margin) {
+    if (componentData.margin.top > 0) {
+      style += 'margin-top:' + componentData.margin.top + 'px' + ';';
+    }
+    if (componentData.margin.bottom > 0) {
+      style += 'margin-bottom:' + componentData.margin.bottom + 'px' + ';';
+    }
+  }
+
   return style;
 });
 

+ 48 - 457
src/views/diy/pcdiy.vue

@@ -1,479 +1,70 @@
 <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 class="pcdiys-pages" :style="{ backgroundColor: componentList.backgroundColor }">
+    <div v-for="(item, index) in componentList" :key="index">
+      <component :is="item.components" :key="item.itemKey" :index="index" :row="item"></component>
     </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 { pcLookDiy } from '@/api/diy/index';
+import { loadDiyComponents, getComponentGroups } from '@/views/diy/pccomponents/index';
 
-import usePcdiyStore from '@/store/modules/pcdiy';
-const diyStore = usePcdiyStore();
+const componentList = ref<any>([]);
+const collapse = ref<any>([]);
+const id = ref<any>(null);
 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 formData = ref<any>({});
 
-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)
-      }
-    ]
+onMounted(() => {
+  loadComponents();
+  if (query.id) {
+    id.value = query.id;
   }
-]);
-
-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) => {
+});
+
+// 加载组件
+const loadComponents = async () => {
+  const components = await loadDiyComponents();
+  collapse.value = getComponentGroups(components);
+  if (id.value) {
+    pcLookDiy(id.value).then((res) => {
       if (res.code == 200) {
+        formData.value = res.data;
+        document.documentElement.style.setProperty('--hover-color', res.data.hoverColor ? res.data.hoverColor : '#E7000B');
+        const datas = JSON.parse(res.data.property);
+        collapse.value.forEach((item1: any) => {
+          datas.forEach((item2: any) => {
+            if (item1.id == item2.fid) {
+              item1.list.forEach((item3: any) => {
+                if (item2.id == item3.id) {
+                  item2.components = item3.components;
+                  item2.edit = item3.edit;
+                }
+              });
+            }
+          });
+        });
+        componentList.value = datas;
       }
-    })
-    .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);
+.pcdiys-pages {
+  min-height: calc(100vh);
+  background-color: #f2f2f2;
+  :deep(.hover-color) {
+    cursor: pointer;
+    &:hover {
+      color: var(--hover-color) !important;
+      .zi-hover {
+        color: var(--hover-color) !important;
       }
     }
-
-    .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>