瀏覽代碼

Merge branch 'master' into hurx

hurx 1 周之前
父節點
當前提交
046357069e
共有 75 個文件被更改,包括 1468 次插入2373 次删除
  1. 30 0
      src/api/diy/index.ts
  2. 3 3
      src/api/mall/pageCategory/api.ts
  3. 二進制
      src/assets/images/diy/category_style1_1.png
  4. 二進制
      src/assets/images/diy/category_style2_1.png
  5. 二進制
      src/assets/images/diy/category_style2_2.png
  6. 4 1
      src/components/ImageUpload/index.vue
  7. 130 28
      src/components/LinkSelector/index.vue
  8. 3 0
      src/components/WebLinkInput/index.vue
  9. 17 8
      src/components/goodsMini/index.vue
  10. 2 2
      src/components/heat-map/index.vue
  11. 655 0
      src/components/heat-mapMini/index.vue
  12. 4 4
      src/views/diy/components/edit-active-cube.vue
  13. 3 98
      src/views/diy/components/edit-carousel-search.vue
  14. 1 1
      src/views/diy/components/edit-float-btn.vue
  15. 0 273
      src/views/diy/components/edit-goods-coupon.vue
  16. 0 405
      src/views/diy/components/edit-goods-list copy.vue
  17. 7 0
      src/views/diy/components/edit-goods-list.vue
  18. 2 2
      src/views/diy/components/edit-graphic-nav.vue
  19. 1 1
      src/views/diy/components/edit-horz-blank.vue
  20. 1 1
      src/views/diy/components/edit-horz-line.vue
  21. 1 1
      src/views/diy/components/edit-hot-area.vue
  22. 2 2
      src/views/diy/components/edit-image-ads.vue
  23. 52 501
      src/views/diy/components/edit-many-goods-list.vue
  24. 2 2
      src/views/diy/components/edit-notice.vue
  25. 2 2
      src/views/diy/components/edit-picture-show.vue
  26. 3 17
      src/views/diy/components/edit-rich-text.vue
  27. 3 2
      src/views/diy/components/edit-rubik-cube.vue
  28. 0 135
      src/views/diy/components/edit-shop-exchange-goods.vue
  29. 0 38
      src/views/diy/components/edit-shop-exchange-info.vue
  30. 31 13
      src/views/diy/components/edit-shop-goods-recommend.vue
  31. 1 1
      src/views/diy/components/edit-shop-search.vue
  32. 37 199
      src/views/diy/components/edit-single-recommend.vue
  33. 1 1
      src/views/diy/components/edit-text.vue
  34. 47 489
      src/views/diy/edit.ts
  35. 1 3
      src/views/diy/edit.vue
  36. 4 4
      src/views/diy/miniEdit.vue
  37. 1 1
      src/views/diy/pccomponents/edit/navigation-edit.vue
  38. 2 2
      src/views/diy/pccomponents/pages/floor.vue
  39. 4 4
      src/views/diy/pccomponents/pages/navigation.vue
  40. 141 0
      src/views/mall/miniPageSet/index.vue
  41. 7 5
      src/views/mall/navigation/index.vue
  42. 10 4
      src/views/mall/pageCategory/index.vue
  43. 11 4
      src/views/mall/pageLink/index.vue
  44. 2 7
      src/views/platform/customerOperation/vipSite/index.vue
  45. 1 1
      src/views/platform/customerOperation/vipSite/styleDesign.vue
  46. 1 1
      src/views/platform/decoration/brand/index.vue
  47. 1 1
      src/views/platform/decoration/carousel/index.vue
  48. 1 1
      src/views/platform/decoration/caseAd/index.vue
  49. 50 43
      src/views/platform/decoration/flashSale/index.vue
  50. 2 2
      src/views/platform/decoration/floor/manage.vue
  51. 1 1
      src/views/platform/decoration/floorAd/index.vue
  52. 1 1
      src/views/platform/decoration/fresh/index.vue
  53. 1 1
      src/views/platform/decoration/recommend/index.vue
  54. 1 1
      src/views/platform/decoration/solution/index.vue
  55. 18 1
      src/views/platform/decoration/special/index.vue
  56. 1 1
      src/views/platform/enterprise/carousel/index.vue
  57. 1 1
      src/views/platform/enterprise/floorAd/index.vue
  58. 1 1
      src/views/platform/gift/banner/index.vue
  59. 3 3
      src/views/platform/gift/carousel/index.vue
  60. 1 1
      src/views/platform/gift/floorAd/index.vue
  61. 1 1
      src/views/platform/gift/hotCustom/index.vue
  62. 3 3
      src/views/platform/gift/icon/index.vue
  63. 13 13
      src/views/platform/gift/longAd/index.vue
  64. 3 3
      src/views/platform/gift/recommendAd/index.vue
  65. 1 1
      src/views/platform/industrial/brandFloor/index.vue
  66. 3 3
      src/views/platform/industrial/carousel/index.vue
  67. 100 5
      src/views/platform/industrial/categoryProduct/index.vue
  68. 1 1
      src/views/platform/industrial/floorAd/index.vue
  69. 1 1
      src/views/platform/industrial/tag/index.vue
  70. 2 2
      src/views/platform/operation/gallery/index.vue
  71. 2 2
      src/views/platform/operation/theme/edit.vue
  72. 6 6
      src/views/platform/ruleCenter/category/index.vue
  73. 1 1
      src/views/platform/ruleCenter/list/edit.vue
  74. 17 5
      src/views/system/basicSetting/index.vue
  75. 1 1
      src/views/system/loginSetting/index.vue

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

@@ -175,3 +175,33 @@ export function navigationSave(data: any) {
     data: data
   });
 }
+
+// 查询小程序页面设置列表
+
+export function miniPageSetCurrent(query: any) {
+  return request({
+    url: '/mall/miniPageSet/current',
+    method: 'get',
+    data: query
+  });
+}
+
+// 新增小程序页面设置
+
+export function miniPageSetAdd(data: any) {
+  return request({
+    url: '/mall/miniPageSet',
+    method: 'post',
+    data: data
+  });
+}
+
+// 新增小程序页面设置
+
+export function miniPageSetEdit(data: any) {
+  return request({
+    url: '/mall/miniPageSet',
+    method: 'put',
+    data: data
+  });
+}

+ 3 - 3
src/api/mall/pageCategory/api.ts

@@ -66,12 +66,12 @@ export const delPageCategory = (id: string | number | Array<string | number>) =>
 /**
  * 查询分类树结构(用于选择器)
  */
-export const getCategoryTree = (): AxiosPromise<PageCategoryVO[]> => {
+export function getCategoryTree(pageType: any) {
   return request({
-    url: '/mall/pageCategory/tree',
+    url: `/mall/pageCategory/tree?pageType=${pageType}`,
     method: 'get'
   });
-};
+}
 
 /**
  * 查询完整的分类树结构(用于树形展示)

二進制
src/assets/images/diy/category_style1_1.png


二進制
src/assets/images/diy/category_style2_1.png


二進制
src/assets/images/diy/category_style2_2.png


+ 4 - 1
src/components/ImageUpload/index.vue

@@ -194,7 +194,10 @@ const handleDelete = (file: UploadFile): boolean => {
   const findex = fileList.value.map((f) => f.name).indexOf(file.name);
   if (findex > -1 && uploadList.value.length === number.value) {
     const ossId = fileList.value[findex].ossId;
-    delOss(ossId);
+    // 只有在 ossId 存在且有效时才调用删除接口
+    if (ossId && ossId !== 'undefined' && ossId !== 'null') {
+      // delOss(ossId);
+    }
     fileList.value.splice(findex, 1);
     emit('update:modelValue', listToString(fileList.value));
     return false;

+ 130 - 28
src/components/LinkSelector/index.vue

@@ -18,7 +18,7 @@
             <div v-for="(item1, index1) in boxData" :key="index1">
               <h5 class="button-title">
                 <div class="title">{{ item1.name }}</div>
-                <div v-if="index1 == 0">
+                <div v-if="index1 == 0 && pageType == 1">
                   <span class="mr-[5px]">是否新窗口打开</span>
                   <el-switch v-model="switchVal" />
                 </div>
@@ -35,8 +35,8 @@
             </div>
           </template>
           <template v-else>
-            <div v-if="boxpageKey == 'goodsClassify'">
-              <div>
+            <div v-if="boxpageKey == 'goodsClassify' || boxpageKey == 'appgoodsClassify'">
+              <div v-if="pageType == 1">
                 <span class="mr-[5px]">是否新窗口打开</span>
                 <el-switch v-model="switchVal" />
               </div>
@@ -70,24 +70,29 @@
               </el-table>
             </div>
             <!-- 商品 -->
-            <div v-if="boxpageKey == 'goodsItem'">
+            <div v-if="boxpageKey == 'goodsItem' || boxpageKey == 'appgoodsItem'">
               <div class="flex justify-between">
                 <el-input v-model="queryParams2.itemName" placeholder="请输入商品名称" clearable style="width: 300px; margin-bottom: 10px">
                   <template #append>
                     <el-button :icon="Search" @click="handleQuery2" />
                   </template>
                 </el-input>
-                <div>
+                <div v-if="pageType == 1">
                   <span class="mr-[5px]">是否新窗口打开</span>
                   <el-switch v-model="switchVal" />
                 </div>
               </div>
               <el-table height="480" ref="multipleTableRef" v-loading="loading2" :data="tableData2" border>
-                <el-table-column width="80" align="center">
+                <el-table-column width="80" align="center" v-if="pageType == 1">
                   <template #default="{ row }">
                     <el-checkbox v-model="tableId2" :true-value="row.productNo" />
                   </template>
                 </el-table-column>
+                <el-table-column width="80" align="center" v-else>
+                  <template #default="{ row }">
+                    <el-checkbox v-model="tableId2" :true-value="row.id" />
+                  </template>
+                </el-table-column>
                 <el-table-column label="商品图片" align="center" prop="productImage" width="100">
                   <template #default="scope">
                     <image-preview :src="scope.row.productImage" :width="60" :height="60" />
@@ -291,7 +296,9 @@
                     >
                       <template #error>
                         <div class="image-placeholder">
-                          <el-icon><Picture /></el-icon>
+                          <el-icon>
+                            <Picture />
+                          </el-icon>
                         </div>
                       </template>
                     </el-image>
@@ -316,6 +323,28 @@
                 @pagination="getTableData6"
               />
             </div>
+            <!-- 新闻咨询 -->
+            <div v-if="boxpageKey == 'appnews'">
+              <el-table v-loading="loading7" border :data="tableData7" height="480">
+                <el-table-column width="80" align="center">
+                  <template #default="{ row }">
+                    <el-checkbox v-model="tableId7" :true-value="row.id" />
+                  </template>
+                </el-table-column>
+                <el-table-column label="标题" align="center" prop="title" min-width="200" show-overflow-tooltip>
+                  <template #default="scope">
+                    {{ scope.row.title }}
+                  </template>
+                </el-table-column>
+              </el-table>
+              <pagination
+                v-show="total7 > 0"
+                :total="total7"
+                v-model:page="queryParams7.pageNum"
+                v-model:limit="queryParams7.pageSize"
+                @pagination="getTableData7"
+              />
+            </div>
             <!-- 自定义 -->
             <div v-if="boxpageKey == 'zdy'">
               <div class="flex justify-between">
@@ -350,6 +379,14 @@ import { listProgram } from '@/api/product/program/index';
 import { programList } from '@/api/pmsProduct/program';
 import { listTopics } from '@/api/product/topics';
 import { listServiceCase } from '@/api/product/serviceCase';
+import { listGiftNotice } from '@/api/product/giftNotice';
+
+interface Props {
+  pageType?: any;
+}
+const props = defineProps<Props>();
+const pageType = props.pageType || '1';
+
 const showDialog = ref(false);
 const menuData = ref<any[]>([]);
 const defaultOpeneds = ref<any>(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10']);
@@ -417,6 +454,15 @@ const queryParams6 = reactive({
   pageSize: 10,
   caseTitle: ''
 });
+//新闻咨询
+const tableId7 = ref<any>(null);
+const total7 = ref(0);
+const loading7 = ref(true);
+const tableData7 = ref<any>([]);
+const queryParams7 = reactive({
+  pageNum: 1,
+  pageSize: 10
+});
 //自定义
 const input = ref('');
 
@@ -436,7 +482,7 @@ const open = () => {
 // 加载分类数据
 const loadCategoryData = async () => {
   try {
-    const res = await getCategoryTree();
+    const res = await getCategoryTree(pageType);
     if (res.data && res.data.length > 0) {
       const firstLevelChildren = res.data[0].children;
       menuData.value = firstLevelChildren;
@@ -484,12 +530,12 @@ const onMenu = (res: any) => {
     if (list.length == 2) {
       const datas = menuData.value[list[0]].children[list[1]];
       boxpageKey.value = datas.pageKey;
-      if (boxpageKey.value == 'goodsClassify') {
+      if (boxpageKey.value == 'goodsClassify' || boxpageKey.value == 'appgoodsClassify') {
         if (tableData1.value.length == 0) {
           getTableData1();
         }
       }
-      if (boxpageKey.value == 'goodsItem') {
+      if (boxpageKey.value == 'goodsItem' || boxpageKey.value == 'appgoodsItem') {
         handleQuery2();
       }
       if (boxpageKey.value == 'plan') {
@@ -504,6 +550,9 @@ const onMenu = (res: any) => {
       if (boxpageKey.value == 'project') {
         handleQuery6();
       }
+      if (boxpageKey.value == 'appnews') {
+        handleQuery7();
+      }
       console.log(datas, 'datas');
     } else {
       if (menuData.value[list[0]].type == '4') {
@@ -538,7 +587,7 @@ const loadChildren = async (row: CategoryVO, treeNode: any, resolve: (data: Cate
     // 标记子节点是否还有子级
     const children = data.map((item: CategoryVO) => ({
       ...item,
-      hasChildren: item.hasChildren ?? item.classLevel < 3
+      hasChildren: item.hasChildren ?? item.classLevel < (pageType == 1 ? 3 : 2)
     }));
     resolve(children);
   } catch (error) {
@@ -637,7 +686,7 @@ const handleQuery6 = () => {
 const getTableData6 = async () => {
   loading6.value = true;
   try {
-    const res = await listServiceCase(queryParams5);
+    const res = await listServiceCase(queryParams6);
     tableData6.value = res.rows || [];
     total6.value = res.total || 0;
   } finally {
@@ -645,6 +694,24 @@ const getTableData6 = async () => {
   }
 };
 
+//新闻咨询查询
+const handleQuery7 = () => {
+  queryParams7.pageNum = 1;
+  getTableData7();
+};
+
+/** 获取列表新闻咨询 */
+const getTableData7 = async () => {
+  loading7.value = true;
+  try {
+    const res = await listGiftNotice(queryParams7);
+    tableData7.value = res.rows || [];
+    total7.value = res.total || 0;
+  } finally {
+    loading7.value = false;
+  }
+};
+
 const onSelected = (item: any) => {
   selected.value = item.url;
 };
@@ -652,7 +719,11 @@ const onSelected = (item: any) => {
 const onConfirm = () => {
   if (boxShow1.value) {
     if (selected.value) {
-      emit('update:modelValue', `${selected.value}?openType=${switchVal.value ? 1 : 0}`);
+      if (pageType == '1') {
+        emit('update:modelValue', `${selected.value}?openType=${switchVal.value ? 1 : 0}`);
+      } else {
+        emit('update:modelValue', `${selected.value}`);
+      }
       showDialog.value = false;
     } else {
       ElMessage({
@@ -662,18 +733,29 @@ const onConfirm = () => {
       });
     }
   } else {
-    if (boxpageKey.value == 'goodsClassify') {
+    if (boxpageKey.value == 'goodsClassify' || boxpageKey.value == 'appgoodsClassify') {
       if (tableId1.value) {
-        let url1 = '/search?type=' + selectedRowObj.value.classLevel;
-        if (selectedRowObj.value.classLevel == 1) {
-          url1 = url1 + '&topCategoryId=' + tableId1.value;
-        } else if (selectedRowObj.value.classLevel == 2) {
-          url1 = url1 + '&middleCategoryId=' + tableId1.value;
-        } else if (selectedRowObj.value.classLevel == 3) {
-          url1 = url1 + '&bottomCategoryId=' + tableId1.value;
+        if (pageType == '1') {
+          let url1 = '/search?type=' + selectedRowObj.value.classLevel;
+          if (selectedRowObj.value.classLevel == 1) {
+            url1 = url1 + '&topCategoryId=' + tableId1.value;
+          } else if (selectedRowObj.value.classLevel == 2) {
+            url1 = url1 + '&mediumCategoryId=' + tableId1.value;
+          } else if (selectedRowObj.value.classLevel == 3) {
+            url1 = url1 + '&bottomCategoryId=' + tableId1.value;
+          }
+          url1 = url1 + '&openType=' + (switchVal.value ? 1 : 0);
+          emit('update:modelValue', url1);
+        } else {
+          let url1 = '/addon/shop/pages/goods/category';
+          if (selectedRowObj.value.classLevel == 1) {
+            url1 = url1 + '?categoryId=' + tableId1.value;
+          } else if (selectedRowObj.value.classLevel == 2) {
+            url1 = url1 + '?mediumCategoryId=' + tableId1.value;
+          }
+          emit('update:modelValue', url1);
         }
-        url1 = url1 + '&openType=' + (switchVal.value ? 1 : 0);
-        emit('update:modelValue', url1);
+
         showDialog.value = false;
       } else {
         ElMessage({
@@ -683,12 +765,18 @@ const onConfirm = () => {
         });
       }
     }
-    if (boxpageKey.value == 'goodsItem') {
+    if (boxpageKey.value == 'goodsItem' || boxpageKey.value == 'appgoodsItem') {
       if (tableId2.value) {
-        let url2 = '/item?productNo=' + tableId2.value;
-        url2 = url2 + '&openType=' + (switchVal.value ? 1 : 0);
-        emit('update:modelValue', url2);
-        showDialog.value = false;
+        if (pageType == '1') {
+          let url2 = '/item?productNo=' + tableId2.value;
+          url2 = url2 + '&openType=' + (switchVal.value ? 1 : 0);
+          emit('update:modelValue', url2);
+          showDialog.value = false;
+        } else {
+          const url2 = '/addon/shop/pages/goods/detail?goodsId=' + tableId2.value;
+          emit('update:modelValue', url2);
+          showDialog.value = false;
+        }
       } else {
         ElMessage({
           message: '请选择一个商品',
@@ -765,6 +853,19 @@ const onConfirm = () => {
         });
       }
     }
+    if (boxpageKey.value == 'appnews') {
+      if (tableId7.value) {
+        const url7 = '/addon/shop_giftcard/pages/detail?id=' + tableId7.value;
+        emit('update:modelValue', url7);
+        showDialog.value = false;
+      } else {
+        ElMessage({
+          message: '请选择一个新闻咨询',
+          type: 'warning',
+          duration: 2000
+        });
+      }
+    }
   }
 };
 
@@ -798,6 +899,7 @@ defineExpose({
       display: flex;
       justify-content: space-between;
       align-items: center;
+
       .title {
         font-size: 14px;
         font-weight: 600;

+ 3 - 0
src/components/WebLinkInput/index.vue

@@ -22,6 +22,7 @@
       ref="linkSelectorRef"
       v-model:visible="selectorVisible"
       v-model="localValue"
+      :pageType="pageType"
       @confirm="handleLinkConfirm"
       @close="handleSelectorClose"
     />
@@ -38,6 +39,7 @@ interface Props {
   placeholder?: string;
   disabled?: boolean;
   inputStyle?: Record<string, any>;
+  pageType?: string;
 }
 
 // 定义事件
@@ -50,6 +52,7 @@ const props = withDefaults(defineProps<Props>(), {
   modelValue: '',
   placeholder: '请输入链接或点击选择',
   disabled: false,
+  pageType: '1',
   inputStyle: () => ({})
 });
 

+ 17 - 8
src/components/goodsMini/index.vue

@@ -159,10 +159,15 @@ const getList = async () => {
     let result = [];
     if (props.navIndex || props.navIndex == 0) {
       if (props.type == 'editManyGoodsList') {
-        result = tableData.value.filter((item: any) => diyStore.editComponent.list[props.navIndex].goods_ids.includes(item.id));
+        result = tableData.value.filter((item: any) => diyStore.editComponent.list[props.navIndex].goodsIds.includes(item.id));
       }
     } else {
-      result = tableData.value.filter((item: any) => diyStore.editComponent.goodsIds.includes(item.id));
+      //商品推荐
+      if (props.type == 'editShopGoodsRecommend') {
+        result = tableData.value.filter((item: any) => diyStore.editComponent.goods_ids.includes(item.id));
+      } else {
+        result = tableData.value.filter((item: any) => diyStore.editComponent.goodsIds.includes(item.id));
+      }
     }
     resultList.value = result;
     nextTick(() => {
@@ -180,14 +185,18 @@ const getList = async () => {
 const onConfirm = () => {
   if (props.navIndex || props.navIndex == 0) {
     if (props.type == 'editManyGoodsList') {
-      const newIds = calculateNewIds(diyStore.editComponent.list[props.navIndex].goods_ids, tableData.value, multipleSelection.value);
-      // console.log(newIds,'?????????????')
-      // return
-      diyStore.editComponent.list[props.navIndex].goods_ids = newIds;
+      const newIds = calculateNewIds(diyStore.editComponent.list[props.navIndex].goodsIds, tableData.value, multipleSelection.value);
+      diyStore.editComponent.list[props.navIndex].goodsIds = newIds;
     }
   } else {
-    const newIds = calculateNewIds(diyStore.editComponent.goodsIds, tableData.value, multipleSelection.value);
-    diyStore.editComponent.goodsIds = newIds;
+    //商品推荐
+    if (props.type == 'editShopGoodsRecommend') {
+      const newIds = calculateNewIds(diyStore.editComponent.goods_ids, tableData.value, multipleSelection.value);
+      diyStore.editComponent.goods_ids = newIds;
+    } else {
+      const newIds = calculateNewIds(diyStore.editComponent.goodsIds, tableData.value, multipleSelection.value);
+      diyStore.editComponent.goodsIds = newIds;
+    }
   }
   showDialog.value = false;
 };

+ 2 - 2
src/components/heat-map/index.vue

@@ -78,8 +78,8 @@ import { img, deepClone } from '@/utils/common';
 
 const prop = defineProps({
   modelValue: {
-    type: String,
-    default: ''
+    type: Object as () => Record<string, any>,
+    default: () => {}
   }
 });
 

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

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

+ 4 - 4
src/views/diy/components/edit-active-cube.vue

@@ -21,13 +21,13 @@
             <upload-image v-model="diyStore.editComponent.textImg" :limit="1" />
           </el-form-item>
           <el-form-item label="链接地址">
-            <diy-link v-model="diyStore.editComponent.textLink" />
+            <WebLinkInput :pageType="'2'" v-model="diyStore.editComponent.textLink" placeholder="请输入或选择链接" />
           </el-form-item>
           <el-form-item label="副标题">
             <el-input v-model.trim="diyStore.editComponent.subTitle.text" placeholder="请输入副标题" clearable maxlength="8" show-word-limit />
           </el-form-item>
           <el-form-item label="链接地址">
-            <diy-link v-model="diyStore.editComponent.subTitle.link" />
+            <WebLinkInput :pageType="'2'" v-model="diyStore.editComponent.subTitle.link" placeholder="请输入或选择链接" />
           </el-form-item>
         </el-form>
 
@@ -138,7 +138,7 @@
               </div>
 
               <el-form-item label="链接地址">
-                <diy-link v-model="item.link" />
+                <WebLinkInput :pageType="'2'" v-model="item.link" placeholder="请输入或选择链接" />
               </el-form-item>
 
               <div
@@ -590,7 +590,7 @@ const addItem = () => {
       endColor: '#FE1E00'
     },
     imageUrl: '',
-    link: { name: '' }
+    link: ''
   });
 };
 

+ 3 - 98
src/views/diy/components/edit-carousel-search.vue

@@ -55,7 +55,7 @@
             </div>
           </el-form-item>
           <el-form-item label="链接地址">
-            <WebLinkInput v-model="diyStore.editComponent.search.link" placeholder="请输入或选择链接" />
+            <WebLinkInput :pageType="'2'" v-model="diyStore.editComponent.search.link" placeholder="请输入或选择链接" />
           </el-form-item>
         </el-form>
 
@@ -120,101 +120,6 @@
       </div>
 
       <el-collapse v-model="activeNames" @change="handleChange" class="collapse-wrap">
-        <el-collapse-item title="选项卡设置" name="tab">
-          <div class="edit-attr-item-wrap">
-            <el-form label-width="100px" class="px-[10px]" @submit.prevent>
-              <el-form-item label="展示开关">
-                <el-switch v-model="diyStore.editComponent.tab.control" />
-              </el-form-item>
-
-              <p class="text-sm text-gray-400 mb-[10px]">鼠标拖拽可调整顺序</p>
-
-              <div ref="tabBoxRef">
-                <div
-                  v-for="(item, index) in diyStore.editComponent.tab.list"
-                  :key="item.id"
-                  class="item-wrap p-[10px] pb-0 relative border border-dashed border-gray-300 mb-[16px]"
-                >
-                  <el-form-item label="分类名称">
-                    <el-input v-model.trim="item.text" placeholder="请输入分类名称" clearable maxlength="4" show-word-limit />
-                  </el-form-item>
-
-                  <el-form-item label="数据来源">
-                    <el-input
-                      v-model.trim="item.diy_title"
-                      placeholder="请选择微页面"
-                      readonly
-                      class="select-diy-page-input"
-                      @click="diyPageShowDialogOpen(index)"
-                    >
-                      <template #suffix>
-                        <div @click.stop="tabClear(index)">
-                          <el-icon v-if="item.diy_title">
-                            <Close />
-                          </el-icon>
-                          <el-icon v-else>
-                            <ArrowRight />
-                          </el-icon>
-                        </div>
-                      </template>
-                    </el-input>
-                  </el-form-item>
-
-                  <div
-                    class="del absolute cursor-pointer z-[2] top-[-8px] right-[-8px]"
-                    v-show="diyStore.editComponent.tab.list.length > 1"
-                    @click="diyStore.editComponent.tab.list.splice(index, 1)"
-                  >
-                    <icon name="element CircleCloseFilled" color="#bbb" size="20px" />
-                  </div>
-                </div>
-                <el-button v-show="diyStore.editComponent.tab.list.length < 50" class="w-full" @click="addTabItem">添加一个选项卡</el-button>
-              </div>
-
-              <!-- 选择微页面弹出框 -->
-              <el-dialog
-                v-model="diyPageShowDialog"
-                title="选择微页面"
-                width="1000px"
-                :close-on-press-escape="true"
-                :destroy-on-close="true"
-                :close-on-click-modal="false"
-              >
-                <el-table
-                  :data="diyPageTable.data"
-                  ref="diyPageTableRef"
-                  size="large"
-                  v-loading="diyPageTable.loading"
-                  height="490px"
-                  @current-change="handleCurrentDiyPageChange"
-                  row-key="id"
-                  highlight-current-row
-                >
-                  <template #empty>
-                    <span>{{ !diyPageTable.loading ? '还没有安装应用' : '' }}</span>
-                  </template>
-                  <el-table-column prop="page_title" label="页面名称" min-width="120" />
-                  <el-table-column prop="addon_name" label="页面类型" min-width="80" />
-                  <el-table-column prop="type_name" label="所属应用" min-width="80" />
-                </el-table>
-                <div class="mt-[16px] flex justify-end">
-                  <el-pagination
-                    v-model:current-page="diyPageTable.page"
-                    v-model:page-size="diyPageTable.limit"
-                    layout="total, sizes, prev, pager, next, jumper"
-                    :total="diyPageTable.total"
-                    @size-change="loadDiyPageList"
-                    @current-change="loadDiyPageList"
-                  />
-                </div>
-                <div class="flex items-center justify-end mt-[15px]">
-                  <el-button type="primary" @click="saveDiyPageId">确认</el-button>
-                  <el-button @click="diyPageShowDialog = false">取消</el-button>
-                </div>
-              </el-dialog>
-            </el-form>
-          </div>
-        </el-collapse-item>
         <el-collapse-item title="轮播图设置" name="swiper">
           <el-form label-width="100px" class="px-[10px]">
             <el-form-item label="展示开关">
@@ -252,7 +157,7 @@
                 </div>
 
                 <el-form-item label="链接地址">
-                  <WebLinkInput v-model="item.link" placeholder="请输入或选择链接" />
+                  <WebLinkInput :pageType="'2'" v-model="item.link" placeholder="请输入或选择链接" />
                 </el-form-item>
               </div>
             </div>
@@ -664,7 +569,7 @@ const addImageAd = () => {
     imageUrl: '',
     imgWidth: 0,
     imgHeight: 0,
-    link: { name: '' }
+    link: ''
   });
 };
 

+ 1 - 1
src/views/diy/components/edit-float-btn.vue

@@ -61,7 +61,7 @@
               </div>
 
               <el-form-item label="链接地址">
-                <diy-link v-model="item.link" />
+                <WebLinkInput :pageType="'2'" v-model="item.link" placeholder="请输入或选择链接" />
               </el-form-item>
             </div>
           </div>

+ 0 - 273
src/views/diy/components/edit-goods-coupon.vue

@@ -1,273 +0,0 @@
-<template>
-  <div>
-    <!-- 内容 -->
-    <div class="content-wrap" v-show="diyStore.editTab == 'content'">
-      <div class="edit-attr-item-wrap">
-        <h3 class="mb-[10px]">风格选择</h3>
-        <el-form label-width="80px" class="px-[10px]">
-          <el-form-item label="风格选择" class="flex">
-            <span class="text-primary flex-1 cursor-pointer" @click="showCouponStyle">{{ diyStore.editComponent.styleName }}</span>
-            <el-icon @click="showCouponStyle" class="cursor-pointer">
-              <ArrowRight />
-            </el-icon>
-          </el-form-item>
-        </el-form>
-
-        <el-dialog v-model="showCouponDialog" title="风格选择" width="500px">
-          <div class="data-bos">
-            <template v-for="(item, index) in couponStyleList" :key="index">
-              <div
-                :class="{ 'border-primary': selectCouponStyle.value == item.value }"
-                @click="changeCouponStyle(item)"
-                class="data-list flex items-center justify-center overflow-hidden w-[200px] h-[100px] m-[6px] cursor-pointer border bg-gray-50"
-              >
-                <img :src="img(item.url)" />
-              </div>
-            </template>
-          </div>
-
-          <template #footer>
-            <span class="dialog-footer">
-              <el-button @click="showCouponDialog = false">取消</el-button>
-              <el-button type="primary" @click="confirmCouponStyle">确认</el-button>
-            </span>
-          </template>
-        </el-dialog>
-      </div>
-
-      <div class="edit-attr-item-wrap">
-        <h3 class="mb-[10px]">优惠券内容</h3>
-        <el-form label-width="90px" class="px-[10px]">
-          <el-form-item label="标题">
-            <el-input
-              v-model.trim="diyStore.editComponent.couponTitle"
-              clearable
-              :maxlength="diyStore.editComponent.style == 'style-3' ? 4 : 8"
-              show-word-limit
-            />
-          </el-form-item>
-
-          <el-form-item label="副标题">
-            <el-input
-              v-model.trim="diyStore.editComponent.couponSubTitle"
-              clearable
-              :maxlength="diyStore.editComponent.style == 'style-3' ? 7 : 10"
-              show-word-limit
-            />
-          </el-form-item>
-        </el-form>
-      </div>
-
-      <div class="edit-attr-item-wrap">
-        <h3 class="mb-[10px]">优惠券数据</h3>
-        <el-form label-width="90px" class="px-[10px]">
-          <el-form-item label="选择优惠券">
-            <el-radio-group v-model="diyStore.editComponent.source" title="选择商品">
-              <el-radio value="all">全部</el-radio>
-              <el-radio value="custom">手动选择</el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="手动选择" v-if="diyStore.editComponent.source == 'custom'">
-            <coupon-select-popup ref="couponSelectPopupRef" v-model="diyStore.editComponent.couponIds" :min="1" :max="20" />
-          </el-form-item>
-          <el-form-item label="优惠券数量" v-if="diyStore.editComponent.source == 'all'">
-            <el-slider show-input v-model="diyStore.editComponent.num" :min="1" :max="20" size="small" class="goods-coupon-slider" />
-          </el-form-item>
-          <el-form-item label="按钮文字" v-if="diyStore.editComponent.style != 'style-4'">
-            <el-input v-model.trim="diyStore.editComponent.btnText" clearable maxlength="5" show-word-limit />
-          </el-form-item>
-        </el-form>
-      </div>
-    </div>
-
-    <!-- 样式 -->
-    <div class="style-wrap" v-show="diyStore.editTab == 'style'">
-      <div class="edit-attr-item-wrap" v-if="diyStore.editComponent.style == 'style-4'">
-        <h3 class="mb-[10px]">标题样式</h3>
-        <el-form label-width="90px" class="px-[10px]">
-          <el-form-item label="标题颜色">
-            <el-color-picker v-model="diyStore.editComponent.titleColor" show-alpha :predefine="diyStore.predefineColors" />
-          </el-form-item>
-          <el-form-item label="副标题颜色">
-            <el-color-picker v-model="diyStore.editComponent.subTitleColor" show-alpha :predefine="diyStore.predefineColors" />
-          </el-form-item>
-        </el-form>
-      </div>
-
-      <div class="edit-attr-item-wrap" v-if="diyStore.editComponent.style == 'style-4'">
-        <h3 class="mb-[10px]">优惠券项样式</h3>
-        <el-form label-width="90px" class="px-[10px]">
-          <el-form-item label="金额颜色">
-            <el-color-picker v-model="diyStore.editComponent.couponItem.moneyColor" show-alpha :predefine="diyStore.predefineColors" />
-          </el-form-item>
-          <el-form-item label="标题颜色">
-            <el-color-picker v-model="diyStore.editComponent.couponItem.textColor" show-alpha :predefine="diyStore.predefineColors" />
-          </el-form-item>
-          <el-form-item label="副标题颜色">
-            <el-color-picker v-model="diyStore.editComponent.couponItem.subTextColor" show-alpha :predefine="diyStore.predefineColors" />
-          </el-form-item>
-          <el-form-item label="背景颜色">
-            <el-color-picker v-model="diyStore.editComponent.couponItem.bgColor" show-alpha :predefine="diyStore.predefineColors" />
-          </el-form-item>
-          <el-form-item label="圆角">
-            <el-slider v-model="diyStore.editComponent.couponItem.aroundRadius" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
-          </el-form-item>
-        </el-form>
-      </div>
-
-      <!-- 组件样式 -->
-      <slot name="style"></slot>
-    </div>
-  </div>
-</template>
-
-<script lang="ts" setup>
-import useDiyStore from '@/store/modules/diy';
-import { img } from '@/utils/common';
-// import couponSelectPopup from '@/addon/shop/views/goods/components/coupon-select-popup.vue';
-
-import style1 from '@/assets/images/diy/shop/style-1.png';
-import style2 from '@/assets/images/diy/shop/style-2.png';
-import style3 from '@/assets/images/diy/shop/style-3.png';
-import style4 from '@/assets/images/diy/shop/style-4.png';
-
-const diyStore = useDiyStore();
-diyStore.editComponent.ignore = ['componentBgColor', 'componentBgUrl']; // 忽略公共属性
-
-// 组件验证
-diyStore.editComponent.verify = (index: number) => {
-  const res = { code: true, message: '' };
-
-  if (diyStore.value[index].source == 'custom') {
-    if (diyStore.value[index].couponIds.length == 0) {
-      res.code = false;
-      res.message = '请选择优惠券';
-      return res;
-    }
-  }
-
-  if (diyStore.value[index].btnText == '') {
-    res.code = false;
-    res.message = '请输入按钮文字';
-    return res;
-  }
-
-  if (diyStore.value[index].couponTitle == '') {
-    res.code = false;
-    res.message = '请输入标题';
-    return res;
-  }
-
-  if (diyStore.value[index].couponSubTitle == '') {
-    res.code = false;
-    res.message = '请输入副标题';
-    return res;
-  }
-
-  return res;
-};
-
-const selectCouponStyle = reactive({
-  title: diyStore.editComponent.styleName,
-  value: diyStore.editComponent.style
-});
-
-// 风格样式
-const showCouponDialog = ref(false);
-
-const showCouponStyle = () => {
-  showCouponDialog.value = true;
-  selectCouponStyle.title = diyStore.editComponent.styleName;
-  selectCouponStyle.value = diyStore.editComponent.style;
-};
-
-const couponStyleList = reactive([
-  {
-    url: style1,
-    title: '风格1',
-    value: 'style-1'
-  },
-  {
-    url: style2,
-    title: '风格2',
-    value: 'style-2'
-  },
-  {
-    url: style3,
-    title: '风格3',
-    value: 'style-3'
-  },
-  {
-    url: style4,
-    title: '风格4',
-    value: 'style-4'
-  }
-]);
-
-const changeCouponStyle = (item: any) => {
-  selectCouponStyle.title = item.title;
-  selectCouponStyle.value = item.value;
-};
-
-const confirmCouponStyle = () => {
-  diyStore.editComponent.styleName = selectCouponStyle.title;
-  diyStore.editComponent.style = selectCouponStyle.value;
-  if (diyStore.editComponent.style == 'style-3') {
-    if (diyStore.editComponent.couponTitle && diyStore.editComponent.couponTitle.length > 4) {
-      diyStore.editComponent.couponTitle = diyStore.editComponent.couponTitle.substring(0, 4);
-    }
-    if (diyStore.editComponent.couponSubTitle && diyStore.editComponent.couponSubTitle.length > 7) {
-      diyStore.editComponent.couponSubTitle = diyStore.editComponent.couponSubTitle.substring(0, 7);
-    }
-  }
-  initStyleFn();
-  showCouponDialog.value = false;
-};
-
-const initStyleFn = () => {
-  const index = diyStore.editComponent.ignore.indexOf('componentBgColor');
-  if (diyStore.editComponent.style == 'style-4' && index != -1) {
-    diyStore.editComponent.ignore.splice(index, 1);
-    diyStore.editComponent.titleColor = '#ffffff';
-    diyStore.editComponent.subTitleColor = '#ffffff';
-
-    diyStore.editComponent.couponItem.moneyColor = '#fa191d';
-    diyStore.editComponent.couponItem.textColor = '#333333';
-    diyStore.editComponent.couponItem.subTextColor = '#999999';
-    diyStore.editComponent.couponItem.bgColor = '#ffffff';
-    diyStore.editComponent.couponItem.aroundRadius = 10;
-    diyStore.editComponent.componentStartBgColor = '#fa191d';
-  } else if (diyStore.editComponent.style != 'style-4' && index == -1) {
-    diyStore.editComponent.ignore.push('componentBgColor');
-  }
-};
-
-defineExpose({});
-</script>
-
-<style lang="scss" scoped></style>
-
-<style lang="scss">
-.goods-coupon-slider {
-  .el-slider__input {
-    width: 100px;
-  }
-}
-
-.data-bos {
-  display: flex;
-  flex-wrap: wrap;
-  width: 624px;
-  gap: 0 12px;
-  .data-list {
-    background-color: #f9fafb;
-    border: 1px solid #e5e7eb;
-    &.border-primary {
-      border-color: var(--el-color-primary);
-    }
-    img {
-      width: 100%;
-    }
-  }
-}
-</style>

+ 0 - 405
src/views/diy/components/edit-goods-list copy.vue

@@ -1,405 +0,0 @@
-<template>
-  <div>
-    <!-- 内容 -->
-    <div class="content-wrap" v-show="diyStore.editTab == 'content'">
-      <div class="edit-attr-item-wrap">
-        <h3 class="mb-[10px]">风格选择</h3>
-        <div class="flex items-center mb-[18px] rounded overflow-hidden">
-          <span
-            class="iconfont iconzuoyoutuwenpc border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
-            :class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-1' }"
-            @click="styleChangeFn('style-1')"
-          ></span>
-
-          <span
-            class="iconfont iconshangxiatuwenpc border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
-            :class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-2' }"
-            @click="styleChangeFn('style-2')"
-          ></span>
-          <span
-            class="iconfont iconliebiaopc border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
-            :class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-3' }"
-            @click="styleChangeFn('style-3')"
-          ></span>
-        </div>
-      </div>
-      <div class="edit-attr-item-wrap">
-        <h3 class="mb-[10px]">选择数据源</h3>
-        <el-form label-width="80px" class="px-[10px]">
-          <el-form-item label="排序">
-            <el-radio-group v-model="diyStore.editComponent.sortWay">
-              <el-radio value="default">综合</el-radio>
-              <el-radio value="sale_num">销量</el-radio>
-              <el-radio value="price">价格</el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="选择商品">
-            <el-radio-group v-model="diyStore.editComponent.source" title="选择商品">
-              <el-radio :value="1">指定商品</el-radio>
-              <el-radio :value="2">商品分类</el-radio>
-              <el-radio :value="3">商品品牌</el-radio>
-              <!-- <el-radio value="all">全部商品</el-radio>
-              <el-radio value="category">选择分类</el-radio>
-              <el-radio value="custom">手动选择</el-radio> -->
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="指定商品" v-if="diyStore.editComponent.source == 1">
-            <div class="data-num" @click="openDialog">
-              <!-- <span v-if="diyStore.editComponent.goodsIds.length == 0">请选择</span>
-              <span v-else>已选择{{ diyStore.editComponent.goodsIds.length }}个</span> -->
-              <el-icon><ArrowRight /></el-icon>
-            </div>
-          </el-form-item>
-          <el-form-item label="选择分类" v-if="diyStore.editComponent.source == 'category'">
-            <div class="flex items-center w-full">
-              <div class="cursor-pointer ml-auto" @click="categoryShowDialogOpen">
-                <span class="text-[var(--el-color-primary)]">{{ diyStore.editComponent.goods_category_name }}</span>
-                <span class="iconfont iconxiangyoujiantou"></span>
-              </div>
-            </div>
-          </el-form-item>
-          <el-form-item label="商品数量" v-if="diyStore.editComponent.source == 'all' || diyStore.editComponent.source == 'category'">
-            <el-slider class="goods-list-slider" show-input v-model="diyStore.editComponent.num" :min="1" :max="20" size="small" />
-          </el-form-item>
-          <!-- <el-form-item label="手动选择" v-if="diyStore.editComponent.source == 'custom'">
-            <goods-select-popup ref="goodsSelectPopupRef" v-model="diyStore.editComponent.goods_ids" :min="1" :max="99" />
-          </el-form-item> -->
-        </el-form>
-
-        <el-dialog v-model="categoryShowDialog" title="商品分类" width="750px" :destroy-on-close="true" :close-on-click-modal="false">
-          <el-table
-            :data="categoryTable.data"
-            ref="categoryTableRef"
-            size="large"
-            v-loading="categoryTable.loading"
-            height="450px"
-            @selection-change="handleSelectionChange"
-            row-key="category_id"
-            :expand-row-keys="expand_category_ids"
-            :tree-props="{ hasChildren: 'hasChildren', children: 'child_list' }"
-          >
-            <template #empty>
-              <span>{{ !categoryTable.loading ? '还没有安装应用' : '' }}</span>
-            </template>
-            <el-table-column type="selection" width="55" />
-            <el-table-column label="分类名称" min-width="120">
-              <template #default="{ row }">
-                <span class="order-2">{{ row.category_name }}</span>
-              </template>
-            </el-table-column>
-            <el-table-column label="分类图片" width="170" align="left">
-              <template #default="{ row }">
-                <div class="h-[30px]">
-                  <el-image class="w-[30px] h-[30px]" :src="img(row.image)" fit="contain">
-                    <template #error>
-                      <div class="image-slot">
-                        <!-- <img class="w-[30px] h-[30px]" src="@/addon/shop/assets/category_default.png" /> -->
-                      </div>
-                    </template>
-                  </el-image>
-                </div>
-              </template>
-            </el-table-column>
-          </el-table>
-          <div class="flex items-center justify-end mt-[15px]">
-            <el-button type="primary" @click="saveCategoryId">确认</el-button>
-            <el-button @click="categoryShowDialog = false">取消</el-button>
-          </div>
-        </el-dialog>
-      </div>
-
-      <div class="edit-attr-item-wrap">
-        <h3 class="mb-[10px]">购买按钮</h3>
-        <el-form label-width="90px" class="px-[10px]">
-          <el-form-item label="是否显示">
-            <el-switch v-model="diyStore.editComponent.btnStyle.control" />
-          </el-form-item>
-          <el-form-item label="点击事件" v-if="diyStore.editComponent.btnStyle.control">
-            <el-radio-group v-model="diyStore.editComponent.btnStyle.cartEvent">
-              <el-radio value="detail">商品详情</el-radio>
-              <el-radio v-if="diyStore.editComponent.style != 'style-3'" value="cart">加入购物车</el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="样式" class="!items-center" v-if="diyStore.editComponent.btnStyle.control">
-            <div class="flex">
-              <template v-for="(item, index) in btnStyleList" :key="index">
-                <div
-                  v-if="item.isShow == true"
-                  class="cursor-pointer flex items-center justify-center border-[1px] border-solid border-transparent rounded-[6px] py-[5px] px-[8px] mr-[10px]"
-                  :class="{ '!border-[var(--el-color-primary)]': diyStore.editComponent.btnStyle.style == item.value }"
-                >
-                  <div
-                    v-if="item.type == 'icon'"
-                    :class="['nc-iconfont !text-[25px] text-[var(--el-color-primary)]', item.title]"
-                    @click="changeBtnStyle(item)"
-                  ></div>
-                  <div
-                    v-if="item.type == 'button'"
-                    class="leading-[1] text-[12px] px-[10px] py-[8px] text-[#fff] rounded-[20px] bg-[var(--el-color-primary)]"
-                    @click="changeBtnStyle(item)"
-                  >
-                    {{ item.title }}
-                  </div>
-                </div>
-              </template>
-            </div>
-          </el-form-item>
-          <el-form-item label="文本" v-if="diyStore.editComponent.btnStyle.control && diyStore.editComponent.btnStyle.style == 'button'">
-            <el-input v-model.trim="diyStore.editComponent.btnStyle.text" placeholder="请输入按钮文字" clearable maxlength="4" show-word-limit />
-          </el-form-item>
-        </el-form>
-      </div>
-
-      <div class="edit-attr-item-wrap">
-        <h3 class="mb-[10px]">显示内容</h3>
-        <el-form label-width="90px" class="px-[10px]">
-          <el-form-item label="商品名称" v-if="diyStore.editComponent.goodsNameStyle.isShow">
-            <el-switch v-model="diyStore.editComponent.goodsNameStyle.control" />
-          </el-form-item>
-          <el-form-item label="销售价" v-if="diyStore.editComponent.priceStyle.isShow">
-            <el-switch v-model="diyStore.editComponent.priceStyle.control" />
-          </el-form-item>
-          <el-form-item label="商品销量" v-if="diyStore.editComponent.saleStyle.isShow">
-            <el-switch v-model="diyStore.editComponent.saleStyle.control" />
-          </el-form-item>
-          <el-form-item label="商品标签" v-if="diyStore.editComponent.labelStyle.isShow">
-            <el-switch v-model="diyStore.editComponent.labelStyle.control" />
-          </el-form-item>
-        </el-form>
-      </div>
-    </div>
-
-    <!-- 样式 -->
-    <div class="style-wrap" v-show="diyStore.editTab == 'style'">
-      <div class="edit-attr-item-wrap">
-        <h3 class="mb-[10px]">商品样式</h3>
-        <el-form label-width="80px" class="px-[10px]">
-          <el-form-item label="商品背景">
-            <el-color-picker v-model="diyStore.editComponent.elementBgColor" show-alpha :predefine="diyStore.predefineColors" />
-          </el-form-item>
-          <el-form-item label="商品名称">
-            <el-color-picker v-model="diyStore.editComponent.goodsNameStyle.color" show-alpha :predefine="diyStore.predefineColors" />
-            <div class="mr-[20px]"></div>
-            <el-radio-group v-model="diyStore.editComponent.goodsNameStyle.fontWeight">
-              <el-radio :value="'normal'">常规</el-radio>
-              <el-radio :value="'bold'">加粗</el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="图片圆角">
-            <el-slider v-model="diyStore.editComponent.imgElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
-          </el-form-item>
-          <el-form-item label="销售价">
-            <el-color-picker v-model="diyStore.editComponent.priceStyle.color" show-alpha :predefine="diyStore.predefineColors" />
-          </el-form-item>
-          <el-form-item label="商品销量">
-            <el-color-picker v-model="diyStore.editComponent.saleStyle.color" show-alpha :predefine="diyStore.predefineColors" />
-          </el-form-item>
-          <el-form-item label="上圆角">
-            <el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
-          </el-form-item>
-          <el-form-item label="下圆角">
-            <el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
-          </el-form-item>
-        </el-form>
-      </div>
-
-      <div class="edit-attr-item-wrap" v-if="diyStore.editComponent.btnStyle.control">
-        <h3 class="mb-[10px]">购买按钮</h3>
-        <el-form label-width="80px" class="px-[10px]">
-          <el-form-item label="是否加粗" v-if="diyStore.editComponent.btnStyle.style == 'button'">
-            <el-switch v-model="diyStore.editComponent.btnStyle.fontWeight" />
-          </el-form-item>
-          <el-form-item label="文字颜色">
-            <el-color-picker v-model="diyStore.editComponent.btnStyle.textColor" show-alpha :predefine="diyStore.predefineColors" />
-          </el-form-item>
-          <el-form-item label="背景颜色">
-            <el-color-picker v-model="diyStore.editComponent.btnStyle.startBgColor" show-alpha :predefine="diyStore.predefineColors" />
-            <icon name="iconfont iconmap-connect" size="20px" class="block !text-gray-400 mx-[5px]" />
-            <el-color-picker v-model="diyStore.editComponent.btnStyle.endBgColor" show-alpha :predefine="diyStore.predefineColors" />
-          </el-form-item>
-          <el-form-item label="圆角" v-if="diyStore.editComponent.btnStyle.style == 'button'">
-            <el-slider v-model="diyStore.editComponent.btnStyle.aroundRadius" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
-          </el-form-item>
-        </el-form>
-      </div>
-
-      <!-- 组件样式 -->
-      <slot name="style"></slot>
-    </div>
-
-    <goods-dialog ref="goodsDialogRef" :categoryOptions="categoryOptions"></goods-dialog>
-  </div>
-</template>
-
-<script lang="ts" setup>
-// import { getCategoryTree } from '@/addon/shop/api/goods';
-import { img } from '@/utils/common';
-import useDiyStore from '@/store/modules/diy';
-import { ElTable } from 'element-plus';
-// import goodsSelectPopup from '@/addon/shop/views/goods/components/goods-select-popup.vue';
-const categoryOptions = ref<any>([]);
-const goodsDialogRef = ref<any>(null);
-
-const diyStore: any = useDiyStore();
-diyStore.editComponent.ignore = []; // 忽略公共属性
-
-// 组件验证
-diyStore.editComponent.verify = (index: number) => {
-  const res = { code: true, message: '' };
-
-  if (diyStore.value[index].source == 'category') {
-    if (diyStore.value[index].goods_category == '') {
-      res.code = false;
-      res.message = '请选择商品分类';
-    }
-  } else if (diyStore.value[index].source == 'custom') {
-    if (diyStore.value[index].goods_ids.length == 0) {
-      res.code = false;
-      res.message = '请选择商品';
-    }
-  }
-  return res;
-};
-
-const categoryShowDialog = ref(false);
-
-const categoryTable = reactive({
-  loading: true,
-  data: []
-});
-onMounted(() => {
-  loadCategoryList();
-  btnStyleList.forEach((item, index, arr) => {
-    if (item.type == 'button') {
-      if (diyStore.editComponent.style == 'style-3') {
-        item.isShow = false;
-      } else {
-        item.isShow = true;
-      }
-    }
-  });
-});
-
-const styleChangeFn = (style) => {
-  btnStyleList.forEach((item, index, arr) => {
-    if (item.type == 'button') {
-      if (style == 'style-3') {
-        item.isShow = false;
-      } else {
-        item.isShow = true;
-      }
-    }
-  });
-
-  if (style == 'style-3') {
-    diyStore.editComponent.btnStyle.style = btnStyleList[1].value;
-    diyStore.editComponent.btnStyle.cartEvent = 'detail';
-
-    diyStore.editComponent.saleStyle.isShow = false;
-    diyStore.editComponent.labelStyle.isShow = false;
-  } else {
-    diyStore.editComponent.btnStyle.style = btnStyleList[0].value;
-
-    diyStore.editComponent.saleStyle.isShow = true;
-    diyStore.editComponent.labelStyle.isShow = true;
-  }
-  diyStore.editComponent.style = style;
-};
-
-const btnStyleList = reactive([
-  {
-    isShow: true,
-    type: 'button',
-    title: diyStore.editComponent.btnStyle.text,
-    value: 'button'
-  },
-  {
-    isShow: true,
-    type: 'icon',
-    title: 'nc-icon-jiahaoV6xx',
-    value: 'nc-icon-jiahaoV6xx'
-  },
-  {
-    isShow: true,
-    type: 'icon',
-    title: 'nc-icon-gouwuche1',
-    value: 'nc-icon-gouwuche1'
-  }
-]);
-
-const changeBtnStyle = (item: any) => {
-  diyStore.editComponent.btnStyle.style = item.value;
-};
-
-const categoryTableRef = ref<InstanceType<typeof ElTable>>();
-/**
- * 获取商品分类列表
- */
-let currCategoryData: any = null;
-const loadCategoryList = () => {
-  categoryTable.loading = true;
-
-  // getCategoryTree()
-  //   .then((res) => {
-  //     categoryTable.loading = false;
-  //     categoryTable.data = res.data;
-  //   })
-  //   .catch(() => {
-  //     categoryTable.loading = false;
-  //   });
-};
-
-// 选择商品分类
-const handleSelectionChange = (val: string | any[]) => {
-  let data = '';
-  if (val) data = val[val.length - 1];
-  if (val.length > 1) categoryTableRef.value!.clearSelection();
-  if (data) categoryTableRef.value!.toggleRowSelection(data, true);
-  currCategoryData = data;
-};
-
-const saveCategoryId = () => {
-  diyStore.editComponent.goods_category = currCategoryData.category_id;
-  diyStore.editComponent.goods_category_name = currCategoryData.category_name;
-  categoryShowDialog.value = false;
-};
-
-const categoryShowDialogOpen = () => {
-  categoryShowDialog.value = true;
-  nextTick(() => {
-    setRowSelection();
-  });
-};
-
-// 分类数据选中回填,设置展开行
-const expand_category_ids = ref<Array<any>>([]);
-const setRowSelection = () => {
-  expand_category_ids.value = [];
-  categoryTable.data.forEach((el: any) => {
-    if (diyStore.editComponent.goods_category == el.category_id) {
-      categoryTableRef.value!.toggleRowSelection(el, true);
-    } else if (el.child_list && el.child_list.length) {
-      el.child_list.forEach((v: any) => {
-        if (diyStore.editComponent.goods_category == v.category_id) {
-          expand_category_ids.value.push(el.category_id.toString());
-          categoryTableRef.value!.toggleRowSelection(v, true);
-        }
-      });
-    }
-  });
-};
-
-//打开弹窗
-const openDialog = () => {
-  goodsDialogRef.value.onOpen();
-};
-
-defineExpose({});
-</script>
-<style lang="scss">
-.goods-list-slider {
-  .el-slider__input {
-    width: 100px;
-  }
-}
-</style>

+ 7 - 0
src/views/diy/components/edit-goods-list.vue

@@ -227,6 +227,13 @@ const getCategoryTree = async () => {
   const list = res.data || [];
   categoryOptions.value = [...list];
   categoryOptions1.value = [...list];
+  categoryOptions1.value.forEach((item1: any) => {
+    if (item1.children && item1.children.length > 0) {
+      item1.children.forEach((item2: any) => {
+        item2.children = [];
+      });
+    }
+  });
   categoryOptions.value.unshift({
     id: '',
     label: '全部'

+ 2 - 2
src/views/diy/components/edit-graphic-nav.vue

@@ -74,7 +74,7 @@
               </div>
 
               <el-form-item label="链接地址">
-                <WebLinkInput v-model="item.link" placeholder="请输入或选择链接" />
+                <WebLinkInput :pageType="'2'" v-model="item.link" placeholder="请输入或选择链接" />
               </el-form-item>
             </div>
           </div>
@@ -200,7 +200,7 @@ const addGraphicNav = () => {
     imageUrl: '',
     imgWidth: 0,
     imgHeight: 0,
-    link: { name: '' },
+    link: '',
     label: {
       control: false,
       text: '热门',

+ 1 - 1
src/views/diy/components/edit-horz-blank.vue

@@ -1,6 +1,6 @@
 <template>
   <div>
-    <!-- 内容 -->
+    <!-- 内容  -->
     <div class="content-wrap" v-show="diyStore.editTab == 'content'">
       <div class="edit-attr-item-wrap">
         <h3 class="mb-[10px]">高度设置</h3>

+ 1 - 1
src/views/diy/components/edit-horz-line.vue

@@ -1,6 +1,6 @@
 <template>
   <div>
-    <!-- 内容 -->
+    <!-- 内容  -->
     <div class="content-wrap" v-show="diyStore.editTab == 'content'">
       <div class="edit-attr-item-wrap">
         <h3 class="mb-[10px]">线条风格</h3>

+ 1 - 1
src/views/diy/components/edit-hot-area.vue

@@ -12,7 +12,7 @@
               </el-form-item>
 
               <el-form-item label="热区设置">
-                <heat-map v-model="diyStore.editComponent" />
+                <heat-mapMini v-model="diyStore.editComponent" />
               </el-form-item>
             </div>
           </div>

+ 2 - 2
src/views/diy/components/edit-image-ads.vue

@@ -38,7 +38,7 @@
               </div>
 
               <el-form-item label="链接地址">
-                <diy-link v-model="item.link" />
+                <WebLinkInput :pageType="'2'" v-model="item.link" placeholder="请输入或选择链接" />
               </el-form-item>
             </div>
           </div>
@@ -108,7 +108,7 @@ const addImageAd = () => {
     imageUrl: '',
     imgWidth: 0,
     imgHeight: 0,
-    link: { name: '' }
+    link: ''
   });
 };
 

+ 52 - 501
src/views/diy/components/edit-many-goods-list.vue

@@ -53,26 +53,28 @@
                 </el-form-item>
                 <el-form-item label="选择商品">
                   <el-radio-group v-model="item.source">
-                    <el-radio value="custom">手动选择</el-radio>
-                    <el-radio value="category">选择分类</el-radio>
-                    <el-radio value="brand">选择品牌</el-radio>
+                    <el-radio :value="1">指定商品</el-radio>
+                    <el-radio :value="2">商品分类</el-radio>
                   </el-radio-group>
                 </el-form-item>
-                <el-form-item label="选择分类" v-if="item.source == 'category'">
-                  <div class="flex items-center w-full">
-                    <div class="cursor-pointer ml-auto" @click="categoryShowDialogOpen(index)">
-                      <span class="text-[var(--el-color-primary)]">{{ item.goods_category_name }}</span>
-                      <span class="iconfont iconxiangyoujiantou"></span>
-                    </div>
-                  </div>
-                </el-form-item>
-                <el-form-item label="手动选择" v-if="item.source == 'custom'">
+                <el-form-item label="指定商品" v-if="item.source == 1">
                   <div class="data-num" @click="openDialog(index)">
-                    <span v-if="item.goods_ids.length == 0">请选择</span>
-                    <span v-else>已选择{{ item.goods_ids.length }}个</span>
-                    <el-icon><ArrowRight /></el-icon>
+                    <span v-if="item.goodsIds.length == 0">请选择</span>
+                    <span v-else>已选择{{ item.goodsIds.length }}个</span>
+                    <el-icon>
+                      <ArrowRight />
+                    </el-icon>
                   </div>
-                  <!-- <goods-select-popup ref="goodsSelectPopupRef" v-model="item.goods_ids" :min="1" :max="99" /> -->
+                </el-form-item>
+                <el-form-item label="选择分类" v-if="item.source == 2">
+                  <el-cascader
+                    v-model="item.categoryIds"
+                    :options="categoryOptions1"
+                    :props="cascaderProps"
+                    placeholder="全部分类"
+                    clearable
+                    collapse-tags
+                  />
                 </el-form-item>
                 <el-form-item label="图片上传" v-show="diyStore.editComponent.headStyle == 'style-3'">
                   <upload-image v-model="item.imageUrl" :limit="1" />
@@ -91,208 +93,8 @@
             <el-button class="w-full" @click="addItem">添加一个多商品组</el-button>
           </div>
         </el-form>
-
-        <!-- 选择一级商品分类弹出框 -->
-        <el-dialog v-model="firstCategoryShowDialog" title="商品分类" width="750px" :destroy-on-close="true" :close-on-click-modal="false">
-          <el-table
-            :data="firstCategoryTable.data"
-            ref="firstCategoryTableRef"
-            size="large"
-            v-loading="firstCategoryTable.loading"
-            height="450px"
-            @current-change="handleCurrentCategoryChange"
-            row-key="category_id"
-            highlight-current-row
-          >
-            <template #empty>
-              <span>{{ !firstCategoryTable.loading ? '还没有安装应用' : '' }}</span>
-            </template>
-            <el-table-column label="分类名称" min-width="120">
-              <template #default="{ row }">
-                <span class="order-2">{{ row.category_name }}</span>
-              </template>
-            </el-table-column>
-            <el-table-column label="分类图片" width="170" align="left">
-              <template #default="{ row }">
-                <div class="h-[30px]">
-                  <el-image class="w-[30px] h-[30px]" :src="img(row.image)" fit="contain">
-                    <template #error>
-                      <div class="image-slot">
-                        <img class="w-[30px] h-[30px]" src="@/assets/images/diy/shop/category_default.png" />
-                      </div>
-                    </template>
-                  </el-image>
-                </div>
-              </template>
-            </el-table-column>
-          </el-table>
-          <div class="flex items-center justify-end mt-[15px]">
-            <el-button type="primary" @click="saveFirstCategoryId">确认</el-button>
-            <el-button @click="firstCategoryShowDialog = false">取消</el-button>
-          </div>
-        </el-dialog>
-
-        <el-dialog v-model="categoryShowDialog" title="商品分类" width="750px" :destroy-on-close="true" :close-on-click-modal="false">
-          <div class="table w-[100%] mt-[15px]" v-loading="categoryTable.loading">
-            <div class="table-head flex items-center bg-[#f5f7f9] py-[10px] text-[14px]" :style="{ paddingRight: scrollBarWidth + 'px' }">
-              <div class="w-[6%]"></div>
-              <div class="w-[10%]">
-                <!-- <el-checkbox v-model="staircheckAll" :indeterminate="isStairIndeterminate" @change="handleCheckAllChange" /> -->
-              </div>
-              <div class="w-[50%]">分类名称</div>
-              <div class="w-[34%] h-[30px] leading-[30px]">分类图片</div>
-            </div>
-            <div class="table-body max-h-[500px] overflow-y-auto" ref="tableBodyRef">
-              <!-- 遍历一级分类 -->
-              <div v-for="(row, rowIndex) in categoryTable.data" :key="rowIndex" class="flex flex-col">
-                <div class="flex items-center border-solid border-[#e5e7eb] py-[10px] border-b-[1px]">
-                  <!-- 图标:展开/收起子级 -->
-                  <div
-                    v-if="row.child_list && row.child_list.length"
-                    class="w-[6%] cursor-pointer text-center !text-[10px]"
-                    @click="secondLevelArrowChange(row)"
-                    :class="{ 'iconfont iconxiangyoujiantou': row.child_list.length, 'arrow-show': row.isShow }"
-                  ></div>
-                  <div v-else class="w-[6%]"></div>
-                  <!-- 一级分类复选框 -->
-                  <div class="w-[10%]">
-                    <el-checkbox
-                      v-model="row.secondLevelCheckAll"
-                      :indeterminate="row.isSecondLevelIndeterminate"
-                      @change="handleCheckboxChange($event, row)"
-                    />
-                  </div>
-                  <!-- 一级分类名称 -->
-                  <div class="ml-2 flex flex-col items-start w-[50%]">
-                    <span :title="row.category_name" class="multi-hidden leading-[1.4] mr-5 text-[14px] text-[#666]">
-                      {{ row.category_name }}
-                    </span>
-                  </div>
-                  <!-- 一级分类图片 -->
-                  <div class="flex items-center cursor-pointer w-[34%]">
-                    <div class="min-w-[30px] h-[30px] flex items-center justify-center">
-                      <el-image v-if="row.img" class="w-[30px] h-[30px]" :src="img(row.img)" fit="contain">
-                        <template #error>
-                          <div class="image-slot">
-                            <img class="w-[30px] h-[30px]" src="@/assets/images/diy/shop/category_default.png" />
-                          </div>
-                        </template>
-                      </el-image>
-                      <img v-else class="w-[30px] h-[30px]" src="@/assets/images/diy/shop/category_default.png" fit="contain" />
-                    </div>
-                  </div>
-                </div>
-                <!-- 子级分类 -->
-                <div v-show="row.child_list && row.isShow">
-                  <div
-                    v-for="(item, index) in row.child_list"
-                    :key="index"
-                    class="flex items-center py-[10px] border-solid border-b-[1px]"
-                    :class="{ 'hidden': !row.isShow, 'border-[#e5e7eb]': index == row.child_list.length - 1 }"
-                  >
-                    <div class="w-[9%]"></div>
-                    <!-- 子级分类复选框 -->
-                    <div class="w-[7%]">
-                      <el-checkbox v-model="item.threeLevelCheckAll" @change="handleCheckboxChange($event, item, row)" />
-                    </div>
-                    <!-- 子级分类名称 -->
-                    <div class="ml-2 flex flex-col items-start w-[50%]">
-                      <span :title="item.category_name" class="multi-hidden leading-[1.4] mr-5 text-[14px] text-[#666]">
-                        {{ item.category_name }}
-                      </span>
-                    </div>
-                    <!-- 子级分类图片 -->
-                    <div class="flex items-center cursor-pointer w-[34%]">
-                      <div class="min-w-[30px] h-[30px] flex items-center justify-center">
-                        <el-image v-if="row.img" class="w-[30px] h-[30px]" :src="img(row.img)" fit="contain">
-                          <template #error>
-                            <div class="image-slot">
-                              <img class="w-[30px] h-[30px]" src="@/assets/images/diy/shop/category_default.png" />
-                            </div>
-                          </template>
-                        </el-image>
-                        <img v-else class="w-[30px] h-[30px]" src="@/assets/images/diy/shop/category_default.png" fit="contain" />
-                      </div>
-                    </div>
-                  </div>
-                </div>
-              </div>
-              <div
-                v-if="!categoryTable.data.length && !categoryTable.loading"
-                class="h-[60px] flex items-center justify-center border-solid border-[#e5e7eb] py-[12px] border-b-[1px]"
-              >
-                还没有安装应用
-              </div>
-            </div>
-          </div>
-          <div class="flex items-center justify-end mt-[15px]">
-            <el-button type="primary" @click="saveCategoryId">确认</el-button>
-            <el-button @click="categoryShowDialog = false">取消</el-button>
-          </div>
-        </el-dialog>
-      </div>
-
-      <div class="edit-attr-item-wrap mt-[20px]">
-        <h3 class="mb-[10px]">购买按钮</h3>
-        <el-form label-width="90px" class="px-[10px]">
-          <el-form-item label="是否显示">
-            <el-switch v-model="diyStore.editComponent.btnStyle.control" />
-          </el-form-item>
-          <el-form-item label="点击事件" v-if="diyStore.editComponent.btnStyle.control">
-            <el-radio-group v-model="diyStore.editComponent.btnStyle.cartEvent">
-              <el-radio value="detail">商品详情</el-radio>
-              <el-radio value="cart">加入购物车</el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="样式" class="!items-center" v-if="diyStore.editComponent.btnStyle.control">
-            <div class="flex">
-              <template v-for="(item, index) in btnStyleList" :key="index">
-                <div
-                  v-if="item.isShow == true"
-                  class="cursor-pointer flex items-center justify-center border-[1px] border-solid border-transparent rounded-[6px] py-[5px] px-[8px] mr-[10px]"
-                  :class="{ '!border-[var(--el-color-primary)]': diyStore.editComponent.btnStyle.style == item.value }"
-                >
-                  <div
-                    v-if="item.type == 'icon'"
-                    :class="['nc-iconfont !text-[25px] text-[var(--el-color-primary)]', item.title]"
-                    @click="changeBtnStyle(item)"
-                  ></div>
-                  <div
-                    v-if="item.type == 'button'"
-                    class="leading-[1] text-[12px] px-[10px] py-[8px] text-[#fff] rounded-[20px] bg-[var(--el-color-primary)]"
-                    @click="changeBtnStyle(item)"
-                  >
-                    {{ item.title }}
-                  </div>
-                </div>
-              </template>
-            </div>
-          </el-form-item>
-          <el-form-item label="文本" v-if="diyStore.editComponent.btnStyle.control && diyStore.editComponent.btnStyle.style == 'button'">
-            <el-input v-model.trim="diyStore.editComponent.btnStyle.text" placeholder="请输入按钮文字" clearable maxlength="4" show-word-limit />
-          </el-form-item>
-        </el-form>
-      </div>
-
-      <div class="edit-attr-item-wrap">
-        <h3 class="mb-[10px]">显示内容</h3>
-        <el-form label-width="90px" class="px-[10px]">
-          <el-form-item label="商品名称" v-if="diyStore.editComponent.goodsNameStyle.isShow">
-            <el-switch v-model="diyStore.editComponent.goodsNameStyle.control" />
-          </el-form-item>
-          <el-form-item label="销售价" v-if="diyStore.editComponent.priceStyle.isShow">
-            <el-switch v-model="diyStore.editComponent.priceStyle.control" />
-          </el-form-item>
-          <el-form-item label="商品销量" v-if="diyStore.editComponent.saleStyle.isShow">
-            <el-switch v-model="diyStore.editComponent.saleStyle.control" />
-          </el-form-item>
-          <el-form-item label="商品标签" v-if="diyStore.editComponent.labelStyle.isShow">
-            <el-switch v-model="diyStore.editComponent.labelStyle.control" />
-          </el-form-item>
-        </el-form>
       </div>
     </div>
-
     <!-- 样式 -->
     <div class="style-wrap" v-show="diyStore.editTab == 'style'">
       <div class="edit-attr-item-wrap">
@@ -359,165 +161,15 @@ import { categoryTree } from '@/api/pmsProduct/base';
 // import goodsSelectPopup from '@/addon/shop/views/goods/components/goods-select-popup.vue';
 const goodsDialogRef = ref<any>(null);
 const categoryOptions = ref<any>([]);
+const categoryOptions1 = ref<any>([]);
 const navIndex = ref<any>(0);
-
+const goodsBoxRef = ref();
 const diyStore: any = useDiyStore();
 diyStore.editComponent.ignore = ['componentBgUrl']; // 忽略公共属性
-
-// 组件验证
-diyStore.editComponent.verify = (index: number) => {
-  const res = { code: true, message: '' };
-
-  if (diyStore.value[index].source == 'custom') {
-    diyStore.value[index].list.forEach((item: any) => {
-      if (item.source === 'category') {
-        if (item.goods_category == '') {
-          res.code = false;
-          res.message = '请选择商品分类';
-          return res;
-        }
-      } else if (item.source == 'custom') {
-        if (item.goods_ids.length == 0) {
-          res.code = false;
-          res.message = '请选择商品';
-        }
-      }
-    });
-  } else if (diyStore.value[index].source == 'goods_category') {
-    if (diyStore.value[index].goods_category == '') {
-      res.code = false;
-      res.message = '请选择商品分类';
-      return res;
-    }
-  }
-
-  return res;
-};
-
-diyStore.editComponent.list.forEach((item: any) => {
-  if (!item.id) item.id = diyStore.generateRandom();
-});
-
-//打开弹窗
-const openDialog = (res: any) => {
-  navIndex.value = res;
-  goodsDialogRef.value.onOpen();
-};
-
-/** 查询分类树 */
-const getCategoryTree = async () => {
-  categoryOptions.value = [];
-  const res = await categoryTree({
-    platform: diyStore.type == 1 ? 0 : diyStore.type == 2 ? 1 : diyStore.type == 3 ? 2 : diyStore.type == 4 ? 4 : 0,
-    pageNum: 1,
-    pageSize: 9999
-  });
-  const list = res.data || [];
-  categoryOptions.value = [...list];
-  categoryOptions.value.unshift({
-    id: '',
-    label: '全部'
-  });
-};
-
-// 一级商品分类
-const firstCategoryShowDialog = ref(false);
-
-const firstCategoryTable = reactive({
-  loading: true,
-  data: [],
-  searchParam: {
-    level: 1
-  }
-});
-
-const firstCategoryTableRef = ref<InstanceType<typeof ElTable>>();
-
-/**
- * 获取商品分类列表
- */
-const loadCategoryList = () => {
-  firstCategoryTable.loading = true;
-
-  // getCategoryList({
-  //   ...firstCategoryTable.searchParam
-  // })
-  //   .then((res) => {
-  //     firstCategoryTable.loading = false;
-  //     firstCategoryTable.data = res.data;
-  //   })
-  //   .catch(() => {
-  //     firstCategoryTable.loading = false;
-  //   });
-};
-
-const saveFirstCategoryId = () => {
-  diyStore.editComponent.goods_category = currFirstCategory.category_id;
-  diyStore.editComponent.goods_category_name = currFirstCategory.category_name;
-  firstCategoryShowDialog.value = false;
-};
-
-const firstCategoryShowDialogOpen = () => {
-  firstCategoryShowDialog.value = true;
-  if (currFirstCategory) {
-    setTimeout(() => {
-      firstCategoryTableRef.value!.setCurrentRow(currFirstCategory);
-    }, 200);
-  }
-};
-
-const btnStyleList = reactive([
-  {
-    isShow: true,
-    type: 'button',
-    title: diyStore.editComponent.btnStyle.text,
-    value: 'button'
-  },
-  {
-    isShow: true,
-    type: 'icon',
-    title: 'nc-icon-jiahaoV6xx',
-    value: 'nc-icon-jiahaoV6xx'
-  },
-  {
-    isShow: true,
-    type: 'icon',
-    title: 'nc-icon-gouwuche1',
-    value: 'nc-icon-gouwuche1'
-  }
-]);
-diyStore.editComponent.btnStyle.style = 'nc-icon-jiahaoV6xx';
-
-const changeBtnStyle = (item: any) => {
-  diyStore.editComponent.btnStyle.style = item.value;
-};
-
-const clearCategory = () => {
-  diyStore.editComponent.goods_category = '';
-  diyStore.editComponent.goods_category_name = '';
-};
-
-// 选择商品分类
-let currFirstCategory: any = {};
-const handleCurrentCategoryChange = (val: string | any[]) => {
-  currFirstCategory = val;
-};
-
-// 商品分类树结构
-const categoryShowDialog = ref(false);
-
-const goodsBoxRef = ref();
-
-const categoryTable = reactive({
-  loading: true,
-  data: []
-});
+const cascaderProps = { multiple: true, value: 'id', label: 'label', children: 'children' };
 
 onMounted(() => {
   getCategoryTree();
-  loadCategoryTree();
-
-  loadCategoryList();
 
   nextTick(() => {
     const sortable = Sortable.create(goodsBoxRef.value, {
@@ -538,34 +190,14 @@ onMounted(() => {
   window.addEventListener('resize', getScrollBarWidth);
 });
 
-/**
- * 获取商品分类列表
- */
-// let currCategoryData: any = null
-const loadCategoryTree = () => {
-  categoryTable.loading = true;
-
-  // getCategoryTree()
-  //   .then((res) => {
-  //     categoryTable.loading = false;
-  //     categoryTable.data = res.data;
-  //     categoryTable.data.forEach((item: any) => {
-  //       // 初始化一级分类的字段
-  //       item.isShow = false; // 控制子级是否展开
-  //       item.isSecondLevelIndeterminate = false; // 一级分类不确定状态
-  //       item.secondLevelCheckAll = false; // 一级分类复选框状态
-
-  //       // 如果有子分类(child_list),初始化子分类的字段
-  //       if (item.child_list && item.child_list.length) {
-  //         item.child_list.forEach((childItem: any) => {
-  //           childItem.threeLevelCheckAll = false; // 子分类复选框状态
-  //         });
-  //       }
-  //     });
-  //   })
-  //   .catch(() => {
-  //     categoryTable.loading = false;
-  //   });
+const scrollBarWidth = ref(0);
+const tableBodyRef = ref(null);
+const getScrollBarWidth = () => {
+  nextTick(() => {
+    if (tableBodyRef.value) {
+      scrollBarWidth.value = tableBodyRef.value.offsetWidth - tableBodyRef.value.clientWidth;
+    }
+  });
 };
 
 const addItem = () => {
@@ -581,118 +213,36 @@ const addItem = () => {
   });
 };
 
-const scrollBarWidth = ref(0);
-const tableBodyRef = ref(null);
-
-const getScrollBarWidth = () => {
-  nextTick(() => {
-    if (tableBodyRef.value) {
-      scrollBarWidth.value = tableBodyRef.value.offsetWidth - tableBodyRef.value.clientWidth;
-    }
+/** 查询分类树 */
+const getCategoryTree = async () => {
+  categoryOptions.value = [];
+  categoryOptions1.value = [];
+  const res = await categoryTree({
+    platform: diyStore.type == 1 ? 0 : diyStore.type == 2 ? 1 : diyStore.type == 3 ? 2 : diyStore.type == 4 ? 4 : 0,
+    pageNum: 1,
+    pageSize: 9999
   });
-};
-
-// 选择商品分类
-let selectIndex = 0; // 当前选择的下标
-const selectedCategories = ref({});
-// 方法:切换子级展开/收起
-const secondLevelArrowChange = (row: any) => {
-  row.isShow = !row.isShow;
-  nextTick(() => getScrollBarWidth());
-};
-
-const saveCategoryId = () => {
-  const selected = selectedCategories.value[selectIndex];
-  if (!selected || !selected.category_id) {
-    // 确保 `category_id` 存在
-    ElMessage({
-      type: 'warning',
-      message: '请选择分类'
-    });
-    return;
-  }
-  diyStore.editComponent.list[selectIndex].goods_category = selectedCategories.value[selectIndex].category_id;
-  diyStore.editComponent.list[selectIndex].goods_category_name = selectedCategories.value[selectIndex].category_name;
-  categoryShowDialog.value = false;
-};
-
-const clearAllSelections = () => {
-  categoryTable.data.forEach((row: any) => {
-    row.secondLevelCheckAll = false;
-    if (row.child_list) {
-      row.child_list.forEach((child: any) => {
-        child.threeLevelCheckAll = false;
+  const list = res.data || [];
+  categoryOptions.value = [...list];
+  categoryOptions1.value = [...list];
+  categoryOptions1.value.forEach((item1: any) => {
+    if (item1.children && item1.children.length > 0) {
+      item1.children.forEach((item2: any) => {
+        item2.children = [];
       });
     }
   });
-};
-
-const categoryShowDialogOpen = (index: any) => {
-  selectIndex = index;
-  clearAllSelections();
-
-  // 设置 isShow 状态
-  categoryTable.data.forEach((row: any) => {
-    row.isShow = false; // 默认所有分类都是合住的
-  });
-  // 确保 `selectedCategories.value[selectIndex]` 存在
-  if (!selectedCategories.value[selectIndex]) {
-    selectedCategories.value[selectIndex] = {}; // 初始化为空对象
-  }
-  // 回显已选中的分类
-  nextTick(() => {
-    const selectedCategory = diyStore.editComponent.list[selectIndex];
-    // 初始化 selectedCategories.value[selectIndex]
-    if (!selectedCategories.value[selectIndex]) {
-      selectedCategories.value[selectIndex] = {}; // 初始化为空对象
-      selectedCategories.value[selectIndex].category_id = selectedCategory.goods_category;
-      selectedCategories.value[selectIndex].category_name = selectedCategory.goods_category_name;
-    }
-
-    if (selectedCategory) {
-      categoryTable.data.forEach((row: any) => {
-        if (row.category_id === selectedCategory.goods_category) {
-          row.secondLevelCheckAll = true;
-          row.isShow = true; // 展开选中的一级分类
-        }
-        if (row.child_list) {
-          row.child_list.forEach((child: any) => {
-            if (child.category_id === selectedCategory.goods_category) {
-              child.threeLevelCheckAll = true;
-              row.isShow = true;
-            }
-          });
-        }
-      });
-    }
+  categoryOptions.value.unshift({
+    id: '',
+    label: '全部'
   });
-  nextTick(() => getScrollBarWidth());
-  categoryShowDialog.value = true;
 };
 
-// 处理复选框变化
-const handleCheckboxChange = (checked: any, target: any, parentRow?: any) => {
-  clearAllSelections(); // 清空所有复选框的选中状态
-  if (checked) {
-    // 设置当前选中的分类
-    if (parentRow) {
-      // 如果是子分类
-      target.threeLevelCheckAll = checked;
-      selectedCategories.value[selectIndex] = target;
-      parentRow.isShow = true; // 展开父级分类
-    } else {
-      // 如果是一级分类
-      target.secondLevelCheckAll = checked;
-      selectedCategories.value[selectIndex] = target;
-      target.isShow = true; // 展开选中的一级分类
-    }
-  } else {
-    // 取消勾选时,清空选中的分类 ID
-    delete selectedCategories.value[selectIndex];
-  }
+//打开弹窗
+const openDialog = (res: any) => {
+  navIndex.value = res;
+  goodsDialogRef.value.onOpen();
 };
-
-defineExpose({});
 </script>
 
 <style lang="scss" scoped>
@@ -700,6 +250,7 @@ defineExpose({});
   transform: rotate(90deg) !important;
   /* 提高优先级 */
 }
+
 .data-num {
   width: 100%;
   font-size: 14px;

+ 2 - 2
src/views/diy/components/edit-notice.vue

@@ -86,7 +86,7 @@
               </div>
 
               <el-form-item label="链接地址" v-if="diyStore.editComponent.showType == 'link'">
-                <diy-link v-model="item.link" />
+                <WebLinkInput :pageType="'2'" v-model="item.link" placeholder="请输入或选择链接" />
               </el-form-item>
             </div>
           </div>
@@ -175,7 +175,7 @@ const addNotice = () => {
   diyStore.editComponent.list.push({
     id: diyStore.generateRandom(),
     text: '公告',
-    link: { name: '' }
+    link: ''
   });
 };
 

+ 2 - 2
src/views/diy/components/edit-picture-show.vue

@@ -53,7 +53,7 @@
             </el-form-item>
 
             <el-form-item label="链接地址">
-              <diy-link v-model="item.link" />
+              <WebLinkInput :pageType="'2'" v-model="item.link" placeholder="请输入或选择链接" />
             </el-form-item>
           </div>
         </el-form>
@@ -109,7 +109,7 @@
             </el-form-item>
 
             <el-form-item label="链接地址">
-              <diy-link v-model="item.link" />
+              <WebLinkInput :pageType="'2'" v-model="item.link" placeholder="请输入或选择链接" />
             </el-form-item>
           </div>
         </el-form>

+ 3 - 17
src/views/diy/components/edit-rich-text.vue

@@ -4,7 +4,9 @@
     <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.html" :height="600" class="editor-width" />
+        <div class="editor-width">
+          <editor v-model="diyStore.editComponent.html" :height="600" />
+        </div>
       </div>
     </div>
 
@@ -18,23 +20,7 @@
 
 <script lang="ts" setup>
 import useDiyStore from '@/store/modules/diy';
-
 const diyStore = useDiyStore();
-diyStore.editComponent.ignore = []; // 忽略公共属性
-
-// 组件验证
-diyStore.editComponent.verify = (index: number) => {
-  const res = { code: true, message: '' };
-
-  if (diyStore.value[index].html == '<p><br></p>') {
-    res.code = false;
-    res.message = '请输入富文本内容';
-    return res;
-  }
-  return res;
-};
-
-defineExpose({});
 </script>
 
 <style lang="scss" scoped></style>

+ 3 - 2
src/views/diy/components/edit-rubik-cube.vue

@@ -54,7 +54,7 @@
             </el-form-item>
 
             <el-form-item label="图片上传">
-              <diy-link v-model="item.link" />
+              <WebLinkInput :pageType="'2'" v-model="item.link" placeholder="请输入或选择链接" />
             </el-form-item>
           </div>
         </el-form>
@@ -307,7 +307,7 @@ const changeTemplateList = (v: number) => {
               imageUrl: '',
               imgWidth: 0,
               imgHeight: 0,
-              link: { name: '' }
+              link: ''
             });
           }
         }
@@ -378,6 +378,7 @@ defineExpose({});
     }
 
     &.selected {
+      position: static !important;
       color: var(--el-color-primary);
       border-color: var(--el-color-primary);
     }

+ 0 - 135
src/views/diy/components/edit-shop-exchange-goods.vue

@@ -1,135 +0,0 @@
-<template>
-  <div>
-    <!-- 内容 -->
-    <div class="content-wrap" v-show="diyStore.editTab == 'content'">
-      <!-- <div class="edit-attr-item-wrap">
-              <h3 class="mb-[10px]">{{ t('selectStyle') }}</h3>
-              <div class="flex items-center mb-[18px] rounded overflow-hidden">
-                <span
-                  class="iconfont icongudingzhanshi border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
-                  :class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-1' }"
-                  @click="diyStore.editComponent.style = 'style-1'"></span>
-                <span
-                  class="iconfont icontuwendaohang3 border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
-                  :class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-2' }"
-                  @click="diyStore.editComponent.style = 'style-2'"></span>
-                <span
-                  class="iconfont iconshangpinliebiaohengxianghuadong border-[1px] border-solid border-[#eee] cursor-pointer flex-1 flex items-center justify-center py-[5px]"
-                  :class="{ 'border-[var(--el-color-primary)] text-[var(--el-color-primary)]': diyStore.editComponent.style == 'style-3' }"
-                  @click="diyStore.editComponent.style = 'style-3'"></span>
-              </div>
-            </div> -->
-      <div class="edit-attr-item-wrap">
-        <h3 class="mb-[10px]">选择数据源</h3>
-        <el-form label-width="80px" class="px-[10px]">
-          <el-form-item label="排序">
-            <el-radio-group v-model="diyStore.editComponent.sortWay">
-              <el-radio label="total_order_num">综合综合</el-radio>
-              <el-radio label="total_exchange_num">销量</el-radio>
-              <el-radio label="price">价格</el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="选择商品">
-            <el-radio-group v-model="diyStore.editComponent.source" title="选择商品">
-              <el-radio label="all">全部商品</el-radio>
-              <el-radio label="custom">手动选择</el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <!-- <el-form-item :label="t('selectCategory')" v-if="diyStore.editComponent.source == 'category'">
-                      <div class="flex items-center w-full">
-                        <div class="cursor-pointer ml-auto" @click="categoryShowDialogOpen">
-                          <span class="text-[var(--el-color-primary)]">{{ diyStore.editComponent.goods_category_name }}</span>
-                          <span class="iconfont iconxiangyoujiantou"></span>
-                        </div>
-                      </div>
-                    </el-form-item>
-                    <el-form-item :label="t('goodsNum')" v-if="diyStore.editComponent.source == 'all' || diyStore.editComponent.source == 'category'">
-                      <div class="flex items-center w-full ml-[5px]">
-                        <el-slider class="flex-1" v-model="diyStore.editComponent.num" :min="1" max="20" size="small" />
-                        <span class="ml-[15px]">{{ diyStore.editComponent.num }}</span>
-                      </div>
-                    </el-form-item> -->
-          <el-form-item label="手动选择" v-if="diyStore.editComponent.source == 'custom'">
-            <el-button type="primary" @click="goodsSelectPopupRef.show(diyStore.editComponent.goods_ids)"> 选择商品 </el-button>
-            <div class="inline-block ml-[10px] text-[14px]" v-show="diyStore.editComponent.goods_ids.length">
-              <span>已选</span>
-              <span class="text-primary mx-[2px]">{{ diyStore.editComponent.goods_ids.length }}</span>
-              <span>个</span>
-            </div>
-            <goods-select-popup ref="goodsSelectPopupRef" :min="1" @select="goodsSelect" />
-          </el-form-item>
-        </el-form>
-      </div>
-    </div>
-
-    <!-- 样式 -->
-    <div class="style-wrap" v-if="diyStore.editTab == 'style'">
-      <div class="edit-attr-item-wrap">
-        <h3 class="mb-[10px]">商品样式</h3>
-        <el-form label-width="80px" class="px-[10px]">
-          <el-form-item label="商品名称">
-            <el-color-picker v-model="diyStore.editComponent.goodsNameStyle.color" show-alpha :predefine="diyStore.predefineColors" />
-            <div class="mr-[20px]"></div>
-            <el-radio-group v-model="diyStore.editComponent.goodsNameStyle.fontWeight">
-              <el-radio :label="'normal'">常规</el-radio>
-              <el-radio :label="'bold'">加粗</el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="图片圆角">
-            <el-slider v-model="diyStore.editComponent.imgElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
-          </el-form-item>
-          <el-form-item label="已兑人数">
-            <el-color-picker v-model="diyStore.editComponent.saleStyle.color" show-alpha :predefine="diyStore.predefineColors" />
-          </el-form-item>
-          <el-form-item label="销售价">
-            <el-color-picker v-model="diyStore.editComponent.priceStyle.mainColor" show-alpha :predefine="diyStore.predefineColors" />
-          </el-form-item>
-          <el-form-item label="上圆角">
-            <el-slider v-model="diyStore.editComponent.topElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
-          </el-form-item>
-          <el-form-item label="下圆角">
-            <el-slider v-model="diyStore.editComponent.bottomElementRounded" show-input size="small" class="ml-[10px] diy-nav-slider" :max="50" />
-          </el-form-item>
-        </el-form>
-      </div>
-
-      <!-- 组件样式 -->
-      <slot name="style"></slot>
-    </div>
-  </div>
-</template>
-
-<script lang="ts" setup>
-import useDiyStore from '@/store/modules/diy';
-import goodsSelectPopup from './goods-select-popup.vue';
-
-const diyStore: any = useDiyStore();
-diyStore.editComponent.ignore = []; // 忽略公共属性
-
-// 组件验证
-diyStore.editComponent.verify = (index: number) => {
-  const res = { code: true, message: '' };
-
-  if (diyStore.value[index].source == 'category') {
-    if (diyStore.value[index].goods_category == '') {
-      res.code = false;
-      res.message = '请选择商品分类';
-    }
-  } else if (diyStore.value[index].source == 'custom') {
-    if (diyStore.value[index].goods_ids.length == 0) {
-      res.code = false;
-      res.message = '请选择商品';
-    }
-  }
-
-  return res;
-};
-
-const goodsSelectPopupRef = ref();
-const goodsSelect = (val: any) => {
-  diyStore.editComponent.goods_ids = val.map((el: any) => el.id);
-};
-defineExpose({});
-</script>
-
-<style lang="scss" scoped></style>

+ 0 - 38
src/views/diy/components/edit-shop-exchange-info.vue

@@ -1,38 +0,0 @@
-<template>
-  <div>
-    <!-- 内容 -->
-    <div class="content-wrap" v-show="diyStore.editTab == 'content'">
-      <div class="edit-attr-item-wrap">
-        <h3 class="mb-[10px]">会员样式</h3>
-        <el-form label-width="80px" class="px-[10px]">
-          <el-form-item label="背景图片">
-            <upload-image v-model="diyStore.editComponent.bgUrl" :limit="1" />
-          </el-form-item>
-        </el-form>
-      </div>
-    </div>
-
-    <!-- 样式 -->
-    <div class="style-wrap" v-show="diyStore.editTab == 'style'">
-      <!-- 组件样式 -->
-      <slot name="style"></slot>
-    </div>
-  </div>
-</template>
-
-<script lang="ts" setup>
-import useDiyStore from '@/store/modules/diy';
-
-const diyStore: any = useDiyStore();
-diyStore.editComponent.ignore = ['componentBgColor', 'componentBgUrl']; // 忽略公共属性
-
-// 组件验证
-diyStore.editComponent.verify = (index: number) => {
-  const res = { code: true, message: '' };
-  return res;
-};
-
-defineExpose({});
-</script>
-
-<style lang="scss" scoped></style>

+ 31 - 13
src/views/diy/components/edit-shop-goods-recommend.vue

@@ -6,18 +6,13 @@
         <h3 class="mb-[10px]">选择数据源</h3>
         <el-form label-width="80px" class="px-[10px]">
           <el-form-item label="选择商品">
-            <el-radio-group v-model="diyStore.editComponent.source" title="选择商品">
-              <el-radio value="all">默认</el-radio>
-              <el-radio value="custom">手动选择</el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="手动选择" v-if="diyStore.editComponent.source == 'custom'">
-            <goods-select-popup
-              ref="goodsSelectPopupRef"
-              v-model="diyStore.editComponent.goods_ids"
-              :min="diyStore.editComponent.list.length"
-              :max="diyStore.editComponent.list.length"
-            />
+            <div class="data-num" @click="openDialog">
+              <span v-if="diyStore.editComponent.goods_ids.length == 0">请选择</span>
+              <span v-else>已选择{{ diyStore.editComponent.goods_ids.length }}个</span>
+              <el-icon>
+                <ArrowRight />
+              </el-icon>
+            </div>
           </el-form-item>
         </el-form>
       </div>
@@ -93,14 +88,17 @@
       <!-- 组件样式 -->
       <slot name="style"></slot>
     </div>
+    <goods-mini ref="goodsDialogRef" :type="'editShopGoodsRecommend'" :categoryOptions="categoryOptions"></goods-mini>
   </div>
 </template>
 
 <script lang="ts" setup>
+import { categoryTree } from '@/api/pmsProduct/base';
 import useDiyStore from '@/store/modules/diy';
-import goodsSelectPopup from './goods-select-popup.vue';
 import Sortable from 'sortablejs';
 import { range } from 'lodash-es';
+const goodsDialogRef = ref<any>(null);
+const categoryOptions = ref<any>([]);
 
 const diyStore: any = useDiyStore();
 diyStore.editComponent.ignore = ['componentBgUrl']; // 忽略公共属性
@@ -141,6 +139,7 @@ diyStore.editComponent.list.forEach((item: any) => {
 const blockBoxRef = ref();
 
 onMounted(() => {
+  getCategoryTree();
   nextTick(() => {
     const sortable = Sortable.create(blockBoxRef.value, {
       group: 'item-wrap',
@@ -192,6 +191,25 @@ const deleteTempFn = (index) => {
   diyStore.editComponent.list.splice(index, 1);
   diyStore.editComponent.goods_ids.splice(index, 1);
 };
+/** 查询分类树 */
+const getCategoryTree = async () => {
+  categoryOptions.value = [];
+  const res = await categoryTree({
+    platform: 0,
+    pageNum: 1,
+    pageSize: 9999
+  });
+  const list = res.data || [];
+  categoryOptions.value = [...list];
+  categoryOptions.value.unshift({
+    id: '',
+    label: '全部'
+  });
+};
+//打开弹窗
+const openDialog = () => {
+  goodsDialogRef.value.onOpen();
+};
 
 defineExpose({});
 </script>

+ 1 - 1
src/views/diy/components/edit-shop-search.vue

@@ -1,6 +1,6 @@
 <template>
   <div>
-    <!-- 内容 -->
+    <!-- 内容  -->
     <div class="content-wrap" v-show="diyStore.editTab == 'content'">
       <div class="edit-attr-item-wrap">
         <h3 class="mb-[10px]">搜索设置</h3>

+ 37 - 199
src/views/diy/components/edit-single-recommend.vue

@@ -5,63 +5,34 @@
       <div class="edit-attr-item-wrap">
         <h3 class="mb-[10px]">标题内容</h3>
         <el-form label-width="80px" class="px-[10px]">
-          <el-form-item label="风格选择" class="flex">
-            <span class="text-primary flex-1 cursor-pointer" @click="showTitleStyle">{{ diyStore.editComponent.titleStyle.title }}</span>
-            <el-icon @click="showTitleStyle" class="cursor-pointer">
-              <ArrowRight />
-            </el-icon>
-          </el-form-item>
           <el-form-item label="图片上传">
             <upload-image v-model="diyStore.editComponent.textImg" :limit="1" />
           </el-form-item>
           <el-form-item label="链接地址">
-            <diy-link v-model="diyStore.editComponent.textLink" />
+            <WebLinkInput :pageType="'2'" v-model="diyStore.editComponent.textLink" placeholder="请输入或选择链接" />
           </el-form-item>
           <el-form-item label="副标题">
             <el-input v-model.trim="diyStore.editComponent.subTitle.text" placeholder="请输入副标题" clearable maxlength="8" show-word-limit />
           </el-form-item>
           <el-form-item label="链接地址">
-            <diy-link v-model="diyStore.editComponent.subTitle.link" />
+            <WebLinkInput :pageType="'2'" v-model="diyStore.editComponent.subTitle.link" placeholder="请输入或选择链接" />
           </el-form-item>
         </el-form>
-
-        <el-dialog v-model="showTitleDialog" title="风格选择" width="460px">
-          <div class="flex flex-wrap">
-            <template v-for="(item, index) in titleStyleList" :key="index">
-              <div
-                :class="{ 'border-primary': selectTitleStyle.value == item.value }"
-                @click="changeTitleStyle(item)"
-                class="flex items-center justify-center overflow-hidden w-[200px] h-[100px] mr-[12px] mb-[12px] cursor-pointer border bg-[#eee]"
-              >
-                <img :src="img(item.url)" />
-              </div>
-            </template>
-          </div>
-
-          <template #footer>
-            <span class="dialog-footer">
-              <el-button @click="showTitleDialog = false">取消</el-button>
-              <el-button type="primary" @click="confirmTitleStyle">确认</el-button>
-            </span>
-          </template>
-        </el-dialog>
       </div>
-
       <div class="edit-attr-item-wrap">
         <h3 class="mb-[10px]">选择数据源</h3>
         <el-form label-width="80px" class="px-[10px]">
           <el-form-item label="选择商品">
-            <el-radio-group v-model="diyStore.editComponent.source" title="选择商品">
-              <el-radio label="all">默认</el-radio>
-              <el-radio label="custom">手动选择</el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item label="手动选择" v-if="diyStore.editComponent.source == 'custom'">
-            <goods-select-popup ref="goodsSelectPopupRef" v-model="diyStore.editComponent.goods_ids" :min="1" :max="1" />
+            <div class="data-num" @click="openDialog">
+              <span v-if="diyStore.editComponent.goodsIds.length == 0">请选择</span>
+              <span v-else>已选择{{ diyStore.editComponent.goodsIds.length }}个</span>
+              <el-icon>
+                <ArrowRight />
+              </el-icon>
+            </div>
           </el-form-item>
         </el-form>
       </div>
-
       <div class="edit-attr-item-wrap">
         <h3 class="mb-[10px]">图片设置</h3>
         <el-form label-width="80px" class="px-[10px]">
@@ -84,7 +55,7 @@
               </div>
 
               <el-form-item label="链接地址">
-                <diy-link v-model="item.link" />
+                <WebLinkInput :pageType="'2'" v-model="item.link" placeholder="请输入或选择链接" />
               </el-form-item>
             </div>
           </div>
@@ -92,47 +63,6 @@
           <el-button v-show="diyStore.editComponent.list.length < 10" class="w-full" @click="addImageAd">添加图片</el-button>
         </el-form>
       </div>
-
-      <el-dialog v-model="categoryShowDialog" title="商品分类" width="750px" :destroy-on-close="true" :close-on-click-modal="false">
-        <el-table
-          :data="categoryTable.data"
-          ref="categoryTableRef"
-          size="large"
-          v-loading="categoryTable.loading"
-          height="450px"
-          @selection-change="handleSelectionChange"
-          row-key="category_id"
-          :expand-row-keys="expand_category_ids"
-          :tree-props="{ hasChildren: 'hasChildren', children: 'child_list' }"
-        >
-          <template #empty>
-            <span>{{ !categoryTable.loading ? '还没有安装应用' : '' }}</span>
-          </template>
-          <el-table-column type="selection" width="55" />
-          <el-table-column label="分类名称" min-width="120">
-            <template #default="{ row }">
-              <span class="order-2">{{ row.category_name }}</span>
-            </template>
-          </el-table-column>
-          <el-table-column label="分类图片" width="170" align="left">
-            <template #default="{ row }">
-              <div class="h-[30px]">
-                <el-image class="w-[30px] h-[30px]" :src="img(row.image)" fit="contain">
-                  <template #error>
-                    <div class="image-slot">
-                      <img class="w-[30px] h-[30px]" src="@/assets/images/diy/shop/category_default.png" />
-                    </div>
-                  </template>
-                </el-image>
-              </div>
-            </template>
-          </el-table-column>
-        </el-table>
-        <div class="flex items-center justify-end mt-[15px]">
-          <el-button type="primary" @click="saveCategoryId">确认</el-button>
-          <el-button @click="categoryShowDialog = false">取消</el-button>
-        </div>
-      </el-dialog>
     </div>
 
     <!-- 样式 -->
@@ -177,8 +107,8 @@
             <el-color-picker v-model="diyStore.editComponent.goodsNameStyle.color" show-alpha :predefine="diyStore.predefineColors" />
             <div class="mr-[20px]"></div>
             <el-radio-group v-model="diyStore.editComponent.goodsNameStyle.fontWeight">
-              <el-radio :label="'normal'">常规</el-radio>
-              <el-radio :label="'bold'">加粗</el-radio>
+              <el-radio :value="'normal'">常规</el-radio>
+              <el-radio :value="'bold'">加粗</el-radio>
             </el-radio-group>
           </el-form-item>
           <el-form-item label="销售价">
@@ -199,10 +129,12 @@
       <!-- 组件样式 -->
       <slot name="style"></slot>
     </div>
+    <goods-mini ref="goodsDialogRef" :categoryOptions="categoryOptions"></goods-mini>
   </div>
 </template>
 
 <script lang="ts" setup>
+import { categoryTree } from '@/api/pmsProduct/base';
 // import { getCategoryTree } from '@/addon/shop/api/goods';
 import { img } from '@/utils/common';
 import useDiyStore from '@/store/modules/diy';
@@ -213,135 +145,41 @@ import goodsSelectPopup from './goods-select-popup.vue';
 const diyStore: any = useDiyStore();
 diyStore.editComponent.ignore = ['componentBgUrl']; // 忽略公共属性
 
-const selectTitleStyle = reactive({
-  title: diyStore.editComponent.titleStyle.title,
-  value: diyStore.editComponent.titleStyle.value
-});
-
-// 标题风格样式
-const showTitleDialog = ref(false);
+const goodsDialogRef = ref<any>(null);
+const categoryOptions = ref<any>([]);
 
-const showTitleStyle = () => {
-  selectTitleStyle.title = diyStore.editComponent.titleStyle.title;
-  selectTitleStyle.value = diyStore.editComponent.titleStyle.value;
-  showTitleDialog.value = true;
-};
+onMounted(() => {
+  getCategoryTree();
+});
 
-const changeTitleStyle = (item: any) => {
-  selectTitleStyle.title = item.title;
-  selectTitleStyle.value = item.value;
+/** 查询分类树 */
+const getCategoryTree = async () => {
+  categoryOptions.value = [];
+  const res = await categoryTree({
+    platform: 0,
+    pageNum: 1,
+    pageSize: 9999
+  });
+  const list = res.data || [];
+  categoryOptions.value = [...list];
+  categoryOptions.value.unshift({
+    id: '',
+    label: '全部'
+  });
 };
 
-const confirmTitleStyle = () => {
-  diyStore.editComponent.titleStyle.title = selectTitleStyle.title;
-  diyStore.editComponent.titleStyle.value = selectTitleStyle.value;
-  showTitleDialog.value = false;
+//打开弹窗
+const openDialog = () => {
+  goodsDialogRef.value.onOpen();
 };
 
-const titleStyleList = reactive([
-  {
-    url: 'addon/shop/diy/single_recommend/title_style_01.png',
-    title: '风格1',
-    value: 'style-1'
-  }
-]);
-
 const addImageAd = () => {
   diyStore.editComponent.list.push({
     id: diyStore.generateRandom(),
     imageUrl: '',
     imgWidth: 0,
     imgHeight: 0,
-    link: { name: '' }
-  });
-};
-
-// 组件验证
-diyStore.editComponent.verify = (index: number) => {
-  const res = { code: true, message: '' };
-  if (diyStore.value[index].source == 'custom') {
-    if (diyStore.value[index].goods_ids.length == 0) {
-      res.code = false;
-      res.message = '请选择商品';
-    }
-  }
-
-  diyStore.value[index].list.forEach((item, index) => {
-    if (item.imageUrl === '') {
-      res.code = false;
-      res.message = '请上传图片';
-      return res;
-    }
-  });
-
-  return res;
-};
-
-const categoryShowDialog = ref(false);
-
-const categoryTable = reactive({
-  loading: true,
-  data: []
-});
-onMounted(() => {
-  loadCategoryList();
-});
-
-const categoryTableRef = ref<InstanceType<typeof ElTable>>();
-/**
- * 获取商品分类列表
- */
-let currCategoryData: any = null;
-const loadCategoryList = () => {
-  categoryTable.loading = true;
-
-  getCategoryTree()
-    .then((res) => {
-      categoryTable.loading = false;
-      categoryTable.data = res.data;
-    })
-    .catch(() => {
-      categoryTable.loading = false;
-    });
-};
-
-// 选择商品分类
-const handleSelectionChange = (val: string | any[]) => {
-  let data = '';
-  if (val) data = val[val.length - 1];
-  if (val.length > 1) categoryTableRef.value!.clearSelection();
-  if (data) categoryTableRef.value!.toggleRowSelection(data, true);
-  currCategoryData = data;
-};
-
-const saveCategoryId = () => {
-  diyStore.editComponent.goods_category = currCategoryData.category_id;
-  diyStore.editComponent.goods_category_name = currCategoryData.category_name;
-  categoryShowDialog.value = false;
-};
-
-const categoryShowDialogOpen = () => {
-  categoryShowDialog.value = true;
-  nextTick(() => {
-    setRowSelection();
-  });
-};
-
-//分类数据选中回填,设置展开行
-const expand_category_ids = ref<Array<any>>([]);
-const setRowSelection = () => {
-  expand_category_ids.value = [];
-  categoryTable.data.forEach((el: any) => {
-    if (diyStore.editComponent.goods_category == el.category_id) {
-      categoryTableRef.value!.toggleRowSelection(el, true);
-    } else if (el.child_list && el.child_list.length) {
-      el.child_list.forEach((v: any) => {
-        if (diyStore.editComponent.goods_category == v.category_id) {
-          expand_category_ids.value.push(el.category_id.toString());
-          categoryTableRef.value!.toggleRowSelection(v, true);
-        }
-      });
-    }
+    link: ''
   });
 };
 

+ 1 - 1
src/views/diy/components/edit-text.vue

@@ -20,7 +20,7 @@
             <el-input v-model.trim="diyStore.editComponent.text" placeholder="请输入标题" clearable maxlength="15" show-word-limit />
           </el-form-item>
           <el-form-item label="链接地址">
-            <WebLinkInput v-model="diyStore.editComponent.link" placeholder="请输入或选择链接" />
+            <WebLinkInput :pageType="'2'" v-model="diyStore.editComponent.link" placeholder="请输入或选择链接" />
           </el-form-item>
           <el-form-item label="对齐方式" v-show="diyStore.editComponent.style == 'style-1'">
             <el-radio-group v-model="diyStore.editComponent.textAlign">

+ 47 - 489
src/views/diy/edit.ts

@@ -198,37 +198,6 @@ export const data1 = {
             'heatMapData': []
           }
         },
-        'MemberLevel': {
-          'title': '会员等级',
-          'icon': 'iconfont iconhuiyuandengjipc',
-          'path': 'edit-member-level',
-          'uses': 1,
-          'value': {
-            'style': 'style-1',
-            'styleName': '风格1'
-          },
-          'template': {
-            'textColor': '#303133',
-            'pageStartBgColor': '',
-            'pageEndBgColor': '',
-            'pageGradientAngle': 'to bottom',
-            'componentBgUrl': '',
-            'componentBgAlpha': 2,
-            'componentStartBgColor': '',
-            'componentEndBgColor': '',
-            'componentGradientAngle': 'to bottom',
-            'topRounded': 12,
-            'bottomRounded': 0,
-            'elementBgColor': '',
-            'topElementRounded': 0,
-            'bottomElementRounded': 0,
-            'margin': {
-              'top': 0,
-              'bottom': 0,
-              'both': 10
-            }
-          }
-        },
         'Notice': {
           'title': '公告',
           'icon': 'iconfont icongonggaopc',
@@ -272,10 +241,8 @@ export const data1 = {
               'value': 'style-1'
             },
             'text': '超值爆款',
-            'textImg': 'static/resource/images/diy/active_cube/active_cube_text1.png',
-            'textLink': {
-              'name': ''
-            },
+            'textImg': '',
+            'textLink': '',
             'titleColor': '#F91700',
             'subTitle': {
               'text': '为您精选爆款',
@@ -312,7 +279,7 @@ export const data1 = {
                   'endColor': '#FFFFFF'
                 },
                 'link': '',
-                'imageUrl': 'static/resource/images/diy/active_cube/active_cube_goods1.png'
+                'imageUrl': ''
               },
               {
                 'title': {
@@ -335,7 +302,7 @@ export const data1 = {
                   'endColor': '#FFFFFF'
                 },
                 'link': '',
-                'imageUrl': 'static/resource/images/diy/active_cube/active_cube_goods2.png'
+                'imageUrl': ''
               },
               {
                 'title': {
@@ -358,7 +325,7 @@ export const data1 = {
                   'endColor': '#FFFFFF'
                 },
                 'link': '',
-                'imageUrl': 'static/resource/images/diy/active_cube/active_cube_goods3.png'
+                'imageUrl': ''
               },
               {
                 'title': {
@@ -381,7 +348,7 @@ export const data1 = {
                   'endColor': '#FFFFFF'
                 },
                 'link': '',
-                'imageUrl': 'static/resource/images/diy/active_cube/active_cube_goods4.png'
+                'imageUrl': ''
               }
             ],
             'template': {
@@ -544,7 +511,7 @@ export const data1 = {
           'value': {
             'moduleOne': {
               'head': {
-                'textImg': 'static/resource/images/diy/picture_show/picture_show_head_text3.png',
+                'textImg': '',
                 'subText': '最高补1200元',
                 'subTextColor': '#666666'
               },
@@ -557,7 +524,7 @@ export const data1 = {
                     'endColor': '#F5443E'
                   },
                   'link': '',
-                  'imageUrl': 'static/resource/images/diy/picture_show/picture_05.png'
+                  'imageUrl': ''
                 },
                 {
                   'btnTitle': {
@@ -566,10 +533,8 @@ export const data1 = {
                     'startColor': '#F5443E',
                     'endColor': '#F5443E'
                   },
-                  'link': {
-                    'name': ''
-                  },
-                  'imageUrl': 'static/resource/images/diy/picture_show/picture_06.png'
+                  'link': '',
+                  'imageUrl': ''
                 }
               ],
               'listFrame': {
@@ -579,7 +544,7 @@ export const data1 = {
             },
             'moduleTwo': {
               'head': {
-                'textImg': 'static/resource/images/diy/picture_show/picture_show_head_text4.png',
+                'textImg': '',
                 'subText': '每日上新',
                 'subTextColor': '#666666'
               },
@@ -591,10 +556,8 @@ export const data1 = {
                     'startColor': '#F5443E',
                     'endColor': '#F5443E'
                   },
-                  'link': {
-                    'name': ''
-                  },
-                  'imageUrl': 'static/resource/images/diy/picture_show/picture_07.png'
+                  'link': '',
+                  'imageUrl': ''
                 },
                 {
                   'btnTitle': {
@@ -603,10 +566,8 @@ export const data1 = {
                     'startColor': '#F5443E',
                     'endColor': '#F5443E'
                   },
-                  'link': {
-                    'name': ''
-                  },
-                  'imageUrl': 'static/resource/images/diy/picture_show/picture_08.png'
+                  'link': '',
+                  'imageUrl': ''
                 }
               ],
               'listFrame': {
@@ -802,10 +763,11 @@ export const data1 = {
               {
                 'title': '推荐',
                 'desc': '猜你喜欢',
-                'source': 'custom',
+                'source': 1,
                 'goods_category': '',
                 'goods_category_name': '请选择',
                 'goodsIds': [],
+                'categoryIds': [],
                 'imageUrl': ''
               }
             ],
@@ -833,113 +795,6 @@ export const data1 = {
             }
           }
         },
-        'GoodsCoupon': {
-          'title': '优惠券',
-          'icon': 'iconfont iconyouhuiquanpc',
-          'path': 'edit-goods-coupon',
-          'uses': 0,
-          'value': {
-            'style': 'style-1',
-            'styleName': '风格一',
-            'source': 'custom',
-            'num': 6,
-            'couponIds': [],
-            'btnText': '立即领取',
-            'couponTitle': '先领券 再购物',
-            'couponSubTitle': '领券下单 享购物优惠',
-            'titleColor': '#ffffff',
-            'subTitleColor': '#ffffff',
-            'couponItem': {
-              'bgColor': '#ffffff',
-              'textColor': '#333333',
-              'subTextColor': '#666666',
-              'moneyColor': '#333333',
-              'aroundRadius': 12
-            }
-          },
-          'template': {
-            'textColor': '#303133',
-            'pageStartBgColor': '',
-            'pageEndBgColor': '',
-            'pageGradientAngle': 'to bottom',
-            'componentBgUrl': '',
-            'componentBgAlpha': 2,
-            'componentStartBgColor': '',
-            'componentEndBgColor': '',
-            'componentGradientAngle': 'to bottom',
-            'topRounded': 0,
-            'bottomRounded': 0,
-            'elementBgColor': '',
-            'topElementRounded': 0,
-            'bottomElementRounded': 0,
-            'margin': {
-              'top': 10,
-              'bottom': 10,
-              'both': 10
-            }
-          }
-        },
-        'ShopExchangeInfo': {
-          'title': '积分兑换',
-          'icon': 'iconfont iconjifenpc',
-          'path': 'edit-shop-exchange-info',
-          'uses': 0,
-          'value': {
-            'bgUrl': 'addon/shop/diy/point/point_index_bg.jpg'
-          }
-        },
-        'ShopExchangeGoods': {
-          'title': '积分商品',
-          'icon': 'iconfont iconjifenshangpinpc',
-          'path': 'edit-shop-exchange-goods',
-          'uses': 0,
-          'value': {
-            'style': 'style-2',
-            'source': 'custom',
-            'num': 10,
-            'goods_category': '',
-            'goods_category_name': '请选择',
-            'goodsIds': [],
-            'sortWay': 'total_order_num',
-            'goodsNameStyle': {
-              'color': '#333',
-              'control': true,
-              'fontWeight': 'normal'
-            },
-            'priceStyle': {
-              'mainColor': '#FF4142',
-              'mainControl': true,
-              'lineColor': '#999CA7',
-              'lineControl': true
-            },
-            'saleStyle': {
-              'color': '#999999',
-              'control': true
-            },
-            'imgElementRounded': 10
-          },
-          'template': {
-            'textColor': '#303133',
-            'pageStartBgColor': '',
-            'pageEndBgColor': '',
-            'pageGradientAngle': 'to bottom',
-            'componentBgUrl': '',
-            'componentBgAlpha': 2,
-            'componentStartBgColor': '',
-            'componentEndBgColor': '',
-            'componentGradientAngle': 'to bottom',
-            'topRounded': 0,
-            'bottomRounded': 0,
-            'elementBgColor': '',
-            'topElementRounded': 0,
-            'bottomElementRounded': 0,
-            'margin': {
-              'top': 0,
-              'bottom': 0,
-              'both': 10
-            }
-          }
-        },
         'ShopGoodsRecommend': {
           'title': '商品推荐',
           'icon': 'iconfont icona-shangpintuijianpc30',
@@ -950,6 +805,7 @@ export const data1 = {
               'mainColor': '#333333'
             },
             'source': 'custom',
+            'goods_ids': [],
             'goodsIds': [],
             'list': [
               {
@@ -1050,35 +906,27 @@ export const data1 = {
               'title': '风格1',
               'value': 'style-1'
             },
-            'textImg': 'addon/shop/diy/index/style3/single_recommend_text1.png',
-            'textLink': {
-              'name': ''
-            },
+            'textImg': 'https://v6.site.niucloud.com/addon/shop/diy/index/style3/single_recommend_text1.png',
+            'textLink': '',
             'titleColor': '#999999',
             'subTitle': {
               'text': '更多',
               'textColor': '#999999',
-              'link': {
-                'name': ''
-              }
+              'link': ''
             },
             'source': 'custom',
             'goodsIds': [],
             'imageHeight': 250,
             'list': [
               {
-                'link': {
-                  'name': ''
-                },
-                'imageUrl': 'addon/shop/diy/index/style3/single_recommend_banner1.jpg',
+                'link': '',
+                'imageUrl': 'https://v6.site.niucloud.com/addon/shop/diy/index/style3/single_recommend_banner1.jpg',
                 'imgWidth': 345,
                 'imgHeight': 495
               },
               {
-                'link': {
-                  'name': ''
-                },
-                'imageUrl': 'addon/shop/diy/index/style3/single_recommend_banner2.jpg',
+                'link': '',
+                'imageUrl': 'https://v6.site.niucloud.com/addon/shop/diy/index/style3/single_recommend_banner2.jpg',
                 'imgWidth': 345,
                 'imgHeight': 495
               }
@@ -1141,9 +989,7 @@ export const data1 = {
               'textColor': '#FFFFFF',
               'startColor': '#FB792F',
               'endColor': '#F91700',
-              'link': {
-                'name': ''
-              }
+              'link': ''
             },
             'countDown': {
               'numberColor': 'rgba(255, 0, 0, 1)',
@@ -2115,51 +1961,6 @@ export const data3 = {
       },
       'pageStyle': 'padding-top:2rpx;padding-bottom:0rpx;padding-right:20rpx;padding-left:20rpx;'
     },
-    {
-      'path': 'edit-goods-coupon',
-      'uses': 0,
-      'id': '5zq2inzbmu00',
-      'componentName': 'GoodsCoupon',
-      'componentTitle': '优惠券',
-      'ignore': ['componentBgColor', 'componentBgUrl'],
-      'style': 'style-3',
-      'styleName': '风格3',
-      'source': 'custom',
-      'num': 6,
-      'couponIds': [],
-      'btnText': '立即领取',
-      'couponTitle': '每日省钱',
-      'couponSubTitle': '先领券 再购物',
-      'titleColor': '#ffffff',
-      'subTitleColor': '#ffffff',
-      'couponItem': {
-        'bgColor': '#ffffff',
-        'textColor': '#333333',
-        'subTextColor': '#666666',
-        'moneyColor': '#333333',
-        'aroundRadius': 12
-      },
-      'textColor': '#303133',
-      'pageStartBgColor': '',
-      'pageEndBgColor': '',
-      'pageGradientAngle': 'to bottom',
-      'componentBgUrl': '',
-      'componentBgAlpha': 2,
-      'componentStartBgColor': '',
-      'componentEndBgColor': '',
-      'componentGradientAngle': 'to bottom',
-      'topRounded': 12,
-      'bottomRounded': 12,
-      'elementBgColor': '',
-      'topElementRounded': 0,
-      'bottomElementRounded': 0,
-      'margin': {
-        'top': 10,
-        'bottom': null,
-        'both': 10
-      },
-      'pageStyle': 'padding-top:20rpx;padding-bottom:0rpx;padding-right:20rpx;padding-left:20rpx;'
-    },
     {
       'path': 'edit-shop-goods-recommend',
       'uses': 0,
@@ -2172,6 +1973,7 @@ export const data3 = {
         'mainColor': '#333333'
       },
       'source': 'custom',
+      'goods_ids': [],
       'goodsIds': [],
       'list': [
         {
@@ -2275,18 +2077,14 @@ export const data3 = {
         'value': 'style-5'
       },
       'text': '超值爆款',
-      'textLink': {
-        'name': ''
-      },
+      'textLink': '',
       'titleColor': '#F91700',
       'subTitle': {
         'text': '为您精选爆款',
         'textColor': 'rgba(153, 153, 153, 1)',
         'startColor': 'rgba(255, 255, 255, 1)',
         'endColor': 'rgba(255, 255, 255, 1)',
-        'link': {
-          'name': ''
-        }
+        'link': ''
       },
       'blockStyle': {
         'title': '风格2',
@@ -2315,9 +2113,7 @@ export const data3 = {
             'startColor': '#FFF1DB',
             'endColor': '#FFFBF4'
           },
-          'link': {
-            'name': ''
-          },
+          'link': '',
           'imageUrl': 'https://v6.site.niucloud.com/static/resource/images/diy/active_cube/active_cube_goods1.png',
           'id': '77tls7gaho80'
         },
@@ -2341,9 +2137,7 @@ export const data3 = {
             'startColor': '#E6F6E2',
             'endColor': '#F5FDF3'
           },
-          'link': {
-            'name': ''
-          },
+          'link': '',
           'imageUrl': 'https://v6.site.niucloud.com/static/resource/images/diy/active_cube/active_cube_goods2.png',
           'id': 'm4scwuc67do'
         },
@@ -2367,9 +2161,7 @@ export const data3 = {
             'startColor': '#E2F6FF',
             'endColor': '#F2FAFF'
           },
-          'link': {
-            'name': ''
-          },
+          'link': '',
           'imageUrl': 'https://v6.site.niucloud.com/static/resource/images/diy/active_cube/active_cube_goods3.png',
           'id': '33nbfp8czea0'
         },
@@ -2393,9 +2185,7 @@ export const data3 = {
             'startColor': '#FFEAEA',
             'endColor': '#FFFCFB'
           },
-          'link': {
-            'name': ''
-          },
+          'link': '',
           'imageUrl': 'https://v6.site.niucloud.com/static/resource/images/diy/active_cube/active_cube_goods4.png',
           'id': '49scoy4bgsg0'
         }
@@ -2444,9 +2234,7 @@ export const data3 = {
               'startColor': '#F5443E',
               'endColor': '#F5443E'
             },
-            'link': {
-              'name': ''
-            },
+            'link': '',
             'imageUrl': 'https://v6.site.niucloud.com/static/resource/images/diy/picture_show/picture_01.png'
           },
           {
@@ -2456,9 +2244,7 @@ export const data3 = {
               'startColor': '#F5443E',
               'endColor': '#F5443E'
             },
-            'link': {
-              'name': ''
-            },
+            'link': '',
             'imageUrl': 'https://v6.site.niucloud.com/static/resource/images/diy/picture_show/picture_02.png'
           }
         ],
@@ -2481,9 +2267,7 @@ export const data3 = {
               'startColor': '#F5443E',
               'endColor': '#F5443E'
             },
-            'link': {
-              'name': ''
-            },
+            'link': '',
             'imageUrl': 'https://v6.site.niucloud.com/static/resource/images/diy/picture_show/picture_03.png'
           },
           {
@@ -2493,9 +2277,7 @@ export const data3 = {
               'startColor': '#F5443E',
               'endColor': '#F5443E'
             },
-            'link': {
-              'name': ''
-            },
+            'link': '',
             'imageUrl': 'https://v6.site.niucloud.com/static/resource/images/diy/picture_show/picture_04.png'
           }
         ],
@@ -2529,203 +2311,6 @@ export const data3 = {
       },
       'pageStyle': 'padding-top:20rpx;padding-bottom:0rpx;padding-right:20rpx;padding-left:20rpx;'
     },
-    {
-      'path': 'edit-picture-show',
-      'uses': 0,
-      'id': '3wz1r5bww3q0',
-      'componentName': 'PictureShow',
-      'componentTitle': '图片展播',
-      'ignore': [],
-      'moduleOne': {
-        'head': {
-          'textImg': 'https://v6.site.niucloud.com/static/resource/images/diy/picture_show/picture_show_head_text3.png',
-          'subText': '最高补1200元',
-          'subTextColor': '#666666'
-        },
-        'list': [
-          {
-            'btnTitle': {
-              'text': '全网低价',
-              'color': '#ffffff',
-              'startColor': '#F5443E',
-              'endColor': '#F5443E'
-            },
-            'link': {
-              'name': ''
-            },
-            'imageUrl': 'https://v6.site.niucloud.com/static/resource/images/diy/picture_show/picture_05.png'
-          },
-          {
-            'btnTitle': {
-              'text': '大牌特惠',
-              'color': '#ffffff',
-              'startColor': '#F5443E',
-              'endColor': '#F5443E'
-            },
-            'link': {
-              'name': ''
-            },
-            'imageUrl': 'https://v6.site.niucloud.com/static/resource/images/diy/picture_show/picture_06.png'
-          }
-        ],
-        'listFrame': {
-          'startColor': 'rgba(212, 239, 255, 1)',
-          'endColor': 'rgba(235, 244, 250, 1)'
-        }
-      },
-      'moduleTwo': {
-        'head': {
-          'textImg': 'https://v6.site.niucloud.com/static/resource/images/diy/picture_show/picture_show_head_text4.png',
-          'subText': '每日上新',
-          'subTextColor': '#666666'
-        },
-        'list': [
-          {
-            'btnTitle': {
-              'text': '人气爆款',
-              'color': '#ffffff',
-              'startColor': '#F5443E',
-              'endColor': '#F5443E'
-            },
-            'link': {
-              'name': ''
-            },
-            'imageUrl': 'https://v6.site.niucloud.com/static/resource/images/diy/picture_show/picture_07.png'
-          },
-          {
-            'btnTitle': {
-              'text': '官方正品',
-              'color': '#ffffff',
-              'startColor': '#F5443E',
-              'endColor': '#F5443E'
-            },
-            'link': {
-              'name': ''
-            },
-            'imageUrl': 'https://v6.site.niucloud.com/static/resource/images/diy/picture_show/picture_08.png'
-          }
-        ],
-        'listFrame': {
-          'startColor': 'rgba(255, 241, 212, 1)',
-          'endColor': 'rgba(249, 242, 229, 1)'
-        }
-      },
-      'moduleRounded': {
-        'topRounded': 10,
-        'bottomRounded': 10
-      },
-      'textColor': '#303133',
-      'pageStartBgColor': '',
-      'pageEndBgColor': '',
-      'pageGradientAngle': 'to bottom',
-      'componentBgUrl': '',
-      'componentBgAlpha': 2,
-      'componentStartBgColor': '',
-      'componentEndBgColor': '',
-      'componentGradientAngle': 'to bottom',
-      'topRounded': 0,
-      'bottomRounded': 0,
-      'elementBgColor': '',
-      'topElementRounded': 0,
-      'bottomElementRounded': 0,
-      'margin': {
-        'top': 10,
-        'bottom': null,
-        'both': 10
-      },
-      'pageStyle': 'padding-top:20rpx;padding-bottom:0rpx;padding-right:20rpx;padding-left:20rpx;'
-    },
-    // {
-    //   'path': 'edit-single-recommend',
-    //   'uses': 0,
-    //   'id': '2mxsu6pbqpu0',
-    //   'componentName': 'SingleRecommend',
-    //   'mode': 'aspectFill',
-    //   'componentTitle': '精选推荐',
-    //   'ignore': [],
-    //   'titleStyle': {
-    //     'title': '风格1',
-    //     'value': 'style-1'
-    //   },
-    //   'textImg': 'https://v6.site.niucloud.com/addon/shop/diy/index/style3/single_recommend_text1.png',
-    //   'textLink': {
-    //     'name': ''
-    //   },
-    //   'titleColor': 'rgba(153, 153, 153, 1)',
-    //   'subTitle': {
-    //     'text': '更多',
-    //     'textColor': 'rgba(153, 153, 153, 1)',
-    //     'link': {
-    //       'name': ''
-    //     }
-    //   },
-    //   'source': 'custom',
-    //   'goodsIds': [],
-    //   'imageHeight': '250',
-    //   'list': [
-    //     {
-    //       'id': '18o4pyaufktc',
-    //       'imageUrl': 'addon/shop/diy/index/style3/single_recommend_banner1.jpg',
-    //       'imgWidth': 345,
-    //       'imgHeight': 495,
-    //       'link': {
-    //         'name': ''
-    //       },
-    //       'width': 355,
-    //       'height': 509.3478260869565
-    //     },
-    //     {
-    //       'id': '18o8pyaufktc',
-    //       'imageUrl': 'addon/shop/diy/index/style3/single_recommend_banner2.jpg',
-    //       'imgWidth': 345,
-    //       'imgHeight': 495,
-    //       'link': {
-    //         'name': ''
-    //       },
-    //       'width': 355,
-    //       'height': 509.3478260869565
-    //     }
-    //   ],
-    //   'goodsNameStyle': {
-    //     'color': '#303133',
-    //     'control': true,
-    //     'fontWeight': 'normal'
-    //   },
-    //   'priceStyle': {
-    //     'mainColor': '#FF4142',
-    //     'mainControl': true,
-    //     'lineColor': '#999CA7',
-    //     'lineControl': true
-    //   },
-    //   'saleStyle': {
-    //     'color': 'rgba(255, 0, 0, 1)',
-    //     'control': true
-    //   },
-    //   'textColor': '#303133',
-    //   'pageStartBgColor': '',
-    //   'pageEndBgColor': '',
-    //   'pageGradientAngle': 'to bottom',
-    //   'componentBgUrl': '',
-    //   'componentBgAlpha': 2,
-    //   'componentStartBgColor': null,
-    //   'componentEndBgColor': null,
-    //   'componentGradientAngle': 'to bottom',
-    //   'topRounded': 0,
-    //   'bottomRounded': 0,
-    //   'elementBgColor': 'rgba(255, 255, 255, 1)',
-    //   'topElementRounded': 12,
-    //   'bottomElementRounded': 12,
-    //   'margin': {
-    //     'top': 15,
-    //     'bottom': 0,
-    //     'both': 10
-    //   },
-    //   'pageStyle': 'padding-top:20rpx;padding-bottom:20rpx;padding-right:20rpx;padding-left:20rpx;',
-    //   'topCarouselRounded': 12,
-    //   'bottomCarouselRounded': 12,
-    //   'indicatorColor': 'rgba(255, 255, 255, 0.6)',
-    //   'indicatorActiveColor': 'rgba(255, 255, 255, 1)'
-    // },
     {
       'path': 'edit-image-ads',
       'uses': 0,
@@ -2737,9 +2322,7 @@ export const data3 = {
       'isSameScreen': false,
       'list': [
         {
-          'link': {
-            'name': ''
-          },
+          'link': '',
           'imageUrl': 'https://v6.site.niucloud.com/addon/shop/diy/index/style3/discount_img.png',
           'imgWidth': 710,
           'imgHeight': 170,
@@ -2821,10 +2404,11 @@ export const data3 = {
         {
           'title': '推荐',
           'desc': '猜你喜欢',
-          'source': 'custom',
+          'source': 1,
           'goods_category': '',
           'goods_category_name': '请选择',
           'goodsIds': [],
+          'categoryIds': [],
           'imageUrl': '',
           'id': '67pl1ysjhr40'
         },
@@ -2832,30 +2416,33 @@ export const data3 = {
           'id': '6z59zcmk4jk0',
           'title': '衣鞋包饰',
           'desc': '分类描述',
-          'source': 'custom',
+          'source': 1,
           'goods_category': '',
           'goods_category_name': '请选择',
           'goodsIds': [],
+          'categoryIds': [],
           'imageUrl': ''
         },
         {
           'id': '1cfbll6wnmw0',
           'title': '居家百货',
           'desc': '分类描述',
-          'source': 'custom',
+          'source': 1,
           'goods_category': '',
           'goods_category_name': '请选择',
           'goodsIds': [],
+          'categoryIds': [],
           'imageUrl': ''
         },
         {
           'id': '49p79g5l5qs0',
           'title': '食品营养',
           'desc': '分类描述',
-          'source': 'custom',
+          'source': 1,
           'goods_category': '',
           'goods_category_name': '请选择',
           'goodsIds': [],
+          'categoryIds': [],
           'imageUrl': ''
         }
       ],
@@ -2989,35 +2576,6 @@ export const data4 = {
       'bgUrl': '',
       'isShowAccount': true
     },
-    {
-      'path': 'edit-member-level',
-      'uses': 1,
-      'id': '533e6ynytmo0',
-      'componentName': 'MemberLevel',
-      'componentTitle': '会员等级',
-      'ignore': ['componentBgColor', 'componentBgUrl'],
-      'style': 'style-5',
-      'styleName': '风格5',
-      'textColor': '#303133',
-      'componentStartBgColor': '',
-      'componentEndBgColor': '',
-      'topRounded': 12,
-      'bottomRounded': 12,
-      'elementBgColor': '',
-      'topElementRounded': 0,
-      'bottomElementRounded': 0,
-      'margin': {
-        'top': -45,
-        'bottom': 0,
-        'both': 10
-      },
-      'pageStartBgColor': '',
-      'pageEndBgColor': '',
-      'pageGradientAngle': 'to bottom',
-      'componentBgUrl': '',
-      'componentBgAlpha': 2,
-      'componentGradientAngle': 'to bottom'
-    },
     {
       'path': 'edit-shop-order-info',
       'uses': 1,
@@ -3251,7 +2809,7 @@ export const data4 = {
           'imgHeight': 92
         },
         {
-          'title': '优惠券',
+          'title': '优惠券1',
           'link': '',
           'imageUrl': 'https://v6.site.niucloud.com/addon/shop/diy/member/style1/nav_coupon.png',
           'label': {

+ 1 - 3
src/views/diy/edit.vue

@@ -286,14 +286,13 @@ const save = (type?: number) => {
 
   if (isRepeat.value) return;
   isRepeat.value = true;
-
   const datas = {
     id: id.value,
     page_title: diyStore.pageTitle,
     title: diyStore.global.title,
     type: diyStore.type,
     is_default: diyStore.isDefault,
-    isDefault:1,
+    isDefault: 1,
     value: JSON.stringify({
       global: toRaw(diyStore.global),
       value: toRaw(diyStore.value)
@@ -590,4 +589,3 @@ watch(
   }
 }
 </style>
-

+ 4 - 4
src/views/diy/miniEdit.vue

@@ -256,14 +256,14 @@ onMounted(() => {
     diyStore.type = query.type.toString();
     diyStore.pageTitle = query.title.toString();
     // 首页模板1
-    // diyStore.global = data3.global;
-    // diyStore.value = data3.value;
+    diyStore.global = data3.global;
+    diyStore.value = data3.value;
     // 个人中心模板
     // diyStore.global = data4.global;
     // diyStore.value = data4.value;
     // 商品详情模板
-    diyStore.global = data2.shop_goods_detail_style1.data.global;
-    diyStore.value = data2.shop_goods_detail_style1.data.value;
+    // diyStore.global = data2.shop_goods_detail_style1.data.global;
+    // diyStore.value = data2.shop_goods_detail_style1.data.value;
   }
 });
 

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

@@ -44,7 +44,7 @@
                   <div class="flex-row-start">
                     <upload-image v-model="element.imageUrl" :limit="1" />
                     <div class="flex-column-between images-bos">
-                      <div class="annotation3">(建议上传尺寸相同图片,推荐尺寸150*150)</div>
+                      <div class="annotation3">(建议上传尺寸相同图片,推荐尺寸170*112)</div>
                       <div class="flex-row-between images-box">
                         <div>缩放模式</div>
                         <div class="flex-row-start" @click="openImageType(element, index)">

+ 2 - 2
src/views/diy/pccomponents/pages/floor.vue

@@ -67,7 +67,7 @@ const props = defineProps<{
   row?: any;
 }>();
 const componentData = props.row ? props.row : diyStore.componentList[props.index];
-const dataList = ref<any>([{}, {}, {}, {}, {}, {}, {}, {}]);
+const dataList = ref<any>([{}, {}, {}, {}, {}, {}, {}, {}, {}, {}]);
 
 onMounted(() => {
   getDataList();
@@ -82,7 +82,7 @@ watch(
 );
 
 const getDataList = () => {
-  dataList.value = [{}, {}, {}, {}, {}, {}, {}, {}];
+  dataList.value = [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}];
   //手动选择
   if (componentData.goodsIds.length > 0) {
     const apiFunc = diyStore.clientId && diyStore.clientId !== 'undefined' ? getCustomerProductPage : listBase;

+ 4 - 4
src/views/diy/pccomponents/pages/navigation.vue

@@ -135,8 +135,8 @@ const subtitleCss = computed(() => {
       cursor: pointer;
 
       .img {
-        height: 80px;
-        width: 80px;
+        height: 112px;
+        aspect-ratio: 170 / 112;
       }
     }
   }
@@ -152,8 +152,8 @@ const subtitleCss = computed(() => {
         height: 170px;
 
         .img {
-          height: 80px;
-          width: 80px;
+          height: 112px;
+          aspect-ratio: 170 / 112;
         }
       }
     }

+ 141 - 0
src/views/mall/miniPageSet/index.vue

@@ -0,0 +1,141 @@
+<template>
+  <div>
+    <el-card class="box-card mt-[15px] !border-none" shadow="never" v-loading="loading">
+      <div class="flex-1">
+        <div class="flex items-center border-l-[3px] border-primary pl-[5px] leading-[1.1] mt-[10px]">
+          <span class="text-[14px]">正在编辑</span>
+          <span class="text-[14px] text-primary mx-[3px]">H5/小程序</span>
+          <span class="text-[14px]">分类页面</span>
+        </div>
+        <el-form :model="formData" label-width="100px" ref="formRef">
+          <el-form-item label="分类类型" class="mt-[30px]">
+            <el-radio-group v-model="formData.level">
+              <el-radio value="1">一级分类</el-radio>
+              <el-radio value="2">二级分类</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="分类模板">
+            <template v-for="(item, index) in templateList" :key="index">
+              <div
+                v-if="formData.level == item.type"
+                :class="[
+                  'w-[150px] border-[1px] border-[#ededed] border-solid overflow-hidden text-[#ededed] rounded-[4px] mr-[20px] relative',
+                  formData.template === item.template ? 'border-color text-color' : ''
+                ]"
+                @click="levelChange(item.template)"
+              >
+                <img class="w-[100%]" :src="item.preview" fit-object="contain" />
+                <span class="iconfont iconicon-selected absolute right-0 bottom-[-8px]"></span>
+              </div>
+            </template>
+          </el-form-item>
+          <el-form-item label="页面名称">
+            <el-input v-model="formData.pageTitle" clearable placeholder="请输入页面名称" style="width: 280px" maxlength="10" show-word-limit />
+          </el-form-item>
+          <el-form-item label="搜索栏">
+            <el-radio-group v-model="formData.searchControl">
+              <el-radio value="1">开启</el-radio>
+              <el-radio value="0">关闭</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" class="mt-[15px]" @click="onSave">保存</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { miniPageSetCurrent, miniPageSetAdd, miniPageSetEdit } from '@/api/diy/index';
+import category_style1_1 from '@/assets/images/diy/category_style1_1.png';
+import category_style2_1 from '@/assets/images/diy/category_style2_1.png';
+import category_style2_2 from '@/assets/images/diy/category_style2_2.png';
+const loading = ref<boolean>(false);
+const id = ref<any>(null);
+const formData = ref<any>({
+  level: '1',
+  template: 'style-1',
+  pageTitle: '',
+  searchControl: '1'
+});
+const templateList = reactive([
+  {
+    template: 'style-1',
+    preview: category_style1_1,
+    type: '1'
+  },
+  {
+    template: 'style-1',
+    preview: category_style2_1,
+    type: '2'
+  },
+  {
+    template: 'style-2',
+    preview: category_style2_2,
+    type: '2'
+  }
+]);
+
+const getData = () => {
+  loading.value = true;
+  miniPageSetCurrent({})
+    .then((res) => {
+      loading.value = false;
+      if (res.code == 200) {
+        if (res.data.id) {
+          formData.value = {
+            level: res.data.level,
+            template: res.data.template,
+            pageTitle: res.data.pageTitle,
+            searchControl: res.data.searchControl
+          };
+          id.value = res.data.id;
+        } else {
+          formData.value = {
+            level: '1',
+            template: 'style-1',
+            pageTitle: '',
+            searchControl: '1'
+          };
+          id.value = null;
+        }
+      }
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+
+const levelChange = (value: any) => {
+  formData.value.template = value;
+};
+
+const onSave = () => {
+  const api = id.value ? miniPageSetEdit : miniPageSetAdd;
+  if (id.value) {
+    formData.value.id = id.value;
+  }
+  api(formData.value)
+    .then((res) => {
+      if (res.code == 200) {
+        getData();
+      }
+    })
+    .catch(() => {});
+};
+
+onMounted(() => {
+  getData();
+});
+</script>
+<style lang="scss" scoped>
+.border-color {
+  border-color: var(--el-color-primary);
+}
+
+.text-color {
+  color: var(--el-color-primary);
+}
+</style>

+ 7 - 5
src/views/mall/navigation/index.vue

@@ -12,7 +12,7 @@
                 class="w-[22px] h-[22px] mb-[5px] leading-1"
                 :src="item.iconNormal"
                 fit="cover"
-                v-if="['1', '2'].includes(diyBottomData.value.type.toString())"
+                v-if="['icon_text', 'icon'].includes(diyBottomData.value.type.toString())"
               >
                 <template #error>
                   <div class="image-slot flex justify-center items-center mt-1">
@@ -24,7 +24,7 @@
               </el-image>
               <span
                 class="text-[12px]"
-                v-if="['1', '3'].includes(diyBottomData.value.type.toString())"
+                v-if="['icon_text', 'text'].includes(diyBottomData.value.type.toString())"
                 :style="{ 'color': diyBottomData.value.bottomNavTextColor }"
                 >{{ item.text }}</span
               >
@@ -81,9 +81,9 @@
             <el-tab-pane label="样式设置" name="setStyle">
               <el-form-item label="导航类型">
                 <el-radio-group v-model="diyBottomData.value.type" class="ml-4">
-                  <el-radio value="1" size="large">图文</el-radio>
-                  <el-radio value="2" size="large">图片</el-radio>
-                  <el-radio value="3" size="large">文字</el-radio>
+                  <el-radio value="icon_text" size="large">图文</el-radio>
+                  <el-radio value="icon" size="large">图片</el-radio>
+                  <el-radio value="text" size="large">文字</el-radio>
                 </el-radio-group>
               </el-form-item>
               <el-form-item label="文字颜色">
@@ -155,6 +155,7 @@ const getData = () => {
         diyBottomData.value.bottomNavBgColor = res.data.bottomNavBgColor;
         diyBottomData.value.bottomNavTextColor = res.data.bottomNavTextColor;
         diyBottomData.value.bottomNavSelectedColor = res.data.bottomNavSelectedColor;
+        diyBottomData.value.type = res.data.navStyle;
       }
     })
     .catch(() => {
@@ -180,6 +181,7 @@ const onSave = () => {
     bottomNavTextColor: diyBottomData.value.bottomNavTextColor,
     bottomNavSelectedColor: diyBottomData.value.bottomNavSelectedColor,
     navItems: diyBottomData.value.list,
+    navStyle: diyBottomData.value.type,
     id: id.value
   };
   navigationSave(datas)

+ 10 - 4
src/views/mall/pageCategory/index.vue

@@ -13,6 +13,12 @@
             <el-form-item label="分类名称" prop="name">
               <el-input v-model="queryParams.name" placeholder="请输入" clearable @keyup.enter="handleQuery" />
             </el-form-item>
+            <el-form-item label="页面类型" prop="pageType">
+              <el-select v-model="queryParams.pageType" placeholder="页面类型" clearable>
+                <el-option label="pc端" :value="'1'" />
+                <el-option label="小程序端" :value="'2'" />
+              </el-select>
+            </el-form-item>
             <!-- <el-form-item label="添加时间" prop="createTime">
               <el-date-picker clearable v-model="queryParams.createTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择添加时间" />
             </el-form-item> -->
@@ -56,7 +62,7 @@
           </template>
         </el-table-column>
         <el-table-column prop="level" align="center" label="层级" width="80"></el-table-column>
-        <el-table-column prop="pageType" align="center" label="页面类型" width="80">
+        <el-table-column prop="pageType" align="center" label="页面类型" width="120">
           <template #default="scope">
             <el-tag> {{ scope.row.pageType == '1' ? 'pc端' : '小程序' }}</el-tag>
           </template>
@@ -228,7 +234,6 @@ const initFormData: PageCategoryForm = {
   sort: 100,
   status: 1, // 默认设置为正常状态
   isMer: 0,
-  pageType: '1',
   createTime: undefined
 };
 const initData: PageData<PageCategoryForm, PageCategoryQuery> = {
@@ -237,7 +242,8 @@ const initData: PageData<PageCategoryForm, PageCategoryQuery> = {
     title: undefined,
     pageKey: undefined,
     name: undefined,
-    createTime: undefined
+    createTime: undefined,
+    pageType: '1'
   },
   rules: {
     parentId: [{ required: true, message: '上级分类不能为空', trigger: 'blur' }],
@@ -336,7 +342,7 @@ const handleStatusChange = async (row: PageCategoryVO) => {
 /** 获取分类树数据方法 */
 const loadCategoryTreeData = async () => {
   try {
-    const res = await getCategoryTree();
+    const res = await getCategoryTree(queryParams.value.pageType);
     if (res.data) {
       // 直接使用后端返回的数据,不再添加前端顶级父类节点
       categoryOptions.value = res.data as CategoryOptionsType[];

+ 11 - 4
src/views/mall/pageLink/index.vue

@@ -10,6 +10,12 @@
             <el-form-item label="页面名称" prop="name">
               <el-input v-model="queryParams.name" placeholder="请输入页面名称" clearable @keyup.enter="handleQuery" />
             </el-form-item>
+            <el-form-item label="页面类型" prop="pageType">
+              <el-select v-model="queryParams.pageType" placeholder="页面类型" clearable>
+                <el-option label="pc端" :value="'1'" />
+                <el-option label="小程序端" :value="'2'" />
+              </el-select>
+            </el-form-item>
             <!-- <el-form-item label="添加时间" prop="createTime">
               <el-date-picker clearable v-model="queryParams.createTime" type="date" value-format="YYYY-MM-DD" placeholder="请选择添加时间" />
             </el-form-item> -->
@@ -66,7 +72,7 @@
           </template>
         </el-table-column>
         <el-table-column label="排序" align="center" prop="sort" />
-        <el-table-column prop="pageType" align="center" label="页面类型" width="80">
+        <el-table-column prop="pageType" align="center" label="页面类型" width="120">
           <template #default="scope">
             <el-tag> {{ scope.row.pageType == '1' ? 'pc端' : '小程序' }}</el-tag>
           </template>
@@ -201,8 +207,7 @@ const initFormData: PageLinkForm = {
   status: 1,
   sort: 100,
   createTime: undefined,
-  isMer: 0,
-  pageType: '1'
+  isMer: 0
 };
 const data = reactive<PageData<PageLinkForm, PageLinkQuery>>({
   form: { ...initFormData },
@@ -212,6 +217,7 @@ const data = reactive<PageData<PageLinkForm, PageLinkQuery>>({
     cateId: undefined,
     name: undefined,
     createTime: undefined,
+    pageType: '1',
     params: {}
   },
   rules: {}
@@ -223,7 +229,7 @@ const { queryParams, form, rules } = toRefs(data);
 const getCategorys = async () => {
   try {
     // 获取分类树形结构用于选择器
-    const res = await getCategoryTree();
+    const res = await getCategoryTree(queryParams.value.pageType);
     if (res.data) {
       categoryOptions.value = res.data as CategoryOptionsType[];
     }
@@ -240,6 +246,7 @@ const getList = async () => {
   pageLinkList.value = res.rows;
   total.value = res.total;
   loading.value = false;
+  getCategorys();
 };
 
 /** 取消按钮 */

+ 2 - 7
src/views/platform/customerOperation/vipSite/index.vue

@@ -89,11 +89,6 @@
         <el-table-column label="客户编号" align="center" prop="clientNo" width="100" />
         <el-table-column label="客户名称" align="center" prop="clientName" min-width="100" />
         <el-table-column label="站点名称" align="center" prop="siteName" min-width="180" />
-        <el-table-column label="站点域名" align="center" prop="siteDomain" min-width="220">
-          <template #default="scope">
-            <el-link type="primary" :href="scope.row.siteDomain" target="_blank">{{ scope.row.siteDomain }}</el-link>
-          </template>
-        </el-table-column>
         <el-table-column label="状态" align="center" width="80">
           <template #default="scope">
             <el-tag :type="scope.row.status === '0' ? 'success' : 'info'" size="small">
@@ -115,8 +110,8 @@
           <template #default="scope">
             <div class="flex flex-col items-center gap-1">
               <el-link type="primary" :underline="false" @click="handleSiteConfig(scope.row)">站点配置</el-link>
-              <el-link type="primary" :underline="false" @click="handleProductConfig(scope.row)">商品配置</el-link>
-              <el-link type="primary" :underline="false" @click="handleStyleDesign(scope.row)">样式设计</el-link>
+              <!-- <el-link type="primary" :underline="false" @click="handleProductConfig(scope.row)">商品配置</el-link>
+              <el-link type="primary" :underline="false" @click="handleStyleDesign(scope.row)">样式设计</el-link> -->
               <el-link type="primary" :underline="false" @click="handleDiy(scope.row)">diy设计</el-link>
               <el-link :type="scope.row.status === '0' ? 'danger' : 'success'" :underline="false" @click="handleStatusChange(scope.row)">
                 {{ scope.row.status === '0' ? '停 用' : '启 用' }}

+ 1 - 1
src/views/platform/customerOperation/vipSite/styleDesign.vue

@@ -103,7 +103,7 @@
           </el-col>
         </el-row>
         <el-form-item label="图片" prop="imageUrl">
-          <image-upload v-model="form.imageUrl" :limit="1" :file-size="5" :file-type="['png', 'jpg', 'jpeg']" />
+          <upload-image v-model="form.imageUrl" :limit="1" :file-size="5" :file-type="['png', 'jpg', 'jpeg']" />
         </el-form-item>
       </el-form>
       <template #footer>

+ 1 - 1
src/views/platform/decoration/brand/index.vue

@@ -71,7 +71,7 @@
           <el-input v-model="form.advertBrief" placeholder="请输入品牌简介" />
         </el-form-item>
         <el-form-item label="品牌图片" prop="coverImage">
-          <image-upload v-model="form.coverImage" :limit="1" />
+          <upload-image v-model="form.coverImage" :limit="1" />
           <div class="upload-tip">建议尺寸:400x300,支持 jpg/png 格式</div>
         </el-form-item>
         <el-form-item label="跳转链接" prop="advertUrl">

+ 1 - 1
src/views/platform/decoration/carousel/index.vue

@@ -94,7 +94,7 @@
           <el-input v-model="form.title" placeholder="请输入广告名称" />
         </el-form-item>
         <el-form-item label="广告图片" prop="imageUrl">
-          <image-upload v-model="form.imageUrl" :limit="1" />
+          <upload-image v-model="form.imageUrl" :limit="1" />
         </el-form-item>
         <el-form-item label="开始时间" prop="startTime">
           <el-date-picker

+ 1 - 1
src/views/platform/decoration/caseAd/index.vue

@@ -10,7 +10,7 @@
       </div>
       <!-- 隐藏的image-upload -->
       <div style="display: none">
-        <image-upload ref="uploadRef" v-model="imageOssId" :limit="1" />
+        <upload-image ref="uploadRef" v-model="imageOssId" :limit="1" />
       </div>
     </div>
   </div>

+ 50 - 43
src/views/platform/decoration/flashSale/index.vue

@@ -110,7 +110,7 @@
           <el-switch v-model="bannerForm.showText" />
         </el-form-item>
         <el-form-item label="图片" required>
-          <image-upload v-model="bannerForm.coverImage" :limit="1" />
+          <upload-image v-model="bannerForm.coverImage" :limit="1" />
         </el-form-item>
         <el-form-item label="描述" required>
           <el-input v-model="bannerForm.advertBrief" type="textarea" :rows="3" placeholder="请输入描述" />
@@ -194,11 +194,11 @@ const defaultBannerUrl = 'https://via.placeholder.com/200x300/1a237e/FFFFFF?text
 // 左侧大图配置
 const bannerConfig = ref({ id: null as number | null, imageUrl: '', imageOssId: '', link: '', title: '', brief: '', showText: false });
 const bannerDialog = reactive({ visible: false });
-const bannerForm = reactive({ 
-  id: null as number | null, 
-  advertTitle: '', 
-  coverImage: '', 
-  advertUrl: '', 
+const bannerForm = reactive({
+  id: null as number | null,
+  advertTitle: '',
+  coverImage: '',
+  advertUrl: '',
   advertBrief: '',
   showText: false,
   advertPosition: ADVERT_POSITION,
@@ -219,10 +219,10 @@ const loadBannerConfig = async () => {
           if (ossRes.data && ossRes.data.length > 0) displayUrl = ossRes.data[0].url;
         } catch (e) { console.error('获取图片URL失败', e); }
       }
-      bannerConfig.value = { 
-        id: data.id, 
-        imageUrl: displayUrl, 
-        imageOssId: ossId, 
+      bannerConfig.value = {
+        id: data.id,
+        imageUrl: displayUrl,
+        imageOssId: ossId,
         link: data.advertUrl || '',
         title: data.advertTitle || '',
         brief: data.advertBrief || '',
@@ -238,10 +238,17 @@ const handleEditBanner = async () => {
       const res = await getFloorAdvertManage(bannerConfig.value.id);
       if (res.data) {
         const data = res.data;
-        Object.assign(bannerForm, { 
-          id: data.id, 
+        // 确保 coverImage 存储的是 ossId 而不是 URL
+        // 如果 data.coverImage 是数字(ossId),直接使用;如果是URL,需要使用 bannerConfig.value.imageOssId
+        let coverImageValue = data.coverImage || '';
+        // 如果 coverImage 是 URL,说明后端返回的不是 ossId,需要用缓存的 imageOssId
+        if (coverImageValue && typeof coverImageValue === 'string' && coverImageValue.startsWith('http')) {
+          coverImageValue = bannerConfig.value.imageOssId || '';
+        }
+        Object.assign(bannerForm, {
+          id: data.id,
           advertTitle: data.advertTitle || '',
-          coverImage: data.coverImage || '', 
+          coverImage: coverImageValue,
           advertUrl: data.advertUrl || '',
           advertBrief: data.advertBrief || '',
           showText: data.isShow === '1',
@@ -251,10 +258,10 @@ const handleEditBanner = async () => {
         });
       }
     } else {
-      Object.assign(bannerForm, { 
-        id: null, 
+      Object.assign(bannerForm, {
+        id: null,
         advertTitle: '',
-        coverImage: '', 
+        coverImage: '',
         advertUrl: '',
         advertBrief: '',
         showText: true,
@@ -293,12 +300,12 @@ const loadHeaderConfig = async () => {
     const res = await getFloorTitle(TITLE_ID);
     if (res.data) {
       const data = res.data;
-      headerConfig.value = { 
-        id: data.id, 
-        title: data.title || '大牌推荐', 
-        subtitle: data.subtitle || '甄选大牌,优质好品', 
-        linkText: data.linkWord || '查看更多品牌信息', 
-        linkUrl: data.linkUrl || '' 
+      headerConfig.value = {
+        id: data.id,
+        title: data.title || '大牌推荐',
+        subtitle: data.subtitle || '甄选大牌,优质好品',
+        linkText: data.linkWord || '查看更多品牌信息',
+        linkUrl: data.linkUrl || ''
       };
     }
   } catch (error) { console.error('加载标题配置失败', error); }
@@ -308,12 +315,12 @@ const handleEditHeader = () => { Object.assign(headerForm, headerConfig.value);
 
 const saveHeaderConfig = async () => {
   try {
-    const data = { 
-      id: headerForm.id, 
-      title: headerForm.title, 
-      subtitle: headerForm.subtitle, 
-      linkWord: headerForm.linkText, 
-      linkUrl: headerForm.linkUrl 
+    const data = {
+      id: headerForm.id,
+      title: headerForm.title,
+      subtitle: headerForm.subtitle,
+      linkWord: headerForm.linkText,
+      linkUrl: headerForm.linkUrl
     };
     if (headerForm.id) await updateFloorTitle(data);
     else await addFloorTitle(data);
@@ -585,14 +592,14 @@ onMounted(() => { loadHeaderConfig(); loadBannerConfig(); loadRecommendId().then
 
   &:hover { background: #fafafa; }
 
-  .close-icon { 
-    position: absolute; 
-    top: 8px; 
-    right: 8px; 
-    font-size: 18px; 
-    color: #999; 
-    cursor: pointer; 
-    &:hover { color: #f56c6c; } 
+  .close-icon {
+    position: absolute;
+    top: 8px;
+    right: 8px;
+    font-size: 18px;
+    color: #999;
+    cursor: pointer;
+    &:hover { color: #f56c6c; }
   }
 
   .sort-icon {
@@ -643,16 +650,16 @@ onMounted(() => { loadHeaderConfig(); loadBannerConfig(); loadRecommendId().then
   background: #fff;
   border-radius: 4px;
 
-  .search-title { 
-    font-size: 14px; 
-    font-weight: 500; 
-    color: #333; 
+  .search-title {
+    font-size: 14px;
+    font-weight: 500;
+    color: #333;
     padding: 12px 20px;
     border-bottom: 1px solid #e8e8e8;
   }
-  .search-bar { 
-    display: flex; 
-    gap: 10px; 
+  .search-bar {
+    display: flex;
+    gap: 10px;
     padding: 16px 20px;
   }
 }

+ 2 - 2
src/views/platform/decoration/floor/manage.vue

@@ -173,7 +173,7 @@
           </div>
         </el-form-item>
         <el-form-item label="图片" prop="advertiseImage" required>
-          <ImageUpload v-model="adForm.advertiseImage" :limit="1" />
+          <upload-image v-model="adForm.advertiseImage" :limit="1" />
         </el-form-item>
         <el-form-item label="描述" prop="advertiseDescribe" required>
           <el-input v-model="adForm.advertiseDescribe" type="textarea" :rows="4" placeholder="请输入描述" />
@@ -226,7 +226,7 @@ import { listFloorLabel, addFloorLabel, updateFloorLabel, delFloorLabel } from '
 import { listFloor } from '@/api/system/floor';
 import { listProduct } from '@/api/product/productBase';
 import { listDecorationFloorLink, addDecorationFloorLink, delDecorationFloorLink } from '@/api/product/decorationFloorLink';
-import ImageUpload from '@/components/ImageUpload/index.vue';
+import uploadImage from '@/components/upload-image/index.vue';
 
 const route = useRoute();
 const router = useRouter();

+ 1 - 1
src/views/platform/decoration/floorAd/index.vue

@@ -93,7 +93,7 @@
             <el-row :gutter="20">
               <el-col :span="12">
                 <el-form-item label="图片:">
-                  <image-upload v-model="form.imageUrl" :limit="1" />
+                  <upload-image v-model="form.imageUrl" :limit="1" />
                 </el-form-item>
               </el-col>
               <el-col :span="12" v-if="activeTab === 'icon'">

+ 1 - 1
src/views/platform/decoration/fresh/index.vue

@@ -89,7 +89,7 @@
           <el-input v-model="form.advertBrief" placeholder="请输入简介" />
         </el-form-item>
         <el-form-item label="图片" prop="coverImage">
-          <image-upload v-model="form.coverImage" :limit="1" />
+          <upload-image v-model="form.coverImage" :limit="1" />
         </el-form-item>
         <el-form-item label="跳转链接" prop="advertUrl">
           <el-input v-model="form.advertUrl" placeholder="请输入跳转链接" />

+ 1 - 1
src/views/platform/decoration/recommend/index.vue

@@ -84,7 +84,7 @@
           <el-switch v-model="sceneForm.showText" />
         </el-form-item>
         <el-form-item label="图片" prop="coverImage">
-          <image-upload v-model="sceneForm.coverImage" :limit="1" />
+          <upload-image v-model="sceneForm.coverImage" :limit="1" />
         </el-form-item>
         <el-form-item label="描述" prop="advertBrief">
           <el-input v-model="sceneForm.advertBrief" type="textarea" :rows="3" placeholder="请输入描述" />

+ 1 - 1
src/views/platform/decoration/solution/index.vue

@@ -88,7 +88,7 @@
           <el-input v-model="form.advertBrief" type="textarea" :rows="3" placeholder="请输入方案描述" />
         </el-form-item>
         <el-form-item label="方案图片" prop="coverImage">
-          <image-upload v-model="form.coverImage" :limit="1" />
+          <upload-image v-model="form.coverImage" :limit="1" />
         </el-form-item>
         <el-form-item label="链接地址" prop="advertUrl">
           <el-input v-model="form.advertUrl" placeholder="请输入链接地址" />

+ 18 - 1
src/views/platform/decoration/special/index.vue

@@ -459,7 +459,7 @@ onMounted(() => {
 
 .product-grid {
   display: grid;
-  grid-template-columns: repeat(5, 1fr);
+  grid-template-columns: repeat(5, minmax(0, 1fr));
   gap: 12px;
   margin-bottom: 12px;
 }
@@ -469,6 +469,9 @@ onMounted(() => {
   background: #fff;
   padding: 15px;
   cursor: pointer;
+  min-width: 0;
+  overflow: hidden;
+  box-sizing: border-box;
 
   &:hover { background: #fafafa; }
 
@@ -491,10 +494,24 @@ onMounted(() => {
     width: 100%;
     height: 200px;
     margin-bottom: 10px;
+    overflow: hidden;
 
     .image {
       width: 100%;
       height: 100%;
+      display: block;
+    }
+
+    :deep(.el-image) {
+      width: 100%;
+      height: 100%;
+      display: block;
+    }
+
+    :deep(.el-image__inner) {
+      width: 100%;
+      height: 100%;
+      object-fit: cover;
     }
 
     .image-slot {

+ 1 - 1
src/views/platform/enterprise/carousel/index.vue

@@ -94,7 +94,7 @@
           <el-input v-model="form.title" placeholder="请输入广告名称" />
         </el-form-item>
         <el-form-item label="广告图片" prop="imageUrl">
-          <image-upload v-model="form.imageUrl" :limit="1" />
+          <upload-image v-model="form.imageUrl" :limit="1" />
         </el-form-item>
         <el-form-item label="开始时间" prop="startTime">
           <el-date-picker

+ 1 - 1
src/views/platform/enterprise/floorAd/index.vue

@@ -93,7 +93,7 @@
             <el-row :gutter="20">
               <el-col :span="12">
                 <el-form-item label="图片:">
-                  <image-upload v-model="form.imageUrl" :limit="1" />
+                  <upload-image v-model="form.imageUrl" :limit="1" />
                 </el-form-item>
               </el-col>
               <el-col :span="12" v-if="activeTab === 'icon'">

+ 1 - 1
src/views/platform/gift/banner/index.vue

@@ -68,7 +68,7 @@
           <el-input v-model="form.title" placeholder="请输入公告标题" />
         </el-form-item>
         <el-form-item label="封面图片" prop="imageUrl">
-          <image-upload v-model="form.imageUrl" :limit="1" />
+          <upload-image v-model="form.imageUrl" :limit="1" />
         </el-form-item>
         <el-form-item label="链接地址" prop="link">
           <el-input v-model="form.link" placeholder="请输入链接地址" />

+ 3 - 3
src/views/platform/gift/carousel/index.vue

@@ -149,7 +149,7 @@
         </el-form-item>
 
         <el-form-item label="图片" prop="imageUrl">
-          <image-upload v-model="form.imageUrl" :limit="1" />
+          <upload-image v-model="form.imageUrl" :limit="1" />
         </el-form-item>
 
         <el-form-item label="描述" prop="remark">
@@ -365,12 +365,12 @@ onMounted(() => {
 .action-link {
   cursor: pointer;
   margin: 0 8px;
-  
+
   &.primary {
     color: #409eff;
     &:hover { color: #66b1ff; }
   }
-  
+
   &.danger {
     color: #f56c6c;
     &:hover { color: #f78989; }

+ 1 - 1
src/views/platform/gift/floorAd/index.vue

@@ -90,7 +90,7 @@
           </el-col>
         </el-row>
         <el-form-item label="封面图片" prop="mainImg">
-          <image-upload v-model="form.mainImg" :limit="1" />
+          <upload-image v-model="form.mainImg" :limit="1" />
         </el-form-item>
       </el-form>
       <template #footer>

+ 1 - 1
src/views/platform/gift/hotCustom/index.vue

@@ -90,7 +90,7 @@
           </el-col>
         </el-row>
         <el-form-item label="封面图片" prop="mainImg">
-          <image-upload v-model="form.mainImg" :limit="1" />
+          <upload-image v-model="form.mainImg" :limit="1" />
         </el-form-item>
       </el-form>
       <template #footer>

+ 3 - 3
src/views/platform/gift/icon/index.vue

@@ -100,7 +100,7 @@
         </el-row>
 
         <el-form-item label="封面图片" prop="imageUrl">
-          <image-upload v-model="form.imageUrl" :limit="1" />
+          <upload-image v-model="form.imageUrl" :limit="1" />
         </el-form-item>
       </el-form>
       <template #footer>
@@ -294,12 +294,12 @@ onMounted(() => {
 .action-link {
   cursor: pointer;
   margin: 0 8px;
-  
+
   &.primary {
     color: #409eff;
     &:hover { color: #66b1ff; }
   }
-  
+
   &.danger {
     color: #f56c6c;
     &:hover { color: #f78989; }

+ 13 - 13
src/views/platform/gift/longAd/index.vue

@@ -8,10 +8,10 @@
 
       <!-- 广告图片卡片 -->
       <el-card class="ad-card" shadow="hover" @click="handleClickAd">
-        <el-image 
-          v-if="adData.imageUrl" 
-          :src="adData.imageUrl" 
-          fit="contain" 
+        <el-image
+          v-if="adData.imageUrl"
+          :src="adData.imageUrl"
+          fit="contain"
           class="ad-image"
         >
           <template #error>
@@ -35,7 +35,7 @@
           </el-form-item>
           <el-form-item label="图片:">
             <div class="upload-area">
-              <image-upload v-model="form.imageUrl" :limit="1" />
+              <upload-image v-model="form.imageUrl" :limit="1" />
               <span class="upload-tip">建议尺寸:1200 x 300 像素</span>
             </div>
           </el-form-item>
@@ -142,11 +142,11 @@ onMounted(() => {
 
 .title-card {
   margin-bottom: 16px;
-  
+
   :deep(.el-card__body) {
     padding: 12px 20px;
   }
-  
+
   .title-text {
     font-size: 14px;
     font-weight: 500;
@@ -158,19 +158,19 @@ onMounted(() => {
   margin-bottom: 16px;
   cursor: pointer;
   transition: all 0.3s;
-  
+
   &:hover {
     box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
   }
-  
+
   :deep(.el-card__body) {
     padding: 0;
   }
-  
+
   .ad-image {
     width: 100%;
     display: block;
-    
+
     :deep(img) {
       width: 100%;
       height: auto;
@@ -189,7 +189,7 @@ onMounted(() => {
   background: #f5f7fa;
   color: #909399;
   font-size: 14px;
-  
+
   .el-icon {
     font-size: 40px;
     margin-bottom: 10px;
@@ -206,7 +206,7 @@ onMounted(() => {
   display: flex;
   flex-direction: column;
   gap: 10px;
-  
+
   .upload-tip {
     color: #909399;
     font-size: 12px;

+ 3 - 3
src/views/platform/gift/recommendAd/index.vue

@@ -68,7 +68,7 @@
           <el-input v-model="form.title" placeholder="请输入公告标题" />
         </el-form-item>
         <el-form-item label="封面图片" prop="imageUrl">
-          <image-upload v-model="form.imageUrl" :limit="1" />
+          <upload-image v-model="form.imageUrl" :limit="1" />
         </el-form-item>
         <el-form-item label="链接地址" prop="link">
           <el-input v-model="form.link" placeholder="请输入链接地址" />
@@ -264,12 +264,12 @@ onMounted(() => {
 .action-link {
   cursor: pointer;
   margin: 0 6px;
-  
+
   &.primary {
     color: #409eff;
     &:hover { color: #66b1ff; }
   }
-  
+
   &.danger {
     color: #f56c6c;
     &:hover { color: #f78989; }

+ 1 - 1
src/views/platform/industrial/brandFloor/index.vue

@@ -87,7 +87,7 @@
         <el-row :gutter="20">
           <el-col :span="24">
             <el-form-item label="图片" prop="imageUrl">
-              <image-upload v-model="form.imageUrl" :limit="1" />
+              <upload-image v-model="form.imageUrl" :limit="1" />
               <div class="upload-tip">请上传大小不超过 <span class="tip-highlight">5MB</span> 格式为 <span class="tip-highlight">png/jpg/jpeg</span> 的文件</div>
             </el-form-item>
           </el-col>

+ 3 - 3
src/views/platform/industrial/carousel/index.vue

@@ -94,7 +94,7 @@
           <el-input v-model="form.title" placeholder="请输入广告名称" />
         </el-form-item>
         <el-form-item label="广告图片" prop="imageUrl">
-          <image-upload v-model="form.imageUrl" :limit="1" />
+          <upload-image v-model="form.imageUrl" :limit="1" />
         </el-form-item>
         <el-form-item label="开始时间" prop="startTime">
           <el-date-picker
@@ -332,12 +332,12 @@ onMounted(() => {
 .action-link {
   cursor: pointer;
   margin: 0 8px;
-  
+
   &.primary {
     color: #409eff;
     &:hover { color: #66b1ff; }
   }
-  
+
   &.danger {
     color: #f56c6c;
     &:hover { color: #f78989; }

+ 100 - 5
src/views/platform/industrial/categoryProduct/index.vue

@@ -53,7 +53,7 @@
     <el-dialog v-model="productDialog.visible" title="推荐商品" width="1000px" append-to-body>
       <div class="product-dialog-header">
         <el-button type="default" @click="handleAddProduct">新增商品</el-button>
-        <el-button type="default">导入商品</el-button>
+        <el-button type="default" @click="handleImportProduct">导入商品</el-button>
       </div>
       <el-table v-loading="productLoading" :data="productList" border max-height="450px">
         <el-table-column label="轮播商品编号" align="center" prop="productNo" width="140" />
@@ -84,7 +84,8 @@
     <!-- 新增商品选择对话框 -->
     <el-dialog v-model="selectProductDialog.visible" title="商品信息" width="800px" append-to-body>
       <div class="select-product-search">
-        <el-input v-model="selectProductQuery.keyword" placeholder="请输入商品编号名称输入搜索" style="width: 350px" />
+        <el-input v-model="selectProductQuery.productNo" placeholder="请输入商品编号" clearable style="width: 220px" />
+        <el-input v-model="selectProductQuery.itemName" placeholder="请输入商品名称" clearable style="width: 220px" />
         <el-button type="primary" @click="handleSearchProduct">搜 索</el-button>
       </div>
       <el-table
@@ -124,6 +125,24 @@
       </template>
     </el-dialog>
 
+    <!-- 导入商品对话框 -->
+    <el-dialog v-model="importProductDialog.visible" title="导入商品" width="500px" append-to-body>
+      <el-form label-width="90px">
+        <el-form-item label="商品编号">
+          <el-input
+            v-model="importProductDialog.productNos"
+            type="textarea"
+            :rows="5"
+            placeholder="请输入商品编号,多个用逗号隔开"
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="confirmImportProducts">确 定</el-button>
+        <el-button @click="importProductDialog.visible = false">取 消</el-button>
+      </template>
+    </el-dialog>
+
     <!-- 添加/编辑对话框 -->
     <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
       <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
@@ -319,14 +338,22 @@ const selectProductLoading = ref(false);
 const selectProductTotal = ref(0);
 const selectedProducts = ref<any[]>([]);
 const selectProductQuery = reactive({
-  keyword: '',
+  productNo: '',
+  itemName: '',
   pageNum: 1,
   pageSize: 10
 });
 
+// 导入商品对话框
+const importProductDialog = reactive({
+  visible: false,
+  productNos: ''
+});
+
 // 打开新增商品对话框
 const handleAddProduct = () => {
-  selectProductQuery.keyword = '';
+  selectProductQuery.productNo = '';
+  selectProductQuery.itemName = '';
   selectProductQuery.pageNum = 1;
   selectedProducts.value = [];
   selectProductDialog.visible = true;
@@ -339,7 +366,8 @@ const getSelectProductList = async () => {
   try {
     const res = await request.get('/product/base/list', {
       params: {
-        itemName: selectProductQuery.keyword,
+        productNo: selectProductQuery.productNo,
+        itemName: selectProductQuery.itemName,
         pageNum: selectProductQuery.pageNum,
         pageSize: selectProductQuery.pageSize
       }
@@ -398,6 +426,73 @@ const handleConfirmSelect = async () => {
   }
 };
 
+// 打开导入商品对话框
+const handleImportProduct = () => {
+  importProductDialog.productNos = '';
+  importProductDialog.visible = true;
+};
+
+// 确认导入商品
+const confirmImportProducts = async () => {
+  const input = importProductDialog.productNos.trim();
+  if (!input) {
+    ElMessage.warning('请输入商品编号');
+    return;
+  }
+  const productNos = input
+    .split(/[,,\s\n]+/)
+    .map((s: string) => s.trim())
+    .filter(Boolean);
+  if (productNos.length === 0) {
+    ElMessage.warning('请输入有效的商品编号');
+    return;
+  }
+  const existingProductNos = productList.value.map((item: any) => item.productNo);
+  const newProductNos = productNos.filter((no: string) => !existingProductNos.includes(no));
+  if (newProductNos.length === 0) {
+    ElMessage.warning('所有商品编号已存在,请勿重复添加');
+    return;
+  }
+  try {
+    const productRes: any = await request.get('/product/base/list', {
+      params: { productNos: newProductNos.join(','), pageSize: 1000 }
+    });
+    const productMap = new Map<string, any>((productRes.rows || []).map((p: any) => [p.productNo, p]));
+    let successCount = 0;
+    const notFound: string[] = [];
+    for (const productNo of newProductNos) {
+      const product = productMap.get(productNo);
+      if (!product) {
+        notFound.push(productNo);
+        continue;
+      }
+      await request.post('/product/categoryRecommendedLink', {
+        categoryId: productDialog.categoryId,
+        productId: product.id,
+        productNo: product.productNo,
+        sort: 0
+      });
+      successCount++;
+    }
+    const skipped = productNos.length - newProductNos.length;
+    const tips: string[] = [];
+    if (successCount > 0) tips.push(`成功导入${successCount}个`);
+    if (skipped > 0) tips.push(`${skipped}个已存在被跳过`);
+    if (notFound.length > 0) tips.push(`${notFound.length}个商品编号不存在`);
+    if (successCount > 0) {
+      ElMessage.success(tips.join(','));
+    } else {
+      ElMessage.warning(tips.join(',') || '未导入任何商品');
+    }
+    importProductDialog.visible = false;
+    if (productDialog.categoryId) {
+      getProductList(productDialog.categoryId);
+    }
+  } catch (error) {
+    ElMessage.error('导入失败');
+  }
+};
+
 // 提交表单
 const submitForm = () => {
   formRef.value?.validate(async (valid: boolean) => {

+ 1 - 1
src/views/platform/industrial/floorAd/index.vue

@@ -115,7 +115,7 @@
               </el-col>
             </el-row>
             <el-form-item label="图片:">
-              <image-upload v-model="form.imageUrl" :limit="1" />
+              <upload-image v-model="form.imageUrl" :limit="1" />
               <div class="upload-tip">请上传大小不超过 <span class="tip-highlight">5MB</span> 格式为 <span class="tip-highlight">png/jpg/jpeg</span> 的文件</div>
             </el-form-item>
             <el-form-item label="描述:">

+ 1 - 1
src/views/platform/industrial/tag/index.vue

@@ -38,7 +38,7 @@
           <el-input v-model="form.link" placeholder="请输入链接" />
         </el-form-item>
         <el-form-item label="图片" prop="imageUrl">
-          <image-upload v-model="form.imageUrl" :limit="1" />
+          <upload-image v-model="form.imageUrl" :limit="1" />
         </el-form-item>
         <el-form-item label="描述" prop="remark">
           <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入描述" />

+ 2 - 2
src/views/platform/operation/gallery/index.vue

@@ -77,7 +77,7 @@
           <el-input v-model="uploadForm.imageName" placeholder="请输入图片名称" />
         </el-form-item>
         <el-form-item label="图片" prop="ossId">
-          <image-upload v-model="uploadForm.ossId" :limit="1" />
+          <upload-image v-model="uploadForm.ossId" :limit="1" />
         </el-form-item>
       </el-form>
       <template #footer>
@@ -89,7 +89,7 @@
     <!-- 编辑对话框 -->
     <el-dialog v-model="editDialog.visible" title="编辑图片" width="500px" append-to-body>
       <div class="edit-image-container">
-        <image-upload v-model="editForm.ossId" :limit="1" />
+        <upload-image v-model="editForm.ossId" :limit="1" />
       </div>
       <template #footer>
         <el-button type="primary" @click="submitEdit">确 认</el-button>

+ 2 - 2
src/views/platform/operation/theme/edit.vue

@@ -125,7 +125,7 @@
         <el-row :gutter="20">
           <el-col :span="8">
             <el-form-item label="封面图片" prop="coverImage">
-              <image-upload v-model="form.coverImage" :limit="1" />
+              <upload-image v-model="form.coverImage" :limit="1" />
             </el-form-item>
           </el-col>
         </el-row>
@@ -133,7 +133,7 @@
         <el-row :gutter="20">
           <el-col :span="8">
             <el-form-item label="内页图片" prop="titlePage">
-              <image-upload v-model="form.titlePage" :limit="1" />
+              <upload-image v-model="form.titlePage" :limit="1" />
             </el-form-item>
           </el-col>
         </el-row>

+ 6 - 6
src/views/platform/ruleCenter/category/index.vue

@@ -60,7 +60,7 @@
     <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
       <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
         <el-form-item label="分类图标" prop="ruleCategoryImage">
-          <image-upload v-model="form.ruleCategoryImage" :limit="1" />
+          <upload-image v-model="form.ruleCategoryImage" :limit="1" />
         </el-form-item>
         <el-form-item label="分类名称" prop="ruleCategoryName">
           <el-input v-model="form.ruleCategoryName" placeholder="请输入分类名称" />
@@ -145,7 +145,7 @@ const flattenTree = (tree: any[], level: number = 1, parentExpanded: boolean = t
   tree.forEach((node, index) => {
     const isLastItem = index === tree.length - 1;
     const newIsLast = [...isLast, isLastItem];
-    
+
     if (parentExpanded) {
       result.push({
         ...node,
@@ -155,7 +155,7 @@ const flattenTree = (tree: any[], level: number = 1, parentExpanded: boolean = t
         expanded: expandedKeys.value.has(node.ruleCategoryNo)
       });
     }
-    
+
     if (node.children && node.children.length > 0) {
       const childExpanded = parentExpanded && expandedKeys.value.has(node.ruleCategoryNo);
       result.push(...flattenTree(node.children, level + 1, childExpanded, newIsLast));
@@ -211,7 +211,7 @@ const getList = async () => {
     const list = res.rows || [];
     categoryList.value = buildTree(list);
     categoryTreeOptions.value = [{ ruleCategoryNo: '', ruleCategoryName: '顶级分类', children: categoryList.value }];
-    
+
     // 默认全部展开
     if (isExpandAll.value) {
       const allKeys = new Set<string>();
@@ -226,7 +226,7 @@ const getList = async () => {
       collectKeys(categoryList.value);
       expandedKeys.value = allKeys;
     }
-    
+
     updateFlatList();
   } catch (error) {
     console.error('加载列表失败', error);
@@ -379,7 +379,7 @@ onMounted(() => {
   cursor: pointer;
   font-size: 10px;
   margin-right: 4px;
-  
+
   &:hover {
     color: #409eff;
   }

+ 1 - 1
src/views/platform/ruleCenter/list/edit.vue

@@ -37,7 +37,7 @@
         </el-form-item>
 
         <el-form-item label="规则图片" prop="ruleImage">
-          <image-upload v-model="form.ruleImage" :limit="1" />
+          <upload-image v-model="form.ruleImage" :limit="1" />
         </el-form-item>
 
         <el-form-item label="规则内容" prop="ruleContent">

+ 17 - 5
src/views/system/basicSetting/index.vue

@@ -176,24 +176,24 @@
         <el-row :gutter="20">
           <el-col :span="8">
             <el-form-item label="平台LOGO" prop="logo">
-              <image-upload v-model="form.logo" :limit="1" />
+              <upload-image v-model="form.logo" :limit="1" />
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="平台图标" prop="icon">
-              <image-upload v-model="form.icon" :limit="1" />
+              <upload-image v-model="form.icon" :limit="1" />
             </el-form-item>
           </el-col>
           <el-col :span="8">
             <el-form-item label="默认头像" prop="defaultAvatar">
-              <image-upload v-model="form.defaultAvatar" :limit="1" />
+              <upload-image v-model="form.defaultAvatar" :limit="1" />
             </el-form-item>
           </el-col>
         </el-row>
 
         <!-- 小程序二维码 -->
         <el-form-item label="小程序二维码" prop="miniQrcode">
-          <image-upload v-model="form.miniQrcode" :limit="1" />
+          <upload-image v-model="form.miniQrcode" :limit="1" />
         </el-form-item>
 
         <!-- 内容 -->
@@ -206,6 +206,16 @@
           <el-input v-model="form.detailDesc" type="textarea" :rows="3" placeholder="请输入详细页描述" />
         </el-form-item>
 
+        <!-- 客户协议 -->
+        <el-form-item label="客户协议" prop="customerLoginAgreement">
+          <editor v-model="form.customerLoginAgreement" :min-height="192" />
+        </el-form-item>
+
+        <!-- 供应商协议 -->
+        <el-form-item label="供应商协议" prop="supplierLoginAgreement">
+          <editor v-model="form.supplierLoginAgreement" :min-height="192" />
+        </el-form-item>
+
         <!-- 提交按钮 -->
         <el-form-item>
           <el-button type="primary" @click="submitForm" v-hasPermi="['system:platformConfig:edit']">提交</el-button>
@@ -256,7 +266,9 @@ const data = reactive({
     detailDesc: '',
     platormDiy: '0', // 平台商城自定义 0不diy 1diy
     industrialDiy: '0', // 工业品商城自定义 0不diy 1diy
-    fuliDiy: '0' // 福利商城自定义 0不diy 1diy
+    fuliDiy: '0', // 福利商城自定义 0不diy 1diy
+    customerLoginAgreement: '', // 客户登录协议
+    supplierLoginAgreement: '' // 供应商登录协议
   } as any,
   rules: {
     platformName: [{ required: true, message: '平台名称不能为空', trigger: 'blur' }],

+ 1 - 1
src/views/system/loginSetting/index.vue

@@ -33,7 +33,7 @@
           </div>
           <div class="image-actions">
             <div class="upload-btn">
-              <image-upload v-model="item.image" :limit="1" :fileSize="5" :isShowTip="false" />
+              <upload-image v-model="item.image" :limit="1" width="100px" height="100px" />
             </div>
             <div class="color-picker">
               <span>背景色:</span>