weixin_52219567 4 週間 前
コミット
5656e010b5
33 ファイル変更1507 行追加1032 行削除
  1. 0 1
      src/api/pmsProduct/base/index.ts
  2. 8 6
      src/api/pmsProduct/base/types.ts
  3. BIN
      src/assets/images/pcdiy/activity1.png
  4. BIN
      src/assets/images/pcdiy/activity2.png
  5. BIN
      src/assets/images/pcdiy/activity3-1.png
  6. BIN
      src/assets/images/pcdiy/activity3.png
  7. BIN
      src/assets/images/pcdiy/activity4-1.png
  8. BIN
      src/assets/images/pcdiy/activity4.png
  9. BIN
      src/assets/images/pcdiy/activity5.png
  10. BIN
      src/assets/images/pcdiy/activity6.png
  11. BIN
      src/assets/images/pcdiy/activity7.png
  12. BIN
      src/assets/images/pcdiy/activity8.png
  13. 247 0
      src/components/goodsDialog/index.vue
  14. 99 15
      src/store/modules/pcdiy.ts
  15. 1 5
      src/views/diy/pcEdit.vue
  16. 250 71
      src/views/diy/pccomponents/edit/activity-edit.vue
  17. 51 0
      src/views/diy/pccomponents/edit/blank-edit.vue
  18. 56 0
      src/views/diy/pccomponents/edit/border-edit.vue
  19. 4 181
      src/views/diy/pccomponents/edit/discover-edit.vue
  20. 43 0
      src/views/diy/pccomponents/edit/editordiy-edit.vue
  21. 9 179
      src/views/diy/pccomponents/edit/floor-edit.vue
  22. 18 227
      src/views/diy/pccomponents/edit/goods-edit.vue
  23. 15 185
      src/views/diy/pccomponents/edit/goodsList-edit.vue
  24. 23 3
      src/views/diy/pccomponents/index.ts
  25. 275 89
      src/views/diy/pccomponents/pages/activity.vue
  26. 82 0
      src/views/diy/pccomponents/pages/blank.vue
  27. 92 0
      src/views/diy/pccomponents/pages/border.vue
  28. 30 21
      src/views/diy/pccomponents/pages/discover.vue
  29. 83 0
      src/views/diy/pccomponents/pages/editordiy.vue
  30. 14 5
      src/views/diy/pccomponents/pages/floor.vue
  31. 91 27
      src/views/diy/pccomponents/pages/goods.vue
  32. 15 9
      src/views/diy/pccomponents/pages/goodsList.vue
  33. 1 8
      src/views/diy/pccomponents/pages/hot.vue

+ 0 - 1
src/api/pmsProduct/base/index.ts

@@ -198,4 +198,3 @@ export const changeProductType = (data: BaseForm) => {
     data: data
   });
 };
-

+ 8 - 6
src/api/pmsProduct/base/types.ts

@@ -242,7 +242,6 @@ export interface BaseVO {
    * 商品属性值(JSON字符串)
    */
   attributesList?: string;
-
 }
 
 export interface BaseForm extends BaseEntity {
@@ -575,11 +574,9 @@ export interface BaseForm extends BaseEntity {
    * 上下架审核意见
    */
   shelfComments?: string;
-
 }
 
 export interface BaseQuery extends PageQuery {
-
   /**
    * 搜索文本(商品名称/商品编号)
    */
@@ -733,6 +730,14 @@ export interface BaseQuery extends PageQuery {
    * 指定商品
    */
   ids?: any;
+  /**
+   * 分类多选
+   */
+  categoryIds?: any;
+  /**
+   * 品牌多选
+   */
+  brandIds?: any;
 }
 /**
  * 状态数量统计视图对象
@@ -768,6 +773,3 @@ export class StatusCountVo {
    */
   auditReject: number | null = null;
 }
-
-
-

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


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


BIN
src/assets/images/pcdiy/activity3-1.png


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


BIN
src/assets/images/pcdiy/activity4-1.png


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


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


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


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


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


+ 247 - 0
src/components/goodsDialog/index.vue

@@ -0,0 +1,247 @@
+<template>
+  <div>
+    <el-dialog v-model="showDialog" title="选择商品" width="1400">
+      <div class="dialog-bos">
+        <div class="flex">
+          <el-tree-select
+            v-model="goodsClassify"
+            :data="categoryOptions"
+            :props="treeProps"
+            value-key="id"
+            placeholder="请选择商品分类"
+            clearable
+            check-strictly
+            @change="goodsClassifyChange"
+            style="width: 300px; margin-bottom: 10px"
+          />
+          <el-input class="ml-[20px]" v-model="queryParams.itemName" placeholder="请输入商品名称" clearable style="width: 300px; margin-bottom: 10px">
+            <template #append>
+              <el-button :icon="Search" @click="handleQuery" />
+            </template>
+          </el-input>
+        </div>
+
+        <div class="flex">
+          <div class="tree-bos">
+            <!-- <el-tree :data="categoryOptions" :props="defaultProps" @node-click="handleNodeClick" :highlight-current="true" /> -->
+          </div>
+          <el-table height="600" ref="multipleTableRef" v-loading="loading" :data="tableData" border @selection-change="handleSelectionChange">
+            <el-table-column type="selection" width="55" />
+            <el-table-column label="商品图片" align="center" prop="productImage" width="100">
+              <template #default="scope">
+                <image-preview :src="scope.row.productImage" :width="60" :height="60" />
+              </template>
+            </el-table-column>
+            <el-table-column label="商品信息" align="center" minWidth="250" show-overflow-tooltip>
+              <template #default="scope">
+                <div class="text-left">
+                  <div>{{ scope.row.itemName }}</div>
+                  <div class="text-gray-500" style="font-size: 12px">品牌: {{ scope.row.brandName || '-' }}</div>
+                </div>
+              </template>
+            </el-table-column>
+            <el-table-column label="SKU价格" align="center" width="180">
+              <template #default="scope">
+                <div class="text-left" style="font-size: 12px">
+                  <div>
+                    <span class="text-gray-500">市场价:</span>
+                    <span class="text-red-500">¥{{ scope.row.marketPrice || '0.00' }}</span>
+                  </div>
+                  <div>
+                    <span class="text-gray-500">会员价:</span>
+                    <span class="text-red-500">¥{{ scope.row.memberPrice || '0.00' }}</span>
+                  </div>
+                  <div>
+                    <span class="text-gray-500">最低价:</span>
+                    <span class="text-red-500">¥{{ scope.row.minSellingPrice || '0.00' }}</span>
+                  </div>
+                </div>
+              </template>
+            </el-table-column>
+            <el-table-column label="成本情况" align="center" width="150">
+              <template #default="scope">
+                <div class="text-left" style="font-size: 12px">
+                  <div>
+                    <span class="text-gray-500">采购价:</span>
+                    <span>¥{{ scope.row.purchasingPrice || '0.00' }}</span>
+                  </div>
+                  <div>
+                    <span class="text-gray-500">暂估毛利率:</span>
+                    <span>{{ scope.row.tempGrossMargin || '0.0000' }}%</span>
+                  </div>
+                </div>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+        <pagination v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList">
+          <template #slotDiv>
+            <div class="selected">已选择 {{ multipleSelection.length }} 个</div>
+          </template>
+        </pagination>
+      </div>
+
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="showDialog = false">取消</el-button>
+          <el-button type="primary" @click="onConfirm">确认</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { Search } from '@element-plus/icons-vue';
+import { listBase } from '@/api/pmsProduct/base';
+import usePcdiyStore from '@/store/modules/pcdiy';
+import type { TableInstance } from 'element-plus';
+const goodsClassify = ref<any>('');
+const diyStore = usePcdiyStore();
+const showDialog = ref(false);
+const loading = ref(false);
+const tableData = ref<any[]>([]);
+const resultList = ref<any>([]); //单页之前被选中的数据
+const multipleSelection: any = ref([]); // 选中数据
+const multipleTableRef = ref<TableInstance>();
+const total = ref(0);
+const queryParams = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  itemName: '',
+  topCategoryId: '',
+  mediumCategoryId: '',
+  bottomCategoryId: ''
+});
+const treeProps = {
+  value: 'id',
+  label: 'label',
+  children: 'children'
+};
+const props = defineProps<{
+  categoryOptions?: any;
+  navIndex?: any;
+}>();
+const onOpen = () => {
+  console.log('打开弹窗');
+  showDialog.value = true;
+  getList();
+};
+
+// 监听表格单行选中
+const handleSelectionChange = (val: []) => {
+  multipleSelection.value = val;
+};
+
+/** 搜索 */
+const handleQuery = () => {
+  queryParams.pageNum = 1;
+  getList();
+};
+
+/** 获取列表 */
+const getList = async () => {
+  loading.value = true;
+  try {
+    const res = await listBase(queryParams);
+    tableData.value = res.rows || [];
+    let result = [];
+    if (props.navIndex || props.navIndex == 0) {
+      result = tableData.value.filter((item: any) => diyStore.editComponent.tabList[props.navIndex].goodsIds.includes(item.id));
+    } else {
+      result = tableData.value.filter((item: any) => diyStore.editComponent.goodsIds.includes(item.id));
+    }
+    resultList.value = result;
+    nextTick(() => {
+      result.forEach((item: any) => {
+        multipleTableRef.value?.toggleRowSelection(item, true);
+      });
+    });
+    total.value = res.total || 0;
+  } finally {
+    loading.value = false;
+  }
+};
+
+//确定
+const onConfirm = () => {
+  if (props.navIndex || props.navIndex == 0) {
+    const newIds = calculateNewIds(diyStore.editComponent.tabList[props.navIndex].goodsIds, tableData.value, multipleSelection.value);
+    diyStore.editComponent.tabList[props.navIndex].goodsIds = newIds;
+  } else {
+    const newIds = calculateNewIds(diyStore.editComponent.goodsIds, tableData.value, multipleSelection.value);
+    diyStore.editComponent.goodsIds = newIds;
+  }
+  showDialog.value = false;
+};
+
+const calculateNewIds = (cacheIds: any, allPageItems: any, selectedItems: any) => {
+  // 1. 获取当前页所有存在的 ID 集合 (用于识别哪些旧数据属于当前页)
+  const currentPageIdSet = new Set(allPageItems.map((item) => item.id));
+  // 2. 获取最终选中项的 ID 集合
+  const selectedIdSet = new Set(selectedItems.map((item) => item.id));
+  // 3. 过滤旧的缓存 IDs
+  const retainedOldIds = cacheIds.filter((id) => {
+    // 情况 A: 该 ID 不在当前页数据中 (说明是其他页的数据,必须无条件保留)
+    if (!currentPageIdSet.has(id)) {
+      return true;
+    }
+    // 情况 B: 该 ID 在当前页数据中,且也在最终选中列表中 (说明用户保持了选中)
+    if (selectedIdSet.has(id)) {
+      return true;
+    }
+    // 情况 C: 该 ID 在当前页数据中,但不在最终选中列表中 (说明用户取消了选中,如 ID 4)
+    // 返回 false,将其剔除
+    return false;
+  });
+  // 4. 合并:保留的旧数据 + 当前页新选中的数据
+  // 使用 Set 去重,虽然逻辑上 retainedOldIds 和 selectedIdSet 不会有交集,但以防万一
+  const newIdsSet = new Set([...retainedOldIds, ...selectedIdSet]);
+  // 转回数组 (如果需要保持原有顺序或特定排序,可以在此处调整)
+  // 这里简单转为数组,通常建议按数字大小排序以便阅读,或者保持插入顺序
+  return Array.from(newIdsSet).sort((a, b) => a - b);
+};
+
+//选择商品分类
+const goodsClassifyChange = (res: any) => {
+  const foundNode = findNodeByKey(props.categoryOptions, res);
+  goodsClassify.value = res;
+  queryParams.topCategoryId = '';
+  queryParams.mediumCategoryId = '';
+  queryParams.bottomCategoryId = '';
+  if (foundNode.parentId == 0) {
+    queryParams.topCategoryId = foundNode.id;
+  } else if (foundNode.children) {
+    queryParams.mediumCategoryId = foundNode.id;
+  } else {
+    queryParams.bottomCategoryId = foundNode.id;
+  }
+  handleQuery();
+};
+
+// 递归查找节点的辅助函数
+const findNodeByKey = (nodes: any, key: any) => {
+  for (const node of nodes) {
+    if (node.id === key) {
+      return node;
+    }
+    if (node.children) {
+      const found = findNodeByKey(node.children, key);
+      if (found) return found;
+    }
+  }
+  return null;
+};
+
+defineExpose({
+  onOpen
+});
+</script>
+
+<style lang="scss">
+.selected {
+  line-height: 32px;
+  position: absolute;
+  left: 0px;
+}
+</style>

+ 99 - 15
src/store/modules/pcdiy.ts

@@ -330,6 +330,78 @@ const usePcdiyStore: any = defineStore('pcdiy', {
         newItem.gap = 10;
         newItem.imageTopRounded = 10;
         newItem.imageBottomRoundedRounded = 10;
+      } else if (item.id == 5) {
+        // 活动魔方
+        newItem.styleType = 1;
+        newItem.title = '超值爆款';
+        newItem.titleUrl = '';
+        newItem.titleImg = '';
+        newItem.titleImgType = 1;
+        newItem.titleColor = '#101828';
+        newItem.subtitle = '为您精选爆款';
+        newItem.subtitleUrl = '';
+        newItem.subtitleColor = '#ffffff';
+        newItem.subtitleColor1 = '#FA742C';
+        newItem.subtitleColor2 = '#F91C02';
+        newItem.navType = 1;
+        newItem.navWeight = 'bold';
+        newItem.navBtnType = 1;
+        newItem.navtopRounded = 10;
+        newItem.navBtnbottomRounded = 10;
+        newItem.navlList = [
+          {
+            imageUrl: '',
+            imgType: 1,
+            title: '标题名称',
+            subtitle: '副标题名称',
+            color1: '#ffffff',
+            color2: '',
+            btnText: '去看看',
+            btncolor1: '#FA742C',
+            btncolor2: '#F91C02',
+            url: '',
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            imgType: 1,
+            title: '标题名称',
+            subtitle: '副标题名称',
+            color1: '#ffffff',
+            color2: '',
+            btnText: '去看看',
+            btncolor1: '#FA742C',
+            btncolor2: '#F91C02',
+            url: '',
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            imgType: 1,
+            title: '标题名称',
+            subtitle: '副标题名称',
+            color1: '#ffffff',
+            color2: '',
+            btnText: '去看看',
+            btncolor1: '#FA742C',
+            btncolor2: '#F91C02',
+            url: '',
+            id: Date.now()
+          },
+          {
+            imageUrl: '',
+            imgType: 1,
+            title: '标题名称',
+            subtitle: '副标题名称',
+            color1: '#ffffff',
+            color2: '',
+            btnText: '去看看',
+            btncolor1: '#FA742C',
+            btncolor2: '#F91C02',
+            url: '',
+            id: Date.now()
+          }
+        ];
       } else if (item.id == 6) {
         //轮播图
         newItem.imageHeight = 200;
@@ -573,13 +645,14 @@ const usePcdiyStore: any = defineStore('pcdiy', {
         newItem.goodsShow = [1, 2, 3];
         newItem.goodsType = 1;
         newItem.goodsIds = [];
+        newItem.categoryIds = [];
         newItem.goodsClassify = '';
         newItem.topCategoryId = '';
         newItem.mediumCategoryId = '';
         newItem.bottomCategoryId = '';
         newItem.goodsNumber = 5;
         newItem.goodsSort = 1;
-        newItem.goodsBrand = '';
+        newItem.brandIds = [];
 
         newItem.goodsbackgroundColor = '#ffffff';
         newItem.goodsTitleType = 1;
@@ -620,6 +693,7 @@ const usePcdiyStore: any = defineStore('pcdiy', {
             title: '选项卡',
             goodsType: 1,
             goodsIds: [],
+            categoryIds: [],
             goodsClassify: '',
             topCategoryId: '',
             mediumCategoryId: '',
@@ -656,24 +730,24 @@ const usePcdiyStore: any = defineStore('pcdiy', {
         newItem.planList = [
           {
             imageUrl: '',
-            title: '',
-            subtitle: '',
+            title: '方案名称',
+            subtitle: '方案详情',
             url: '',
             imgType: 1,
             id: Date.now()
           },
           {
             imageUrl: '',
-            title: '',
-            subtitle: '',
+            title: '方案名称',
+            subtitle: '方案详情',
             url: '',
             imgType: 1,
             id: Date.now()
           },
           {
             imageUrl: '',
-            title: '',
-            subtitle: '',
+            title: '方案名称',
+            subtitle: '方案详情',
             url: '',
             imgType: 1,
             id: Date.now()
@@ -683,24 +757,24 @@ const usePcdiyStore: any = defineStore('pcdiy', {
         newItem.detectList = [
           {
             imageUrl: '',
-            title: '',
-            subtitle: '',
+            title: '名称',
+            subtitle: '详情',
             url: '',
             imgType: 1,
             id: Date.now()
           },
           {
             imageUrl: '',
-            title: '',
-            subtitle: '',
+            title: '名称',
+            subtitle: '详情',
             url: '',
             imgType: 1,
             id: Date.now()
           },
           {
             imageUrl: '',
-            title: '',
-            subtitle: '',
+            title: '名称',
+            subtitle: '详情',
             url: '',
             imgType: 1,
             id: Date.now()
@@ -717,11 +791,21 @@ const usePcdiyStore: any = defineStore('pcdiy', {
         newItem.goodsIds = [];
       } else if (item.id == 14) {
         // 热区
-        newItem.title = '456';
         newItem.imageUrl = '';
         newItem.imgWidth = 0;
         newItem.imgHeight = 0;
         newItem.heatMapData = [];
+      } else if (item.id == 15) {
+        // 富文本
+        newItem.detail = '';
+        newItem.pageStartBgColor = '#ffffff';
+      } else if (item.id == 16) {
+        // 辅助空白
+        newItem.boxHeight = 20;
+      } else if (item.id == 17) {
+        // 辅助线
+        newItem.boxHeight = 1;
+        newItem.boxColor = '#000000';
       }
 
       this.componentList.push(newItem);
@@ -783,7 +867,7 @@ const usePcdiyStore: any = defineStore('pcdiy', {
     onIndex(index: any) {
       this.currentIndex = index;
     },
-    resetComponent(){
+    resetComponent() {
       this.currentIndex = -99;
     }
   }

+ 1 - 5
src/views/diy/pcEdit.vue

@@ -44,7 +44,7 @@
             <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>
@@ -271,10 +271,6 @@ const onDragMove = (evt: any) => {
   return true;
 };
 
-const itemKey = ref<any>(0);
-//左边得组件
-const uniqueIdCounter = ref<any>(0);
-
 const handleChange = (val: string[]) => {};
 // 返回上一页
 const goBack = () => {};

+ 250 - 71
src/views/diy/pccomponents/edit/activity-edit.vue

@@ -2,27 +2,44 @@
   <div class="pc-edit">
     <div class="content-wrap" v-show="diyStore.editTab == 'content'">
       <div class="edit-attr-item-wrap">
-        <h3 class="mb-[10px]">导航模式</h3>
+        <h3 class="mb-[10px]">标题内容</h3>
         <el-form label-width="80px" class="px-[10px]">
-          <el-form-item label="展示风格">
-            <el-radio-group v-model="diyStore.editComponent.styleType">
-              <el-radio :value="1">固定显示</el-radio>
-              <el-radio :value="2">单行滑动</el-radio>
-              <el-radio :value="3">分页滑动</el-radio>
-            </el-radio-group>
+          <el-form-item label="风格选择" class="flex">
+            <span class="text-primary flex-1 cursor-pointer" @click="showStyle1">风格{{ diyStore.editComponent.styleType }}</span>
+            <el-icon @click="showStyle1" class="cursor-pointer">
+              <ArrowRight />
+            </el-icon>
           </el-form-item>
-          <el-form-item label="每行数量">
-            <el-radio-group v-model="diyStore.editComponent.number">
-              <el-radio :value="3">3个</el-radio>
-              <el-radio :value="4">4个</el-radio>
-              <el-radio :value="5">5个</el-radio>
-            </el-radio-group>
+          <el-form-item label="标题图片" v-if="diyStore.editComponent.styleType == 4">
+            <div class="flex-row-start">
+              <upload-image v-model="diyStore.editComponent.titleImg" :limit="1" />
+              <div class="flex-column-between images-bos">
+                <div class="annotation3">(建议上传尺寸相同图片,推荐尺寸30*150)</div>
+                <div class="flex-row-between images-box">
+                  <div>缩放模式</div>
+                  <div class="flex-row-start" @click="openImageType({ imgType: diyStore.editComponent.titleImgType }, 1)">
+                    <span style="margin-top: 2px" class="text-primary flex-1 cursor-pointer">{{
+                      diyStore.editComponent.titleImgType == 1 ? '拉伸' : diyStore.editComponent.titleImgType == 2 ? '缩放' : '填充'
+                    }}</span>
+                    <el-icon class="cursor-pointer">
+                      <ArrowRight />
+                    </el-icon>
+                  </div>
+                </div>
+              </div>
+            </div>
           </el-form-item>
-          <el-form-item label="每页行数" v-if="diyStore.editComponent.styleType == 3">
-            <el-radio-group v-model="diyStore.editComponent.count">
-              <el-radio :value="1">1个</el-radio>
-              <el-radio :value="2">2个</el-radio>
-            </el-radio-group>
+          <el-form-item label="标题名称" v-else>
+            <el-input v-model="diyStore.editComponent.title" placeholder="请输入标题名称" />
+          </el-form-item>
+          <el-form-item label="链接地址">
+            <WebLinkInput v-model="diyStore.editComponent.titleUrl" placeholder="请输入或选择链接" />
+          </el-form-item>
+          <el-form-item label="副标题">
+            <el-input v-model="diyStore.editComponent.subtitle" placeholder="请输入副标题" />
+          </el-form-item>
+          <el-form-item label="链接地址">
+            <WebLinkInput v-model="diyStore.editComponent.subtitleUrl" placeholder="请输入或选择链接" />
           </el-form-item>
         </el-form>
       </div>
@@ -34,12 +51,15 @@
           </div>
         </div>
         <el-form label-width="86px" class="px-[10px]">
+          <el-form-item label="风格选择" class="mt-[20px]">
+            <span class="text-primary flex-1 cursor-pointer" @click="showStyle2">风格{{ diyStore.editComponent.navType }}</span>
+            <el-icon @click="showStyle2" class="cursor-pointer">
+              <ArrowRight />
+            </el-icon>
+          </el-form-item>
           <draggable v-model="diyStore.editComponent.navlList" item-key="id">
             <template #item="{ element, index }">
               <div class="edit-attr-box">
-                <el-icon @click="onDel(index)" color="#F56C6C" size="18px" class="circleClose">
-                  <CircleCloseFilled />
-                </el-icon>
                 <el-form-item label="图片上传">
                   <div class="flex-row-start">
                     <upload-image v-model="element.imageUrl" :limit="1" />
@@ -65,13 +85,25 @@
                 <el-form-item label="副标题名称">
                   <el-input v-model="element.subtitle" placeholder="请输入标签名称" />
                 </el-form-item>
+                <el-form-item label="框体颜色">
+                  <el-color-picker v-model="element.color1" />
+                  <icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
+                  <el-color-picker v-model="element.color2" />
+                </el-form-item>
+                <el-form-item label="按钮">
+                  <el-input v-model="element.btnText" placeholder="请输入标签名称" />
+                </el-form-item>
+                <el-form-item label="按钮颜色">
+                  <el-color-picker v-model="element.btncolor1" />
+                  <icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
+                  <el-color-picker v-model="element.btncolor2" />
+                </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>
           </draggable>
-          <el-button @click="onAdd" style="width: 100%; margin-top: 10px">新增导航</el-button>
         </el-form>
       </div>
     </div>
@@ -80,15 +112,6 @@
       <div class="edit-attr-item-wrap">
         <h3 class="mb-[10px]">样式设置</h3>
         <el-form label-width="80px" class="px-[10px]">
-          <el-form-item label="文字大小">
-            <el-slider size="small" v-model="diyStore.editComponent.titleSize" show-input :min="1" :max="50" />
-          </el-form-item>
-          <el-form-item label="文字加粗">
-            <el-radio-group size="small" v-model="diyStore.editComponent.titleWeight" fill="#409eff">
-              <el-radio-button label="加粗" :value="'bold'" />
-              <el-radio-button label="不加粗" :value="'normal'" />
-            </el-radio-group>
-          </el-form-item>
           <el-form-item label="文字颜色">
             <span class="mr-[10px]">{{ diyStore.editComponent.titleColor }}</span>
             <el-color-picker class="mr-[10px]" v-model="diyStore.editComponent.titleColor" />
@@ -99,21 +122,38 @@
       <div class="edit-attr-item-wrap">
         <h3 class="mb-[10px]">副标题样式</h3>
         <el-form label-width="80px" class="px-[10px]">
-          <el-form-item label="文字大小">
-            <el-slider size="small" v-model="diyStore.editComponent.subtitleSize" show-input :min="1" :max="50" />
-          </el-form-item>
-          <el-form-item label="文字颜色">
+          <el-form-item label="标题样式">
             <span class="mr-[10px]">{{ diyStore.editComponent.subtitleColor }}</span>
             <el-color-picker class="mr-[10px]" v-model="diyStore.editComponent.subtitleColor" />
-            <el-button @click="diyStore.editComponent.subtitleColor = '#101828'" size="small">重置</el-button>
+            <el-button @click="diyStore.editComponent.subtitleColor = '#ffffff'" size="small">重置</el-button>
+          </el-form-item>
+          <el-form-item label="背景色">
+            <el-color-picker v-model="diyStore.editComponent.subtitleColor1" />
+            <icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
+            <el-color-picker v-model="diyStore.editComponent.subtitleColor2" />
           </el-form-item>
         </el-form>
       </div>
       <div class="edit-attr-item-wrap">
-        <h3 class="mb-[10px]">图片设置</h3>
+        <h3 class="mb-[10px]">版块样式</h3>
         <el-form label-width="80px" class="px-[10px]">
-          <el-form-item label="图片圆角">
-            <el-slider size="small" v-model="diyStore.editComponent.imageRadius" show-input :min="1" :max="50" />
+          <el-form-item label="文字加粗">
+            <el-radio-group v-model="diyStore.editComponent.navWeight">
+              <el-radio :value="'bold'">加粗</el-radio>
+              <el-radio :value="'normal'">不加粗</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="按钮文字">
+            <el-radio-group v-model="diyStore.editComponent.navBtnType">
+              <el-radio :value="1">常规</el-radio>
+              <el-radio :value="2">斜体</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="上圆角">
+            <el-slider size="small" v-model="diyStore.editComponent.navtopRounded" show-input :min="0" :max="100" />
+          </el-form-item>
+          <el-form-item label="下圆角">
+            <el-slider size="small" v-model="diyStore.editComponent.navBtnbottomRounded" show-input :min="0" :max="100" />
           </el-form-item>
         </el-form>
       </div>
@@ -121,16 +161,142 @@
       <slot name="style"></slot>
     </div>
     <ImagesForm ref="ImagesFormRef" @confirmCallBack="confirmCallBack"></ImagesForm>
+
+    <!-- 风格弹窗1 -->
+    <el-dialog v-model="showDialog1" title="风格选择" width="950">
+      <div class="data-bos">
+        <template v-for="(item, index) in styleList" :key="index">
+          <div
+            :class="{ 'border-primary': styleId1 == item.id }"
+            @click="changeTitleStyle1(item)"
+            class="data-list flex items-center justify-center overflow-hidden w-[200px] h-[100px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]"
+          >
+            <img :src="item.img" />
+          </div>
+        </template>
+      </div>
+
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="showDialog1 = false">取消</el-button>
+          <el-button type="primary" @click="confirmTitleStyle1">确认</el-button>
+        </span>
+      </template>
+    </el-dialog>
+    <!-- 风格弹窗2 -->
+    <el-dialog v-model="showDialog2" title="风格选择" width="950">
+      <div class="data-bos">
+        <template v-for="(item, index) in navList" :key="index">
+          <div
+            :class="{ 'border-primary': styleId2 == item.id }"
+            @click="changeTitleStyle2(item)"
+            class="data-list flex items-center justify-center overflow-hidden w-[400px] h-[100px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]"
+          >
+            <img :src="item.img" />
+          </div>
+        </template>
+      </div>
+
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="showDialog2 = false">取消</el-button>
+          <el-button type="primary" @click="confirmTitleStyle2">确认</el-button>
+        </span>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
 <script lang="ts" setup>
+import activity1 from '@/assets/images/pcdiy/activity1.png';
+import activity2 from '@/assets/images/pcdiy/activity2.png';
+import activity3 from '@/assets/images/pcdiy/activity3.png';
+import activity4 from '@/assets/images/pcdiy/activity4.png';
+import activity5 from '@/assets/images/pcdiy/activity5.png';
+import activity6 from '@/assets/images/pcdiy/activity6.png';
+import activity7 from '@/assets/images/pcdiy/activity7.png';
+import activity8 from '@/assets/images/pcdiy/activity8.png';
 import draggable from 'vuedraggable';
 import usePcdiyStore from '@/store/modules/pcdiy';
 import uploadImage from '@/components/upload-image/index.vue';
 import ImagesForm from '@/components/ImagesForm/index.vue';
 const diyStore = usePcdiyStore();
 const ImagesFormRef = ref();
+const showDialog1 = ref(false);
+const styleId1 = ref<any>(1);
+const showDialog2 = ref(false);
+const styleId2 = ref<any>(1);
+
+const styleList = [
+  {
+    img: activity1,
+    id: 1
+  },
+  {
+    img: activity2,
+    id: 2
+  },
+  {
+    img: activity3,
+    id: 3
+  },
+  {
+    img: activity4,
+    id: 4
+  }
+];
+const navList = [
+  {
+    img: activity5,
+    id: 1
+  },
+  {
+    img: activity5,
+    id: 2
+  },
+  {
+    img: activity6,
+    id: 3
+  }
+  // {
+  //   img: activity8,
+  //   id: 4
+  // }
+];
+
+//打开弹窗1
+const showStyle1 = () => {
+  showDialog1.value = true;
+  styleId1.value = diyStore.editComponent.styleType;
+};
+
+//选择弹窗1
+const changeTitleStyle1 = (item: any) => {
+  styleId1.value = item.id;
+};
+
+//确定弹窗1
+const confirmTitleStyle1 = () => {
+  diyStore.editComponent.styleType = styleId1.value;
+  showDialog1.value = false;
+};
+
+//打开弹窗2
+const showStyle2 = () => {
+  showDialog2.value = true;
+  styleId2.value = diyStore.editComponent.navType;
+};
+
+//选择弹窗1
+const changeTitleStyle2 = (item: any) => {
+  styleId2.value = item.id;
+};
+
+//确定弹窗1
+const confirmTitleStyle2 = () => {
+  diyStore.editComponent.navType = styleId2.value;
+  showDialog2.value = false;
+};
 
 // 打开图片类型
 const openImageType = (element: any, type: any) => {
@@ -138,22 +304,16 @@ const openImageType = (element: any, type: any) => {
   ImagesFormRef.value.onOpen(datas, type);
 };
 //图片类型返回
-const confirmCallBack = (res: any, index: any) => {
-  diyStore.editComponent.navlList[index].imgType = res.imgType;
-};
-const onAdd = () => {
-  diyStore.editComponent.navlList.push({
-    imageUrl: '',
-    title: '标题名称',
-    subtitle: '副标题名称',
-    url: '',
-    imgType: 1,
-    id: Date.now()
-  });
-};
-
-const onDel = (index: any) => {
-  diyStore.editComponent.navlList.splice(index, 1);
+const confirmCallBack = (res: any, type: any) => {
+  if (type == 1) {
+    diyStore.editComponent.titleImgType = res.imgType;
+  } else {
+    diyStore.editComponent.navlList.forEach((item: any) => {
+      if (item.id == res.id) {
+        item.imgType = res.imgType;
+      }
+    });
+  }
 };
 </script>
 
@@ -184,24 +344,24 @@ const onDel = (index: any) => {
       border-radius: 4px;
       position: relative;
       margin-top: 18px;
+    }
 
-      .images-bos {
-        flex: 1;
-        height: 98px;
-        padding: 5px 0;
-      }
+    .images-bos {
+      flex: 1;
+      height: 98px;
+      padding: 5px 0;
+    }
 
-      .images-box {
-        font-size: 13px;
-        color: #666;
-      }
+    .images-box {
+      font-size: 13px;
+      color: #666;
+    }
 
-      .circleClose {
-        position: absolute;
-        top: -9px;
-        right: -9px;
-        cursor: pointer;
-      }
+    .circleClose {
+      position: absolute;
+      top: -9px;
+      right: -9px;
+      cursor: pointer;
     }
   }
 
@@ -211,6 +371,25 @@ const onDel = (index: any) => {
     line-height: 14px;
   }
 
+  .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;
   }

+ 51 - 0
src/views/diy/pccomponents/edit/blank-edit.vue

@@ -0,0 +1,51 @@
+<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="高度" class="flex">
+            <el-input-number v-model="diyStore.editComponent.boxHeight" :min="1">
+              <template #suffix>
+                <span>px</span>
+              </template>
+            </el-input-number>
+          </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 lang="ts">
+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;
+    }
+  }
+
+  :deep(.el-form-item__label) {
+    font-weight: 400;
+  }
+
+  :deep(.file-selector) {
+    display: none;
+  }
+}
+</style>

+ 56 - 0
src/views/diy/pccomponents/edit/border-edit.vue

@@ -0,0 +1,56 @@
+<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="高度" class="flex">
+            <el-input-number v-model="diyStore.editComponent.boxHeight" :min="1">
+              <template #suffix>
+                <span>px</span>
+              </template>
+            </el-input-number>
+          </el-form-item>
+          <el-form-item label="颜色">
+            <span class="mr-[10px]">{{ diyStore.editComponent.boxColor }}</span>
+            <el-color-picker class="mr-[10px]" v-model="diyStore.editComponent.boxColor" />
+            <el-button @click="diyStore.editComponent.boxColor = '#000000'" size="small">重置</el-button>
+          </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 lang="ts">
+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;
+    }
+  }
+
+  :deep(.el-form-item__label) {
+    font-weight: 400;
+  }
+
+  :deep(.file-selector) {
+    display: none;
+  }
+}
+</style>

+ 4 - 181
src/views/diy/pccomponents/edit/discover-edit.vue

@@ -275,82 +275,7 @@
     </div>
 
     <ImagesForm ref="ImagesFormRef" @confirmCallBack="confirmCallBack"></ImagesForm>
-
-    <!-- 手动选择 -->
-    <el-dialog v-model="showDialog" title="选择商品" width="1400">
-      <div class="dialog-bos">
-        <el-input v-model="queryParams.itemName" placeholder="请输入商品名称" clearable style="width: 300px; margin-bottom: 10px">
-          <template #append>
-            <el-button :icon="Search" @click="handleQuery" />
-          </template>
-        </el-input>
-        <div class="flex">
-          <div class="tree-bos">
-            <el-tree :data="categoryOptions" :props="defaultProps" @node-click="handleNodeClick" :highlight-current="true" />
-          </div>
-          <el-table ref="multipleTableRef" v-loading="loading" :data="tableData" border @selection-change="handleSelectionChange">
-            <el-table-column type="selection" width="55" />
-            <el-table-column label="商品图片" align="center" prop="productImage" width="100">
-              <template #default="scope">
-                <image-preview :src="scope.row.productImage" :width="60" :height="60" />
-              </template>
-            </el-table-column>
-            <el-table-column label="商品信息" align="center" minWidth="250" show-overflow-tooltip>
-              <template #default="scope">
-                <div class="text-left">
-                  <div>{{ scope.row.itemName }}</div>
-                  <div class="text-gray-500" style="font-size: 12px">品牌: {{ scope.row.brandName || '-' }}</div>
-                </div>
-              </template>
-            </el-table-column>
-            <el-table-column label="SKU价格" align="center" width="180">
-              <template #default="scope">
-                <div class="text-left" style="font-size: 12px">
-                  <div>
-                    <span class="text-gray-500">市场价:</span>
-                    <span class="text-red-500">¥{{ scope.row.marketPrice || '0.00' }}</span>
-                  </div>
-                  <div>
-                    <span class="text-gray-500">会员价:</span>
-                    <span class="text-red-500">¥{{ scope.row.memberPrice || '0.00' }}</span>
-                  </div>
-                  <div>
-                    <span class="text-gray-500">最低价:</span>
-                    <span class="text-red-500">¥{{ scope.row.minSellingPrice || '0.00' }}</span>
-                  </div>
-                </div>
-              </template>
-            </el-table-column>
-            <el-table-column label="成本情况" align="center" width="150">
-              <template #default="scope">
-                <div class="text-left" style="font-size: 12px">
-                  <div>
-                    <span class="text-gray-500">采购价:</span>
-                    <span>¥{{ scope.row.purchasingPrice || '0.00' }}</span>
-                  </div>
-                  <div>
-                    <span class="text-gray-500">暂估毛利率:</span>
-                    <span>{{ scope.row.tempGrossMargin || '0.0000' }}%</span>
-                  </div>
-                </div>
-              </template>
-            </el-table-column>
-          </el-table>
-        </div>
-        <pagination v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList">
-          <template #slotDiv>
-            <div class="selected">已选择 {{ multipleSelection.length }} 个</div>
-          </template>
-        </pagination>
-      </div>
-
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="showDialog = false">取消</el-button>
-          <el-button type="primary" @click="onConfirm">确认</el-button>
-        </span>
-      </template>
-    </el-dialog>
+    <goods-dialog ref="goodsDialogRef" :categoryOptions="categoryOptions"></goods-dialog>
   </div>
 </template>
 
@@ -359,33 +284,11 @@ import draggable from 'vuedraggable';
 import usePcdiyStore from '@/store/modules/pcdiy';
 import uploadImage from '@/components/upload-image/index.vue';
 import ImagesForm from '@/components/ImagesForm/index.vue';
-import { categoryTree, listBase } from '@/api/pmsProduct/base';
-import type { TableInstance } from 'element-plus';
-import { Search } from '@element-plus/icons-vue';
-import { el } from 'element-plus/es/locale/index.mjs';
+import { categoryTree } from '@/api/pmsProduct/base';
+const goodsDialogRef = ref<any>(null);
 const diyStore = usePcdiyStore();
 const ImagesFormRef = ref();
-
-const multipleTableRef = ref<TableInstance>();
-const showDialog = ref(false);
-const loading = ref(false);
-const tableData = ref<any[]>([]);
-const multipleSelection: any = ref([]); // 选中数据
-const total = ref(0);
-const queryParams = reactive({
-  pageNum: 1,
-  pageSize: 10,
-  itemName: '',
-  topCategoryId: '',
-  mediumCategoryId: '',
-  bottomCategoryId: ''
-});
-const resultList = ref<any>([]); //单页之前被选中的数据
 const categoryOptions = ref<any>([]);
-const defaultProps = {
-  children: 'children',
-  label: 'label'
-};
 
 onMounted(() => {
   getCategoryTree();
@@ -438,31 +341,6 @@ const confirmCallBack = (res: any, type: any) => {
   });
 };
 
-/** 搜索 */
-const handleQuery = () => {
-  queryParams.pageNum = 1;
-  getList();
-};
-
-/** 获取列表 */
-const getList = async () => {
-  loading.value = true;
-  try {
-    const res = await listBase(queryParams);
-    tableData.value = res.rows || [];
-    const result = tableData.value.filter((item: any) => diyStore.editComponent.goodsIds.includes(item.id));
-    resultList.value = result;
-    nextTick(() => {
-      result.forEach((item: any) => {
-        multipleTableRef.value?.toggleRowSelection(item, true);
-      });
-    });
-    total.value = res.total || 0;
-  } finally {
-    loading.value = false;
-  }
-};
-
 /** 查询分类树 */
 const getCategoryTree = async () => {
   categoryOptions.value = [];
@@ -477,62 +355,7 @@ const getCategoryTree = async () => {
 
 //打开弹窗
 const openDialog = () => {
-  showDialog.value = true;
-  getList();
-};
-
-const handleNodeClick = (data: any) => {
-  queryParams.topCategoryId = '';
-  queryParams.mediumCategoryId = '';
-  queryParams.bottomCategoryId = '';
-  if (data.parentId == 0) {
-    queryParams.topCategoryId = data.id;
-  } else if (data.children) {
-    queryParams.mediumCategoryId = data.id;
-  } else {
-    queryParams.bottomCategoryId = data.id;
-  }
-  handleQuery();
-};
-// 监听表格单行选中
-const handleSelectionChange = (val: []) => {
-  multipleSelection.value = val;
-};
-//确定
-const onConfirm = () => {
-  const newIds = calculateNewIds(diyStore.editComponent.goodsIds, tableData.value, multipleSelection.value);
-  if (newIds.length < 5) {
-    diyStore.editComponent.goodsIds = newIds;
-    showDialog.value = false;
-  } else {
-    ElMessage.error('最多只能选择4个商品');
-  }
-};
-const calculateNewIds = (cacheIds: any, allPageItems: any, selectedItems: any) => {
-  // 1. 获取当前页所有存在的 ID 集合 (用于识别哪些旧数据属于当前页)
-  const currentPageIdSet = new Set(allPageItems.map((item) => item.id));
-  // 2. 获取最终选中项的 ID 集合
-  const selectedIdSet = new Set(selectedItems.map((item) => item.id));
-  // 3. 过滤旧的缓存 IDs
-  const retainedOldIds = cacheIds.filter((id) => {
-    // 情况 A: 该 ID 不在当前页数据中 (说明是其他页的数据,必须无条件保留)
-    if (!currentPageIdSet.has(id)) {
-      return true;
-    }
-    // 情况 B: 该 ID 在当前页数据中,且也在最终选中列表中 (说明用户保持了选中)
-    if (selectedIdSet.has(id)) {
-      return true;
-    }
-    // 情况 C: 该 ID 在当前页数据中,但不在最终选中列表中 (说明用户取消了选中,如 ID 4)
-    // 返回 false,将其剔除
-    return false;
-  });
-  // 4. 合并:保留的旧数据 + 当前页新选中的数据
-  // 使用 Set 去重,虽然逻辑上 retainedOldIds 和 selectedIdSet 不会有交集,但以防万一
-  const newIdsSet = new Set([...retainedOldIds, ...selectedIdSet]);
-  // 转回数组 (如果需要保持原有顺序或特定排序,可以在此处调整)
-  // 这里简单转为数组,通常建议按数字大小排序以便阅读,或者保持插入顺序
-  return Array.from(newIdsSet).sort((a, b) => a - b);
+  goodsDialogRef.value.onOpen();
 };
 </script>
 

+ 43 - 0
src/views/diy/pccomponents/edit/editordiy-edit.vue

@@ -0,0 +1,43 @@
+<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>
+        <Editor v-model="diyStore.editComponent.detail" :height="400" />
+      </div>
+    </div>
+    <!-- 样式 -->
+    <div class="style-wrap" v-show="diyStore.editTab == 'style'">
+      <!-- 组件样式 -->
+      <slot name="style"></slot>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+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;
+    }
+  }
+
+  :deep(.el-form-item__label) {
+    font-weight: 400;
+  }
+
+  :deep(.file-selector) {
+    display: none;
+  }
+}
+</style>

+ 9 - 179
src/views/diy/pccomponents/edit/floor-edit.vue

@@ -217,82 +217,8 @@
       </template>
     </el-dialog>
 
-    <!-- 手动选择 -->
-    <el-dialog v-model="showDialog" title="选择商品" width="1400">
-      <div class="dialog-bos">
-        <el-input v-model="queryParams.itemName" placeholder="请输入商品名称" clearable style="width: 300px; margin-bottom: 10px">
-          <template #append>
-            <el-button :icon="Search" @click="handleQuery" />
-          </template>
-        </el-input>
-        <div class="flex">
-          <div class="tree-bos">
-            <el-tree :data="categoryOptions" :props="defaultProps" @node-click="handleNodeClick" :highlight-current="true" />
-          </div>
-          <el-table ref="multipleTableRef" v-loading="loading" :data="tableData" border @selection-change="handleSelectionChange">
-            <el-table-column type="selection" width="55" />
-            <el-table-column label="商品图片" align="center" prop="productImage" width="100">
-              <template #default="scope">
-                <image-preview :src="scope.row.productImage" :width="60" :height="60" />
-              </template>
-            </el-table-column>
-            <el-table-column label="商品信息" align="center" minWidth="250" show-overflow-tooltip>
-              <template #default="scope">
-                <div class="text-left">
-                  <div>{{ scope.row.itemName }}</div>
-                  <div class="text-gray-500" style="font-size: 12px">品牌: {{ scope.row.brandName || '-' }}</div>
-                </div>
-              </template>
-            </el-table-column>
-            <el-table-column label="SKU价格" align="center" width="180">
-              <template #default="scope">
-                <div class="text-left" style="font-size: 12px">
-                  <div>
-                    <span class="text-gray-500">市场价:</span>
-                    <span class="text-red-500">¥{{ scope.row.marketPrice || '0.00' }}</span>
-                  </div>
-                  <div>
-                    <span class="text-gray-500">会员价:</span>
-                    <span class="text-red-500">¥{{ scope.row.memberPrice || '0.00' }}</span>
-                  </div>
-                  <div>
-                    <span class="text-gray-500">最低价:</span>
-                    <span class="text-red-500">¥{{ scope.row.minSellingPrice || '0.00' }}</span>
-                  </div>
-                </div>
-              </template>
-            </el-table-column>
-            <el-table-column label="成本情况" align="center" width="150">
-              <template #default="scope">
-                <div class="text-left" style="font-size: 12px">
-                  <div>
-                    <span class="text-gray-500">采购价:</span>
-                    <span>¥{{ scope.row.purchasingPrice || '0.00' }}</span>
-                  </div>
-                  <div>
-                    <span class="text-gray-500">暂估毛利率:</span>
-                    <span>{{ scope.row.tempGrossMargin || '0.0000' }}%</span>
-                  </div>
-                </div>
-              </template>
-            </el-table-column>
-          </el-table>
-        </div>
-        <pagination v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList">
-          <template #slotDiv>
-            <div class="selected">已选择 {{ multipleSelection.length }} 个</div>
-          </template>
-        </pagination>
-      </div>
-
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="showDialog = false">取消</el-button>
-          <el-button type="primary" @click="onConfirm">确认</el-button>
-        </span>
-      </template>
-    </el-dialog>
     <ImagesForm ref="ImagesFormRef" @confirmCallBack="confirmCallBack"></ImagesForm>
+    <goods-dialog ref="goodsDialogRef" :categoryOptions="categoryOptions"></goods-dialog>
   </div>
 </template>
 
@@ -301,9 +227,7 @@ import usePcdiyStore from '@/store/modules/pcdiy';
 import floor1 from '@/assets/images/pcdiy/floor1.png';
 import floor2 from '@/assets/images/pcdiy/floor2.png';
 import ImagesForm from '@/components/ImagesForm/index.vue';
-import type { TableInstance } from 'element-plus';
-import { Search } from '@element-plus/icons-vue';
-import { categoryTree, listBase } from '@/api/pmsProduct/base';
+import { categoryTree } from '@/api/pmsProduct/base';
 import draggable from 'vuedraggable';
 const styleList = [
   {
@@ -319,30 +243,8 @@ const diyStore = usePcdiyStore();
 const ImagesFormRef = ref();
 const styleDialog = ref(false);
 const styleId = ref<any>(1);
-
-const multipleTableRef = ref<TableInstance>();
-const showDialog = ref(false);
-const loading = ref(false);
-const Brandloading = ref(false);
-const BrandList = ref<any>([]);
-const tableData = ref<any[]>([]);
-const multipleSelection: any = ref([]); // 选中数据
-const total = ref(0);
-const queryParams = reactive({
-  pageNum: 1,
-  pageSize: 10,
-  itemName: '',
-  topCategoryId: '',
-  mediumCategoryId: '',
-  bottomCategoryId: ''
-});
-const resultList = ref<any>([]); //单页之前被选中的数据
 const categoryOptions = ref<any>([]);
-const categoryOptions1 = ref<any>([]);
-const defaultProps = {
-  children: 'children',
-  label: 'label'
-};
+const goodsDialogRef = ref<any>(null);
 
 onMounted(() => {
   getCategoryTree();
@@ -384,35 +286,14 @@ const confirmCallBack = (res: any, type: any) => {
   }
 };
 
-/** 搜索 */
-const handleQuery = () => {
-  queryParams.pageNum = 1;
-  getList();
-};
-
-/** 获取列表 */
-const getList = async () => {
-  loading.value = true;
-  try {
-    const res = await listBase(queryParams);
-    tableData.value = res.rows || [];
-    const result = tableData.value.filter((item: any) => diyStore.editComponent.goodsIds.includes(item.id));
-    resultList.value = result;
-    nextTick(() => {
-      result.forEach((item: any) => {
-        multipleTableRef.value?.toggleRowSelection(item, true);
-      });
-    });
-    total.value = res.total || 0;
-  } finally {
-    loading.value = false;
-  }
-};
-
 /** 查询分类树 */
 const getCategoryTree = async () => {
   categoryOptions.value = [];
-  const res = await categoryTree();
+  const res = await categoryTree({
+    platform: diyStore.type == 1 ? 0 : diyStore.type == 2 ? 1 : diyStore.type == 3 ? 2 : diyStore.type == 4 ? 0 : 0,
+    pageNum: 1,
+    pageSize: 9999
+  });
   const list = res.data || [];
   categoryOptions.value = [...list];
   categoryOptions.value.unshift({
@@ -423,58 +304,7 @@ const getCategoryTree = async () => {
 
 //打开弹窗
 const openDialog = () => {
-  showDialog.value = true;
-  getList();
-};
-
-const handleNodeClick = (data: any) => {
-  queryParams.topCategoryId = '';
-  queryParams.mediumCategoryId = '';
-  queryParams.bottomCategoryId = '';
-  if (data.parentId == 0) {
-    queryParams.topCategoryId = data.id;
-  } else if (data.children) {
-    queryParams.mediumCategoryId = data.id;
-  } else {
-    queryParams.bottomCategoryId = data.id;
-  }
-  handleQuery();
-};
-// 监听表格单行选中
-const handleSelectionChange = (val: []) => {
-  multipleSelection.value = val;
-};
-//确定
-const onConfirm = () => {
-  const newIds = calculateNewIds(diyStore.editComponent.goodsIds, tableData.value, multipleSelection.value);
-  diyStore.editComponent.goodsIds = newIds;
-  showDialog.value = false;
-};
-const calculateNewIds = (cacheIds: any, allPageItems: any, selectedItems: any) => {
-  // 1. 获取当前页所有存在的 ID 集合 (用于识别哪些旧数据属于当前页)
-  const currentPageIdSet = new Set(allPageItems.map((item) => item.id));
-  // 2. 获取最终选中项的 ID 集合
-  const selectedIdSet = new Set(selectedItems.map((item) => item.id));
-  // 3. 过滤旧的缓存 IDs
-  const retainedOldIds = cacheIds.filter((id) => {
-    // 情况 A: 该 ID 不在当前页数据中 (说明是其他页的数据,必须无条件保留)
-    if (!currentPageIdSet.has(id)) {
-      return true;
-    }
-    // 情况 B: 该 ID 在当前页数据中,且也在最终选中列表中 (说明用户保持了选中)
-    if (selectedIdSet.has(id)) {
-      return true;
-    }
-    // 情况 C: 该 ID 在当前页数据中,但不在最终选中列表中 (说明用户取消了选中,如 ID 4)
-    // 返回 false,将其剔除
-    return false;
-  });
-  // 4. 合并:保留的旧数据 + 当前页新选中的数据
-  // 使用 Set 去重,虽然逻辑上 retainedOldIds 和 selectedIdSet 不会有交集,但以防万一
-  const newIdsSet = new Set([...retainedOldIds, ...selectedIdSet]);
-  // 转回数组 (如果需要保持原有顺序或特定排序,可以在此处调整)
-  // 这里简单转为数组,通常建议按数字大小排序以便阅读,或者保持插入顺序
-  return Array.from(newIdsSet).sort((a, b) => a - b);
+  goodsDialogRef.value.onOpen();
 };
 
 const onAdd = () => {

+ 18 - 227
src/views/diy/pccomponents/edit/goods-edit.vue

@@ -78,20 +78,19 @@
             </div>
           </el-form-item>
           <el-form-item label="商品分类" v-if="diyStore.editComponent.goodsType == 2">
-            <el-tree-select
-              v-model="diyStore.editComponent.goodsClassify"
-              :data="categoryOptions1"
-              :props="treeProps"
-              value-key="id"
-              placeholder="请选择商品分类"
+            <el-cascader
+              v-model="diyStore.editComponent.categoryIds"
+              :options="categoryOptions1"
+              :props="cascaderProps"
+              placeholder="全部分类"
               clearable
-              check-strictly
-              @change="goodsClassifyChange"
+              collapse-tags
             />
           </el-form-item>
           <el-form-item label="品牌" v-if="diyStore.editComponent.goodsType == 3">
             <el-select
-              v-model="diyStore.editComponent.goodsBrand"
+              v-model="diyStore.editComponent.brandIds"
+              multiple
               filterable
               remote
               reserve-keyword
@@ -180,155 +179,35 @@
       <!-- 组件样式 -->
       <slot name="style"></slot>
     </div>
-    <!-- 手动选择 -->
-    <el-dialog v-model="showDialog" title="选择商品" width="1400">
-      <div class="dialog-bos">
-        <el-input v-model="queryParams.itemName" placeholder="请输入商品名称" clearable style="width: 300px; margin-bottom: 10px">
-          <template #append>
-            <el-button :icon="Search" @click="handleQuery" />
-          </template>
-        </el-input>
-        <div class="flex">
-          <div class="tree-bos">
-            <el-tree :data="categoryOptions" :props="defaultProps" @node-click="handleNodeClick" :highlight-current="true" />
-          </div>
-          <el-table ref="multipleTableRef" v-loading="loading" :data="tableData" border @selection-change="handleSelectionChange">
-            <el-table-column type="selection" width="55" />
-            <el-table-column label="商品图片" align="center" prop="productImage" width="100">
-              <template #default="scope">
-                <image-preview :src="scope.row.productImage" :width="60" :height="60" />
-              </template>
-            </el-table-column>
-            <el-table-column label="商品信息" align="center" minWidth="250" show-overflow-tooltip>
-              <template #default="scope">
-                <div class="text-left">
-                  <div>{{ scope.row.itemName }}</div>
-                  <div class="text-gray-500" style="font-size: 12px">品牌: {{ scope.row.brandName || '-' }}</div>
-                </div>
-              </template>
-            </el-table-column>
-            <el-table-column label="SKU价格" align="center" width="180">
-              <template #default="scope">
-                <div class="text-left" style="font-size: 12px">
-                  <div>
-                    <span class="text-gray-500">市场价:</span>
-                    <span class="text-red-500">¥{{ scope.row.marketPrice || '0.00' }}</span>
-                  </div>
-                  <div>
-                    <span class="text-gray-500">会员价:</span>
-                    <span class="text-red-500">¥{{ scope.row.memberPrice || '0.00' }}</span>
-                  </div>
-                  <div>
-                    <span class="text-gray-500">最低价:</span>
-                    <span class="text-red-500">¥{{ scope.row.minSellingPrice || '0.00' }}</span>
-                  </div>
-                </div>
-              </template>
-            </el-table-column>
-            <el-table-column label="成本情况" align="center" width="150">
-              <template #default="scope">
-                <div class="text-left" style="font-size: 12px">
-                  <div>
-                    <span class="text-gray-500">采购价:</span>
-                    <span>¥{{ scope.row.purchasingPrice || '0.00' }}</span>
-                  </div>
-                  <div>
-                    <span class="text-gray-500">暂估毛利率:</span>
-                    <span>{{ scope.row.tempGrossMargin || '0.0000' }}%</span>
-                  </div>
-                </div>
-              </template>
-            </el-table-column>
-          </el-table>
-        </div>
-        <pagination v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList">
-          <template #slotDiv>
-            <div class="selected">已选择 {{ multipleSelection.length }} 个</div>
-          </template>
-        </pagination>
-      </div>
-
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="showDialog = false">取消</el-button>
-          <el-button type="primary" @click="onConfirm">确认</el-button>
-        </span>
-      </template>
-    </el-dialog>
+    <goods-dialog ref="goodsDialogRef" :categoryOptions="categoryOptions"></goods-dialog>
   </div>
 </template>
 
 <script lang="ts" setup>
-import { categoryTree, listBase } from '@/api/pmsProduct/base';
+import { categoryTree } from '@/api/pmsProduct/base';
 import usePcdiyStore from '@/store/modules/pcdiy';
-import { Search } from '@element-plus/icons-vue';
-import type { TableInstance } from 'element-plus';
 import { listBrand } from '@/api/product/brand';
 const diyStore = usePcdiyStore();
-
-const multipleTableRef = ref<TableInstance>();
-const showDialog = ref(false);
-const loading = ref(false);
 const Brandloading = ref(false);
 const BrandList = ref<any>([]);
-const tableData = ref<any[]>([]);
-const multipleSelection: any = ref([]); // 选中数据
-const total = ref(0);
-const queryParams = reactive({
-  pageNum: 1,
-  pageSize: 10,
-  itemName: '',
-  topCategoryId: '',
-  mediumCategoryId: '',
-  bottomCategoryId: ''
-});
-const resultList = ref<any>([]); //单页之前被选中的数据
 const categoryOptions = ref<any>([]);
 const categoryOptions1 = ref<any>([]);
-const defaultProps = {
-  children: 'children',
-  label: 'label'
-};
-const treeProps = {
-  value: 'id',
-  label: 'label',
-  children: 'children'
-};
+const cascaderProps = { multiple: true, value: 'id', label: 'label', children: 'children' };
+const goodsDialogRef = ref<any>(null);
 
 onMounted(() => {
   getCategoryTree();
 });
 
-/** 搜索 */
-const handleQuery = () => {
-  queryParams.pageNum = 1;
-  getList();
-};
-
-/** 获取列表 */
-const getList = async () => {
-  loading.value = true;
-  try {
-    const res = await listBase(queryParams);
-    tableData.value = res.rows || [];
-    const result = tableData.value.filter((item: any) => diyStore.editComponent.goodsIds.includes(item.id));
-    resultList.value = result;
-    nextTick(() => {
-      result.forEach((item: any) => {
-        multipleTableRef.value?.toggleRowSelection(item, true);
-      });
-    });
-    total.value = res.total || 0;
-  } finally {
-    loading.value = false;
-  }
-};
-
 /** 查询分类树 */
 const getCategoryTree = async () => {
   categoryOptions.value = [];
   categoryOptions1.value = [];
-  const res = await categoryTree();
+  const res = await categoryTree({
+    platform: diyStore.type == 1 ? 0 : diyStore.type == 2 ? 1 : diyStore.type == 3 ? 2 : diyStore.type == 4 ? 0 : 0,
+    pageNum: 1,
+    pageSize: 9999
+  });
   const list = res.data || [];
   categoryOptions.value = [...list];
   categoryOptions1.value = [...list];
@@ -340,88 +219,7 @@ const getCategoryTree = async () => {
 
 //打开弹窗
 const openDialog = () => {
-  showDialog.value = true;
-  getList();
-};
-
-const handleNodeClick = (data: any) => {
-  queryParams.topCategoryId = '';
-  queryParams.mediumCategoryId = '';
-  queryParams.bottomCategoryId = '';
-  if (data.parentId == 0) {
-    queryParams.topCategoryId = data.id;
-  } else if (data.children) {
-    queryParams.mediumCategoryId = data.id;
-  } else {
-    queryParams.bottomCategoryId = data.id;
-  }
-  handleQuery();
-};
-// 监听表格单行选中
-const handleSelectionChange = (val: []) => {
-  multipleSelection.value = val;
-};
-//确定
-const onConfirm = () => {
-  const newIds = calculateNewIds(diyStore.editComponent.goodsIds, tableData.value, multipleSelection.value);
-  diyStore.editComponent.goodsIds = newIds;
-  showDialog.value = false;
-};
-const calculateNewIds = (cacheIds: any, allPageItems: any, selectedItems: any) => {
-  // 1. 获取当前页所有存在的 ID 集合 (用于识别哪些旧数据属于当前页)
-  const currentPageIdSet = new Set(allPageItems.map((item) => item.id));
-  // 2. 获取最终选中项的 ID 集合
-  const selectedIdSet = new Set(selectedItems.map((item) => item.id));
-  // 3. 过滤旧的缓存 IDs
-  const retainedOldIds = cacheIds.filter((id) => {
-    // 情况 A: 该 ID 不在当前页数据中 (说明是其他页的数据,必须无条件保留)
-    if (!currentPageIdSet.has(id)) {
-      return true;
-    }
-    // 情况 B: 该 ID 在当前页数据中,且也在最终选中列表中 (说明用户保持了选中)
-    if (selectedIdSet.has(id)) {
-      return true;
-    }
-    // 情况 C: 该 ID 在当前页数据中,但不在最终选中列表中 (说明用户取消了选中,如 ID 4)
-    // 返回 false,将其剔除
-    return false;
-  });
-  // 4. 合并:保留的旧数据 + 当前页新选中的数据
-  // 使用 Set 去重,虽然逻辑上 retainedOldIds 和 selectedIdSet 不会有交集,但以防万一
-  const newIdsSet = new Set([...retainedOldIds, ...selectedIdSet]);
-  // 转回数组 (如果需要保持原有顺序或特定排序,可以在此处调整)
-  // 这里简单转为数组,通常建议按数字大小排序以便阅读,或者保持插入顺序
-  return Array.from(newIdsSet).sort((a, b) => a - b);
-};
-
-//选择商品分类
-const goodsClassifyChange = (res: any) => {
-  const foundNode = findNodeByKey(categoryOptions1.value, res);
-  diyStore.editComponent.goodsClassify = res;
-  diyStore.editComponent.topCategoryId = '';
-  diyStore.editComponent.mediumCategoryId = '';
-  diyStore.editComponent.bottomCategoryId = '';
-  if (foundNode.parentId == 0) {
-    diyStore.editComponent.topCategoryId = foundNode.id;
-  } else if (foundNode.children) {
-    diyStore.editComponent.mediumCategoryId = foundNode.id;
-  } else {
-    diyStore.editComponent.bottomCategoryId = foundNode.id;
-  }
-};
-
-// 递归查找节点的辅助函数
-const findNodeByKey = (nodes: any, key: any) => {
-  for (const node of nodes) {
-    if (node.id === key) {
-      return node;
-    }
-    if (node.children) {
-      const found = findNodeByKey(node.children, key);
-      if (found) return found;
-    }
-  }
-  return null;
+  goodsDialogRef.value.onOpen();
 };
 
 //品牌输入
@@ -434,13 +232,6 @@ const remoteMethod = (query: string) => {
         BrandList.value = res.rows || [];
       }
     });
-    console.log(query);
-    // setTimeout(() => {
-    //   Brandloading.value = false
-    //   options.value = list.value.filter((item) => {
-    //     return item.label.toLowerCase().includes(query.toLowerCase())
-    //   })
-    // }, 3000)
   } else {
     BrandList.value = [];
   }

+ 15 - 185
src/views/diy/pccomponents/edit/goodsList-edit.vue

@@ -98,15 +98,13 @@
                   </div>
                 </el-form-item>
                 <el-form-item label="商品分类" v-if="element.goodsType == 2">
-                  <el-tree-select
-                    v-model="element.goodsClassify"
-                    :data="categoryOptions1"
-                    :props="treeProps"
-                    value-key="id"
-                    placeholder="请选择商品分类"
+                  <el-cascader
+                    v-model="element.categoryIds"
+                    :options="categoryOptions1"
+                    :props="cascaderProps"
+                    placeholder="全部分类"
                     clearable
-                    check-strictly
-                    @change="(res: any) => goodsClassifyChange(res, element)"
+                    collapse-tags
                   />
                 </el-form-item>
               </div>
@@ -200,81 +198,7 @@
       <!-- 组件样式 -->
       <slot name="style"></slot>
     </div>
-    <!-- 手动选择 -->
-    <el-dialog v-model="showDialog" title="选择商品" width="1400">
-      <div class="dialog-bos">
-        <el-input v-model="queryParams.itemName" placeholder="请输入商品名称" clearable style="width: 300px; margin-bottom: 10px">
-          <template #append>
-            <el-button :icon="Search" @click="handleQuery" />
-          </template>
-        </el-input>
-        <div class="flex">
-          <div class="tree-bos">
-            <el-tree :data="categoryOptions" :props="defaultProps" @node-click="handleNodeClick" :highlight-current="true" />
-          </div>
-          <el-table ref="multipleTableRef" v-loading="loading" :data="tableData" border @selection-change="handleSelectionChange">
-            <el-table-column type="selection" width="55" />
-            <el-table-column label="商品图片" align="center" prop="productImage" width="100">
-              <template #default="scope">
-                <image-preview :src="scope.row.productImage" :width="60" :height="60" />
-              </template>
-            </el-table-column>
-            <el-table-column label="商品信息" align="center" minWidth="250" show-overflow-tooltip>
-              <template #default="scope">
-                <div class="text-left">
-                  <div>{{ scope.row.itemName }}</div>
-                  <div class="text-gray-500" style="font-size: 12px">品牌: {{ scope.row.brandName || '-' }}</div>
-                </div>
-              </template>
-            </el-table-column>
-            <el-table-column label="SKU价格" align="center" width="180">
-              <template #default="scope">
-                <div class="text-left" style="font-size: 12px">
-                  <div>
-                    <span class="text-gray-500">市场价:</span>
-                    <span class="text-red-500">¥{{ scope.row.marketPrice || '0.00' }}</span>
-                  </div>
-                  <div>
-                    <span class="text-gray-500">会员价:</span>
-                    <span class="text-red-500">¥{{ scope.row.memberPrice || '0.00' }}</span>
-                  </div>
-                  <div>
-                    <span class="text-gray-500">最低价:</span>
-                    <span class="text-red-500">¥{{ scope.row.minSellingPrice || '0.00' }}</span>
-                  </div>
-                </div>
-              </template>
-            </el-table-column>
-            <el-table-column label="成本情况" align="center" width="150">
-              <template #default="scope">
-                <div class="text-left" style="font-size: 12px">
-                  <div>
-                    <span class="text-gray-500">采购价:</span>
-                    <span>¥{{ scope.row.purchasingPrice || '0.00' }}</span>
-                  </div>
-                  <div>
-                    <span class="text-gray-500">暂估毛利率:</span>
-                    <span>{{ scope.row.tempGrossMargin || '0.0000' }}%</span>
-                  </div>
-                </div>
-              </template>
-            </el-table-column>
-          </el-table>
-        </div>
-        <pagination v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList">
-          <template #slotDiv>
-            <div class="selected">已选择 {{ multipleSelection.length }} 个</div>
-          </template>
-        </pagination>
-      </div>
-
-      <template #footer>
-        <span class="dialog-footer">
-          <el-button @click="showDialog = false">取消</el-button>
-          <el-button type="primary" @click="onConfirm">确认</el-button>
-        </span>
-      </template>
-    </el-dialog>
+    <goods-dialog ref="goodsDialogRef" :categoryOptions="categoryOptions" :navIndex="navIndex"></goods-dialog>
   </div>
 </template>
 
@@ -282,16 +206,14 @@
 import draggable from 'vuedraggable';
 import { categoryTree, listBase } from '@/api/pmsProduct/base';
 import usePcdiyStore from '@/store/modules/pcdiy';
-import { Search } from '@element-plus/icons-vue';
 import type { TableInstance } from 'element-plus';
 const diyStore = usePcdiyStore();
-
+const goodsDialogRef = ref<any>(null);
 const multipleTableRef = ref<TableInstance>();
-const showDialog = ref(false);
 const loading = ref(false);
 const tableData = ref<any[]>([]);
-const multipleSelection: any = ref([]); // 选中数据
 const total = ref(0);
+const cascaderProps = { multiple: true, value: 'id', label: 'label', children: 'children' };
 const queryParams = reactive({
   pageNum: 1,
   pageSize: 10,
@@ -304,26 +226,11 @@ const resultList = ref<any>([]); //单页之前被选中的数据
 const categoryOptions = ref<any>([]);
 const categoryOptions1 = ref<any>([]);
 const navIndex = ref<any>(0);
-const defaultProps = {
-  children: 'children',
-  label: 'label'
-};
-const treeProps = {
-  value: 'id',
-  label: 'label',
-  children: 'children'
-};
 
 onMounted(() => {
   getCategoryTree();
 });
 
-/** 搜索 */
-const handleQuery = () => {
-  queryParams.pageNum = 1;
-  getList();
-};
-
 /** 获取列表 */
 const getList = async () => {
   loading.value = true;
@@ -347,7 +254,11 @@ const getList = async () => {
 const getCategoryTree = async () => {
   categoryOptions.value = [];
   categoryOptions1.value = [];
-  const res = await categoryTree();
+  const res = await categoryTree({
+    platform: diyStore.type == 1 ? 0 : diyStore.type == 2 ? 1 : diyStore.type == 3 ? 2 : diyStore.type == 4 ? 0 : 0,
+    pageNum: 1,
+    pageSize: 9999
+  });
   const list = res.data || [];
   categoryOptions.value = [...list];
   categoryOptions1.value = [...list];
@@ -360,88 +271,7 @@ const getCategoryTree = async () => {
 //打开弹窗
 const openDialog = (res: any) => {
   navIndex.value = res;
-  showDialog.value = true;
-  getList();
-};
-
-const handleNodeClick = (data: any) => {
-  queryParams.topCategoryId = '';
-  queryParams.mediumCategoryId = '';
-  queryParams.bottomCategoryId = '';
-  if (data.parentId == 0) {
-    queryParams.topCategoryId = data.id;
-  } else if (data.children) {
-    queryParams.mediumCategoryId = data.id;
-  } else {
-    queryParams.bottomCategoryId = data.id;
-  }
-  handleQuery();
-};
-// 监听表格单行选中
-const handleSelectionChange = (val: []) => {
-  multipleSelection.value = val;
-};
-//确定
-const onConfirm = () => {
-  const newIds = calculateNewIds(diyStore.editComponent.tabList[navIndex.value].goodsIds, tableData.value, multipleSelection.value);
-  diyStore.editComponent.tabList[navIndex.value].goodsIds = newIds;
-  showDialog.value = false;
-};
-const calculateNewIds = (cacheIds: any, allPageItems: any, selectedItems: any) => {
-  // 1. 获取当前页所有存在的 ID 集合 (用于识别哪些旧数据属于当前页)
-  const currentPageIdSet = new Set(allPageItems.map((item) => item.id));
-  // 2. 获取最终选中项的 ID 集合
-  const selectedIdSet = new Set(selectedItems.map((item) => item.id));
-  // 3. 过滤旧的缓存 IDs
-  const retainedOldIds = cacheIds.filter((id) => {
-    // 情况 A: 该 ID 不在当前页数据中 (说明是其他页的数据,必须无条件保留)
-    if (!currentPageIdSet.has(id)) {
-      return true;
-    }
-    // 情况 B: 该 ID 在当前页数据中,且也在最终选中列表中 (说明用户保持了选中)
-    if (selectedIdSet.has(id)) {
-      return true;
-    }
-    // 情况 C: 该 ID 在当前页数据中,但不在最终选中列表中 (说明用户取消了选中,如 ID 4)
-    // 返回 false,将其剔除
-    return false;
-  });
-  // 4. 合并:保留的旧数据 + 当前页新选中的数据
-  // 使用 Set 去重,虽然逻辑上 retainedOldIds 和 selectedIdSet 不会有交集,但以防万一
-  const newIdsSet = new Set([...retainedOldIds, ...selectedIdSet]);
-  // 转回数组 (如果需要保持原有顺序或特定排序,可以在此处调整)
-  // 这里简单转为数组,通常建议按数字大小排序以便阅读,或者保持插入顺序
-  return Array.from(newIdsSet).sort((a, b) => a - b);
-};
-
-//选择商品分类
-const goodsClassifyChange = (res: any, element: any) => {
-  const foundNode = findNodeByKey(categoryOptions1.value, res);
-  element.goodsClassify = res;
-  element.topCategoryId = '';
-  element.mediumCategoryId = '';
-  element.bottomCategoryId = '';
-  if (foundNode.parentId == 0) {
-    element.topCategoryId = foundNode.id;
-  } else if (foundNode.children) {
-    element.mediumCategoryId = foundNode.id;
-  } else {
-    element.bottomCategoryId = foundNode.id;
-  }
-};
-
-// 递归查找节点的辅助函数
-const findNodeByKey = (nodes: any, key: any) => {
-  for (const node of nodes) {
-    if (node.id === key) {
-      return node;
-    }
-    if (node.children) {
-      const found = findNodeByKey(node.children, key);
-      if (found) return found;
-    }
-  }
-  return null;
+  goodsDialogRef.value.onOpen();
 };
 
 const onAdd = () => {

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

@@ -46,7 +46,8 @@ export const componentImportConfigs: any = [
     name: '活动魔方',
     icon: 'iconfont iconmofangpc',
     enabled: true,
-    id: 5
+    id: 5,
+    ...createComponentMap('activity')
   },
   {
     name: '轮播图',
@@ -110,6 +111,27 @@ export const componentImportConfigs: any = [
     enabled: true,
     id: 14,
     ...createComponentMap('hot')
+  },
+  {
+    name: '富文本',
+    icon: 'iconfont iconfuwenbenpc',
+    enabled: true,
+    id: 15,
+    ...createComponentMap('editordiy')
+  },
+  {
+    name: '辅助空白',
+    icon: 'iconfont iconfuzhukongbaipc',
+    enabled: true,
+    id: 16,
+    ...createComponentMap('blank')
+  },
+  {
+    name: '辅助线',
+    icon: 'iconfont iconfuzhuxianpc',
+    enabled: true,
+    id: 17,
+    ...createComponentMap('border')
   }
 ];
 
@@ -118,8 +140,6 @@ export const componentImportConfigs: any = [
  * @returns 组件配置列表
  */
 export async function loadDiyComponents(): Promise<any[]> {
-  const components: any[] = [];
-
   const loadPromises = componentImportConfigs
     .filter((config: any) => config.enabled !== false)
     .map(async (config: any) => {

+ 275 - 89
src/views/diy/pccomponents/pages/activity.vue

@@ -1,33 +1,74 @@
 <template>
-  <div class="pcPages">
-    <div class="carousel-bos" :style="warpCss" v-if="componentData.styleType == 3">
-      <el-carousel :height="270 * componentData.count + (componentData.count == 2 ? 10 : 0) + 'px'" :autoplay="false" arrow="always">
-        <el-carousel-item v-for="(item1, index1) in dataList" :key="index1" class="w100% h100%">
-          <div class="carousel-list">
-            <div v-for="(item, index) in item1" :key="index" class="data-list flex-column-between" :style="boxCss">
-              <el-image
-                class="img"
-                :src="item.imageUrl ? item.imageUrl : figure"
-                :fit="item.imgType == 1 ? 'fill' : item.imgType == 2 ? 'contain' : 'cover'"
-                :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
-              />
-              <div :style="titleCss" class="title">{{ item.title || '' }}</div>
-              <div :style="subtitleCss" class="mt-[2px] mb-[12px] subtitle ellipsis">{{ item.subtitle || '' }}</div>
-            </div>
-          </div>
-        </el-carousel-item>
-      </el-carousel>
-    </div>
-    <div v-else :style="warpCss" class="data-bos">
-      <div v-for="(item, index) in componentData.navlList" :key="index" class="data-list flex-column-between" :style="boxCss">
+  <div class="pcPages" :style="warpCss">
+    <div :style="boxCss">
+      <div class="head-bos flex-row-start" :style="radiusCss">
         <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' } : {}"
+          v-if="componentData.styleType == 4"
+          class="head-img"
+          :src="componentData.titleImg ? componentData.titleImg : figure"
+          :fit="componentData.titleImg ? (componentData.titleImgType == 1 ? 'fill' : componentData.titleImgType == 2 ? 'contain' : 'cover') : 'cover'"
         />
-        <div :style="titleCss" class="title">{{ item.title || '' }}</div>
-        <div :style="subtitleCss" class="mt-[2px] mb-[12px] subtitle ellipsis">{{ item.subtitle || '' }}</div>
+        <div v-else class="head-title" :style="{ color: componentData.titleColor }">{{ componentData.title }}</div>
+        <div
+          class="head-subtitle"
+          :style="subtitleCss"
+          :class="
+            componentData.styleType == 1
+              ? 'head-subtitle1'
+              : componentData.styleType == 2
+                ? 'head-subtitle2'
+                : componentData.styleType == 3
+                  ? 'head-subtitle3'
+                  : componentData.styleType == 4
+                    ? 'head-subtitle4'
+                    : ''
+          "
+        >
+          <span>{{ componentData.subtitle }}</span>
+          <img v-if="componentData.styleType == 3" class="head-image" src="@/assets/images/pcdiy/activity3-1.png" alt="" />
+        </div>
+      </div>
+      <div
+        class="trade-bos"
+        :class="
+          componentData.navType == 1 ? 'trade-bos1' : componentData.navType == 2 ? 'trade-bos2' : componentData.navType == 3 ? 'trade-bos2' : ''
+        "
+      >
+        <div
+          v-for="(item, index) in componentData.navlList"
+          :key="index"
+          class="trade-list flex-row-between hover-color"
+          :style="[radiusCss, backgroundCss(item)]"
+        >
+          <div class="trade-box flex-column-between">
+            <div class="trade-title ellipsis zi-hover" :style="{ fontWeight: componentData.navWeight }">{{ item.title || '' }}</div>
+            <div class="trade-info">
+              {{ item.subtitle || '' }}
+            </div>
+            <div class="flex" v-if="componentData.navType == 3">
+              <div class="trade-look3 flex-row-start" :style="btnCss(item)">
+                <div :style="componentData.navBtnType == 2 ? 'font-style: italic' : ''">{{ item.btnText }}</div>
+                <div class="bnt-box flex-row-center">
+                  <el-icon :color="item.btncolor1"><ArrowRight /></el-icon>
+                </div>
+              </div>
+            </div>
+            <div class="flex" v-else>
+              <div class="trade-look flex-row-start" :style="btnCss(item)">
+                <img class="trade-image" src="@/assets/images/pcdiy/activity3-1.png" alt="" />
+                <div :style="componentData.navBtnType == 2 ? 'font-style: italic' : ''">{{ item.btnText }}</div>
+                <div class="bnt-box flex-row-center">
+                  <el-icon :color="item.btncolor1"><ArrowRight /></el-icon>
+                </div>
+              </div>
+            </div>
+          </div>
+          <el-image
+            class="trade-img"
+            :src="item.imageUrl ? item.imageUrl : figure"
+            :fit="item.imageUrl ? (item.imgType == 1 ? 'fill' : item.imgType == 2 ? 'contain' : 'cover') : 'cover'"
+          />
+        </div>
       </div>
     </div>
   </div>
@@ -38,21 +79,11 @@ import usePcdiyStore from '@/store/modules/pcdiy';
 import figure from '@/assets/images/figure.png';
 const diyStore = usePcdiyStore();
 const props = defineProps<{
-    index: number; // 确保声明 index 为可选属性
-    row?: any;
+  index: number; // 确保声明 index 为可选属性
+  row?: any;
 }>();
 const componentData = props.row ? props.row : diyStore.componentList[props.index];
 
-const dataList = computed(() => {
-  const chunkSize = componentData.number * componentData.count;
-  const result = [];
-  for (let i = 0; i < componentData.navlList.length; i += chunkSize) {
-    const chunk = componentData.navlList.slice(i, i + chunkSize);
-    result.push(chunk);
-  }
-  return result;
-});
-
 const warpCss = computed(() => {
   let style = '';
   style += 'position:relative;';
@@ -75,6 +106,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;
 });
 
@@ -94,79 +134,225 @@ const boxCss = computed(() => {
   return style;
 });
 
-// 标题样式
-const titleCss = computed(() => {
+// 标题样式
+const subtitleCss = computed(() => {
   let style = '';
-  if (componentData.titleColor) style += 'color:' + componentData.titleColor + ';';
-  if (componentData.titleSize) style += 'font-size:' + componentData.titleSize + 'px;';
-  if (componentData.titleWeight) style += 'font-weight:' + componentData.titleWeight + ';';
+  if (componentData.subtitleColor) style += 'color:' + componentData.subtitleColor + ';';
+  //背景颜色
+  if (componentData.subtitleColor1 && componentData.styleType != 4) {
+    if (componentData.subtitleColor1 && componentData.subtitleColor2)
+      style += `background:linear-gradient(to right,${componentData.subtitleColor1},${componentData.subtitleColor2});`;
+    else if (componentData.subtitleColor1) style += `background: ${componentData.subtitleColor1};`;
+    else if (componentData.subtitleColor2) style += `background: ${componentData.subtitleColor2};`;
+  }
   return style;
 });
 
-// 副标题样式
-const subtitleCss = computed(() => {
+//边框圆角
+const radiusCss = computed(() => {
   let style = '';
-  if (componentData.subtitleColor) style += 'color:' + componentData.subtitleColor + ';';
-  if (componentData.subtitleSize) style += 'font-size:' + componentData.subtitleSize + 'px;';
+  //圆角
+  if (componentData.navtopRounded) style += 'border-top-left-radius:' + componentData.navtopRounded + 'px;';
+  if (componentData.navtopRounded) style += 'border-top-right-radius:' + componentData.navtopRounded + 'px;';
+  if (componentData.navBtnbottomRounded) style += 'border-bottom-left-radius:' + componentData.navBtnbottomRounded + 'px;';
+  if (componentData.navBtnbottomRounded) style += 'border-bottom-right-radius:' + componentData.navBtnbottomRounded + 'px;';
   return style;
 });
+
+//背景颜色
+const backgroundCss = (row: any) => {
+  let style = '';
+  //背景颜色
+  if (row.color1) {
+    if (row.color1 && row.color2) style += `background:linear-gradient(to bottom,${row.color1},${row.color2});`;
+    else if (row.color1) style += `background: ${row.color1};`;
+    else if (row.color2) style += `background: ${row.color2};`;
+  }
+  return style;
+};
+
+// 按钮颜色
+const btnCss = (row: any) => {
+  let style = '';
+  //背景颜色
+  if (row.btncolor1) {
+    if (row.btncolor1 && row.btncolor2) style += `background:linear-gradient(to right,${row.btncolor1},${row.btncolor2});`;
+    else if (row.btncolor1) style += `background: ${row.btncolor1};`;
+    else if (row.btncolor2) style += `background: ${row.btncolor2};`;
+  }
+  return style;
+};
 </script>
 
 <style lang="scss" scoped>
 .pcPages {
   width: 1200px;
   margin: 0 auto;
-  .data-bos {
-    display: flex;
-    gap: 10px;
+  .head-bos {
     width: 100%;
-    overflow-x: auto;
-    .data-list {
-      min-height: 270px;
-      width: 0;
-
-      .title {
-        text-align: center;
-        padding: 0 15px;
+    height: 50px;
+    background-color: #ffffff;
+    padding: 0 20px;
+    .head-img {
+      height: 30px;
+      width: 150px;
+      margin-right: 10px;
+    }
+    .head-title {
+      font-weight: 600;
+      font-size: 16px;
+      color: #101828;
+      margin-right: 10px;
+    }
+    .head-subtitle {
+      font-size: 13px;
+      text-align: center;
+      padding: 0 15px;
+      &.head-subtitle1 {
+        border-radius: 0 28px 28px 28px;
+        height: 28px;
+        line-height: 28px;
       }
-
-      .subtitle {
-        text-align: center;
-        padding: 0 15px;
+      &.head-subtitle2 {
+        border-radius: 5px;
+        height: 28px;
+        line-height: 28px;
       }
-
-      .img {
-        height: 200px;
-        width: 100%;
+      &.head-subtitle3 {
+        display: inline-block;
+        clip-path: polygon(0 0, 100% 0, 100% 100%, 5px 100%);
+        padding: 0 0 0 15px;
+        height: 28px;
+        display: flex;
+        align-items: center;
+      }
+      &.head-subtitle4 {
+        padding-left: 10px;
+        position: relative;
+        &::after {
+          content: '';
+          height: 14px;
+          width: 1px;
+          background-color: #666666;
+          position: absolute;
+          left: 0;
+          top: 2px;
+        }
+      }
+      .head-image {
+        height: 28px;
+        transform: rotate(180deg);
       }
     }
   }
-
-  .carousel-bos {
-    .carousel-list {
-      width: 100%;
-      height: 100%;
-      display: flex;
-      gap: 10px;
-      flex-wrap: wrap;
-      .data-list {
-        height: 270px;
+  .trade-bos {
+    width: 100%;
+    display: flex;
+    gap: 0 10px;
+    margin-top: 10px;
+    &.trade-bos1 {
+      .trade-list {
+        height: 146px;
+        .trade-box {
+          .trade-title {
+            font-size: 16px;
+          }
+          .trade-info {
+            font-size: 12px;
+            height: 32px;
+            display: -webkit-box;
+            -webkit-line-clamp: 2;
+            line-clamp: 2;
+            /* 添加标准属性 */
+            -webkit-box-orient: vertical;
+            overflow: hidden;
+            text-overflow: ellipsis;
+          }
+        }
+        .trade-img {
+          width: 80px;
+          height: 80px;
+        }
+      }
+    }
+    &.trade-bos2 {
+      .trade-list {
+        height: 160px;
+        .trade-box {
+          .trade-title {
+            font-size: 24px;
+          }
+          .trade-info {
+            font-size: 20px;
+            height: 32px;
+            overflow: hidden;
+            white-space: nowrap;
+            text-overflow: ellipsis;
+          }
+          .trade-look {
+            height: 35px;
+            .trade-image {
+              height: 35px;
+            }
+          }
+        }
+        .trade-img {
+          width: 100px;
+          height: 100px;
+        }
+      }
+    }
+    .trade-list {
+      flex: 1;
+      background: #ffffff;
+      border-radius: 4px;
+      padding: 22px;
+      cursor: pointer;
+      .trade-box {
+        flex: 1;
         width: 0;
-
-        .title {
-          text-align: center;
-          padding: 0 15px;
+        height: 100%;
+        .trade-title {
+          color: #000000;
         }
-
-        .subtitle {
-          text-align: center;
-          padding: 0 15px;
+        .trade-info {
+          color: #364153;
         }
-
-        .img {
-          height: 200px;
-          width: 100%;
+        .trade-look {
+          height: 28px;
+          font-size: 12px;
+          padding: 0 10px 0 0px;
+          color: #ffffff;
+          border-radius: 0 50px 50px 0px;
+          .bnt-box {
+            height: 16px;
+            width: 16px;
+            background-color: #ffffff;
+            border-radius: 16px;
+            margin-left: 10px;
+          }
+          .trade-image {
+            height: 28px;
+          }
         }
+        .trade-look3 {
+          height: 35px;
+          font-size: 14px;
+          padding: 0 15px;
+          color: #ffffff;
+          border-radius: 50px;
+          .bnt-box {
+            height: 20px;
+            width: 20px;
+            background-color: #ffffff;
+            border-radius: 20px;
+            margin-left: 10px;
+          }
+        }
+      }
+      .trade-img {
+        border-radius: 5px;
+        margin-left: 10px;
       }
     }
   }

+ 82 - 0
src/views/diy/pccomponents/pages/blank.vue

@@ -0,0 +1,82 @@
+<template>
+  <div class="pcPages" :style="warpCss">
+    <div :style="boxCss"></div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import usePcdiyStore from '@/store/modules/pcdiy';
+const diyStore = usePcdiyStore();
+const props = defineProps<{
+  index: number; // 确保声明 index 为可选属性
+  row?: 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;
+});
+
+//组件样式
+const boxCss = computed(() => {
+  let style = '';
+  if (componentData.componentStartBgColor && componentData.componentEndBgColor)
+    style += `background:linear-gradient(${componentData.componentGradientAngle},${componentData.componentStartBgColor},${componentData.componentEndBgColor});`;
+  else if (componentData.componentStartBgColor) style += 'background-color:' + componentData.componentStartBgColor + ';';
+  else if (componentData.componentEndBgColor) style += 'background-color:' + componentData.componentEndBgColor + ';';
+  if (componentData.number) style += 'flex:' + `0 0 calc((100% - ${(componentData.number - 1) * 10}px) / ${componentData.number})` + ';';
+  if (componentData.boxHeight) style += 'height:' + componentData.boxHeight + 'px;';
+  return style;
+});
+</script>
+<style lang="scss" scoped>
+.pcPages {
+  width: 1200px;
+  margin: 0 auto;
+  .pcPages-html {
+    :deep(img) {
+      max-width: 100%;
+      display: inline-block;
+    }
+  }
+}
+</style>

+ 92 - 0
src/views/diy/pccomponents/pages/border.vue

@@ -0,0 +1,92 @@
+<template>
+  <div class="pcPages" :style="warpCss">
+    <div :style="boxCss">
+      <div :style="borderCss" class="w100%"></div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import usePcdiyStore from '@/store/modules/pcdiy';
+const diyStore = usePcdiyStore();
+const props = defineProps<{
+  index: number; // 确保声明 index 为可选属性
+  row?: 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;
+});
+
+//组件样式
+const boxCss = computed(() => {
+  let style = '';
+  if (componentData.componentStartBgColor && componentData.componentEndBgColor)
+    style += `background:linear-gradient(${componentData.componentGradientAngle},${componentData.componentStartBgColor},${componentData.componentEndBgColor});`;
+  else if (componentData.componentStartBgColor) style += 'background-color:' + componentData.componentStartBgColor + ';';
+  else if (componentData.componentEndBgColor) style += 'background-color:' + componentData.componentEndBgColor + ';';
+  if (componentData.number) style += 'flex:' + `0 0 calc((100% - ${(componentData.number - 1) * 10}px) / ${componentData.number})` + ';';
+  if (componentData.boxHeight) style += 'height:' + componentData.boxHeight + 'px;';
+  return style;
+});
+
+//组件样式
+const borderCss = computed(() => {
+  let style = '';
+  if (componentData.boxHeight) style += 'height:' + componentData.boxHeight + 'px;';
+  if (componentData.boxColor) style += 'background-color:' + componentData.boxColor;
+  return style;
+});
+</script>
+<style lang="scss" scoped>
+.pcPages {
+  width: 1200px;
+  margin: 0 auto;
+  .pcPages-html {
+    :deep(img) {
+      max-width: 100%;
+      display: inline-block;
+    }
+  }
+}
+</style>

+ 30 - 21
src/views/diy/pccomponents/pages/discover.vue

@@ -16,7 +16,7 @@
         <el-image
           class="discover-image"
           :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="plan-bos" :style="componentData.boxRadius ? { borderRadius: componentData.boxRadius + 'px' } : {}">
@@ -90,23 +90,24 @@
             </div>
           </div>
         </div>
-        <div
-          class="goods-bos flex-column-between"
-          v-for="(item, index) in dataList"
-          :key="index"
-          :style="componentData.boxRadius ? { borderRadius: componentData.boxRadius + 'px' } : {}"
-        >
-          <img
-            class="goods-img"
-            :src="item.productImage ? item.productImage : figure"
-            alt=""
-            :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
-          />
-          <div>
-            <div class="goods-name">{{ item.itemName || '' }}</div>
-            <div class="goods-price" :style="{ color: componentData.boxColor }">¥{{ item.memberPrice }}</div>
+        <template v-for="(item, index) in dataList" :key="index">
+          <div
+            v-if="Number(index) < 4"
+            class="goods-bos flex-column-between"
+            :style="componentData.boxRadius ? { borderRadius: componentData.boxRadius + 'px' } : {}"
+          >
+            <img
+              class="goods-img"
+              :src="item.productImage ? item.productImage : figure"
+              alt=""
+              :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
+            />
+            <div>
+              <div class="goods-name">{{ item.itemName || '商品名称' }}</div>
+              <div class="goods-price" :style="{ color: componentData.boxColor }">¥{{ item.memberPrice || '0.00' }}</div>
+            </div>
           </div>
-        </div>
+        </template>
       </div>
     </div>
   </div>
@@ -119,7 +120,7 @@ import { listBase } from '@/api/pmsProduct/base';
 const diyStore = usePcdiyStore();
 
 const props = defineProps<{
-    index: number; // 确保声明 index 为可选属性
+  index: number; // 确保声明 index 为可选属性
   row?: any;
 }>();
 const componentData = props.row ? props.row : diyStore.componentList[props.index];
@@ -179,7 +180,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;
 });
 
@@ -232,7 +241,7 @@ const subtitleCss = computed(() => {
   .discover-box {
     height: 340px;
     width: 100%;
-    margin-top: 15px;
+    margin-top: 10px;
     display: flex;
     gap: 10px;
 
@@ -390,7 +399,7 @@ const subtitleCss = computed(() => {
   .discover-foot {
     display: flex;
     gap: 10px;
-    margin-top: 15px;
+    margin-top: 10px;
     .discover-tab {
       width: 230px;
       height: 310px;

+ 83 - 0
src/views/diy/pccomponents/pages/editordiy.vue

@@ -0,0 +1,83 @@
+<template>
+  <div class="pcPages" :style="warpCss">
+    <div :style="boxCss">
+      <div class="pcPages-html" v-html="componentData.detail"></div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import usePcdiyStore from '@/store/modules/pcdiy';
+const diyStore = usePcdiyStore();
+const props = defineProps<{
+  index: number; // 确保声明 index 为可选属性
+  row?: 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;
+});
+
+//组件样式
+const boxCss = computed(() => {
+  let style = '';
+  if (componentData.componentStartBgColor && componentData.componentEndBgColor)
+    style += `background:linear-gradient(${componentData.componentGradientAngle},${componentData.componentStartBgColor},${componentData.componentEndBgColor});`;
+  else if (componentData.componentStartBgColor) style += 'background-color:' + componentData.componentStartBgColor + ';';
+  else if (componentData.componentEndBgColor) style += 'background-color:' + componentData.componentEndBgColor + ';';
+  if (componentData.number) style += 'flex:' + `0 0 calc((100% - ${(componentData.number - 1) * 10}px) / ${componentData.number})` + ';';
+  return style;
+});
+</script>
+<style lang="scss" scoped>
+.pcPages {
+  width: 1200px;
+  margin: 0 auto;
+  .pcPages-html {
+    :deep(img) {
+      max-width: 100%;
+      display: inline-block;
+    }
+  }
+}
+</style>

+ 14 - 5
src/views/diy/pccomponents/pages/floor.vue

@@ -4,7 +4,7 @@
       <el-image
         class="floor-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.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
       />
       <div class="floor-box">
@@ -15,13 +15,13 @@
             alt=""
             :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
           />
-          <div v-if="componentData.goodsShow.includes(1)" class="itemName">{{ item.itemName || '' }}</div>
+          <div v-if="componentData.goodsShow.includes(1)" class="itemName">{{ item.itemName || '商品名称' }}</div>
           <div class="flex-row-between">
             <div>
               <span v-if="componentData.goodsShow.includes(2)" class="memberPrice" :style="{ color: componentData.btnbackgroundColor }"
-                >¥{{ item.memberPrice }}</span
+                >¥{{ item.memberPrice || '0.00' }}</span
               >
-              <span v-if="componentData.goodsShow.includes(3)" class="marketPrice">¥{{ item.marketPrice }}</span>
+              <span v-if="componentData.goodsShow.includes(3)" class="marketPrice">¥{{ item.marketPrice || '0.00' }}</span>
             </div>
             <template v-if="componentData.btnShow">
               <div v-if="componentData.btnStyle == 1" :style="btnCss1" class="btn1 ellipsis">{{ componentData.btnText }}</div>
@@ -62,7 +62,7 @@ import { listBase } from '@/api/pmsProduct/base';
 const diyStore = usePcdiyStore();
 
 const props = defineProps<{
-    index: number; // 确保声明 index 为可选属性
+  index: number; // 确保声明 index 为可选属性
   row?: any;
 }>();
 const componentData = props.row ? props.row : diyStore.componentList[props.index];
@@ -122,6 +122,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;
 });

+ 91 - 27
src/views/diy/pccomponents/pages/goods.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="pcPages" :style="warpCss">
-    <div class="goods-bos" :style="boxCss">
+    <div class="goods-bos" :style="boxCss" v-if="componentData.styleType == 1">
       <div v-for="(item, index) in dataList" :key="index" class="goods-list flex-column-between" :style="goodsCss">
         <img
           class="goods-img"
@@ -8,16 +8,16 @@
           alt=""
           :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
         />
-        <div v-if="componentData.goodsShow.includes(1)" :style="titleCss" class="itemName">{{ item.itemName || '' }}</div>
+        <div v-if="componentData.goodsShow.includes(1)" :style="titleCss" class="itemName">{{ item.itemName || '商品名称' }}</div>
         <div class="flex-row-between">
           <div>
             <span v-if="componentData.goodsShow.includes(2)" class="memberPrice" :style="{ color: componentData.priceColor }"
-              >¥{{ item.memberPrice }}</span
+              >¥{{ item.memberPrice || '0.00' }}</span
             >
-            <span v-if="componentData.goodsShow.includes(3)" class="marketPrice">¥{{ item.marketPrice }}</span>
+            <span v-if="componentData.goodsShow.includes(3)" class="marketPrice">¥{{ item.marketPrice || '0.00' }}</span>
           </div>
           <template v-if="componentData.btnShow">
-            <div v-if="componentData.btnStyle == 1" :style="btnCss1" class="btn1">{{ componentData.btnText }}</div>
+            <div v-if="componentData.btnStyle == 1" :style="btnCss1" class="btn1">{{ componentData.btnText || '购买' }}</div>
             <div v-if="componentData.btnStyle == 2" :style="btnCss2" class="btn2 flex-row-center">
               <el-icon size="14"><Plus /></el-icon>
             </div>
@@ -28,6 +28,40 @@
         </div>
       </div>
     </div>
+    <div v-else :style="boxCss">
+      <el-carousel height="300px" arrow="always" autoplay>
+        <el-carousel-item v-for="(item1, index1) in dataList" :key="index1" class="w100% h100%">
+          <div class="goods-bos">
+            <div v-for="(item, index) in item1" :key="index" class="goods-list flex-column-between" :style="goodsCss">
+              <img
+                class="goods-img"
+                :src="item.productImage ? item.productImage : figure"
+                alt=""
+                :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
+              />
+              <div v-if="componentData.goodsShow.includes(1)" :style="titleCss" class="itemName">{{ item.itemName || '商品名称' }}</div>
+              <div class="flex-row-between">
+                <div>
+                  <span v-if="componentData.goodsShow.includes(2)" class="memberPrice" :style="{ color: componentData.priceColor }"
+                    >¥{{ item.memberPrice || '0.00' }}</span
+                  >
+                  <span v-if="componentData.goodsShow.includes(3)" class="marketPrice">¥{{ item.marketPrice || '0.00' }}</span>
+                </div>
+                <template v-if="componentData.btnShow">
+                  <div v-if="componentData.btnStyle == 1" :style="btnCss1" class="btn1">{{ componentData.btnText || '购买' }}</div>
+                  <div v-if="componentData.btnStyle == 2" :style="btnCss2" class="btn2 flex-row-center">
+                    <el-icon size="14"><Plus /></el-icon>
+                  </div>
+                  <div v-if="componentData.btnStyle == 3" :style="btnCss2" class="btn2 flex-row-center">
+                    <icon name="iconfont icongouwuche" size="14px" />
+                  </div>
+                </template>
+              </div>
+            </div>
+          </div>
+        </el-carousel-item>
+      </el-carousel>
+    </div>
   </div>
 </template>
 
@@ -38,58 +72,75 @@ import { listBase } from '@/api/pmsProduct/base';
 const diyStore = usePcdiyStore();
 
 const props = defineProps<{
-   index: number; // 确保声明 index 为可选属性
+  index: number; // 确保声明 index 为可选属性
   row?: any;
 }>();
 const componentData = props.row ? props.row : diyStore.componentList[props.index];
-const dataList = ref<any>([{}, {}, {}, {}, {}]);
+const original = ref<any>([{}, {}, {}, {}, {}]);
 
 onMounted(() => {
   getDataList();
 });
 
 const getDataList = () => {
-  dataList.value = [{}, {}, {}, {}, {}];
+  original.value = [{}, {}, {}, {}, {}];
   //手动选择
   if (componentData.goodsType == 1) {
     if (componentData.goodsIds.length > 0) {
       listBase({ pageNum: 1, pageSize: 20, ids: componentData.goodsIds.join(',') }).then((res) => {
         if (res.code == 200) {
-          dataList.value = res.rows;
+          original.value = res.rows;
         }
       });
     }
   } else if (componentData.goodsType == 2) {
     //分类查询
-    if (componentData.goodsClassify) {
+    if (componentData.categoryIds && componentData.categoryIds.length > 0) {
+      const categoryIds = componentData.categoryIds.map((item: any) => item[2]);
       listBase({
         pageNum: 1,
-        pageSize: componentData.goodsNumber == 0 ? 50 : componentData.goodsNumber,
-        topCategoryId: componentData.topCategoryId,
-        mediumCategoryId: componentData.mediumCategoryId,
-        bottomCategoryId: componentData.bottomCategoryId
+        pageSize: componentData.goodsNumber == 0 ? 9999 : componentData.goodsNumber,
+        categoryIds: categoryIds.join(',')
       }).then((res) => {
         if (res.code == 200) {
-          dataList.value = res.rows;
+          if (res.rows && res.rows.length > 0) {
+            original.value = res.rows;
+          }
         }
       });
     }
   } else if (componentData.goodsType == 3) {
     //品牌查询
-    if (componentData.goodsBrand) {
+    if (componentData.brandIds && componentData.brandIds.length > 0) {
       listBase({
         pageNum: 1,
-        pageSize: componentData.goodsNumber == 0 ? 50 : componentData.goodsNumber,
-        brandId: componentData.goodsBrand
+        pageSize: componentData.goodsNumber == 0 ? 9999 : componentData.goodsNumber,
+        brandIds: componentData.brandIds.join(',')
       }).then((res) => {
         if (res.code == 200) {
-          dataList.value = res.rows;
+          if (res.rows && res.rows.length > 0) {
+            original.value = res.rows;
+          }
         }
       });
     }
   }
 };
 
+const dataList = computed(() => {
+  if (componentData.styleType == 1) {
+    return original.value;
+  } else {
+    const pageSize = 5;
+    const groupedUsers = [];
+    for (let i = 0; i < original.value.length; i += pageSize) {
+      groupedUsers.push(original.value.slice(i, i + pageSize));
+    }
+    return groupedUsers;
+  }
+});
+
+// 选择方式
 watch(
   () => componentData.goodsType,
   () => {
@@ -97,29 +148,35 @@ watch(
   }
 );
 
+// 商品数量
 watch(
-  () => componentData.goodsClassify,
+  () => componentData.goodsNumber,
   () => {
     getDataList();
   }
 );
 
+// 手动选择商品ids
 watch(
-  () => componentData.goodsNumber,
+  () => componentData.goodsIds,
   () => {
     getDataList();
-  }
+  },
+  { deep: true } // 5. 数组变化需要 deep 监听
 );
 
+// 分类
 watch(
-  () => componentData.goodsBrand,
+  () => componentData.categoryIds,
   () => {
     getDataList();
-  }
+  },
+  { deep: true } // 5. 数组变化需要 deep 监听
 );
 
+// 品牌
 watch(
-  () => componentData.goodsIds,
+  () => componentData.brandIds,
   () => {
     getDataList();
   },
@@ -157,7 +214,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;
 });
 
@@ -221,7 +286,6 @@ const btnCss2 = computed(() => {
   .goods-bos {
     display: flex;
     gap: 10px;
-    overflow: auto;
     .goods-list {
       padding: 20px 15px;
       height: 300px;

+ 15 - 9
src/views/diy/pccomponents/pages/goodsList.vue

@@ -16,13 +16,13 @@
             alt=""
             :style="componentData.imageRadius ? { borderRadius: componentData.imageRadius + 'px' } : {}"
           />
-          <div v-if="componentData.goodsShow.includes(1)" :style="titleCss" class="itemName">{{ item.itemName || '' }}</div>
+          <div v-if="componentData.goodsShow.includes(1)" :style="titleCss" class="itemName">{{ item.itemName || '商品名称' }}</div>
           <div class="flex-row-between">
             <div>
               <span v-if="componentData.goodsShow.includes(2)" class="memberPrice" :style="{ color: componentData.priceColor }"
-                >¥{{ item.memberPrice }}</span
+                >¥{{ item.memberPrice || '0.00' }}</span
               >
-              <span v-if="componentData.goodsShow.includes(3)" class="marketPrice">¥{{ item.marketPrice }}</span>
+              <span v-if="componentData.goodsShow.includes(3)" class="marketPrice">¥{{ item.marketPrice || '0.00' }}</span>
             </div>
             <template v-if="componentData.btnShow">
               <div v-if="componentData.btnStyle == 1" :style="btnCss1" class="btn1">{{ componentData.btnText }}</div>
@@ -72,13 +72,11 @@ const getDataList = () => {
       }
     } else if (datas.goodsType == 2) {
       //分类查询
-      if (datas.goodsClassify) {
+      if (datas.categoryIds && datas.categoryIds.length > 0) {
         listBase({
           pageNum: 1,
-          pageSize: datas.goodsNumber == 0 ? 50 : componentData.goodsNumber,
-          topCategoryId: datas.topCategoryId,
-          mediumCategoryId: datas.mediumCategoryId,
-          bottomCategoryId: datas.bottomCategoryId
+          pageSize: componentData.goodsNumber == 0 ? 9999 : componentData.goodsNumber,
+          categoryIds: datas.categoryIds.join(',')
         }).then((res) => {
           if (res.code == 200) {
             dataList.value = res.rows;
@@ -92,7 +90,6 @@ const getDataList = () => {
 watch(
   () => componentData.tabIndex,
   () => {
-    console.log('tabIndex', componentData.tabIndex);
     getDataList();
   }
 );
@@ -136,6 +133,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;
 });

+ 1 - 8
src/views/diy/pccomponents/pages/hot.vue

@@ -3,7 +3,7 @@
     <el-image class="hot-image" :src="componentData.imageUrl ? componentData.imageUrl : figure" fit="fill" />
     <div
       @click="onClick(mapItem)"
-      class="absolute hot-aaaaaa"
+      class="absolute"
       v-for="(mapItem, mapIndex) in componentData.heatMapData"
       :key="mapIndex"
       :style="{ width: mapItem.width + '%', height: mapItem.height + '%', left: mapItem.left + '%', top: mapItem.top + '%' }"
@@ -77,13 +77,6 @@ const warpCss = computed(() => {
 
   .hot-image {
     width: 100%;
-
-
   }
-
-  .hot-aaaaaa {
-      border: 1px red solid;
-      z-index: 10;
-    }
 }
 </style>