weixin_52219567 il y a 3 jours
Parent
commit
1c9d13ce82

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

@@ -155,3 +155,23 @@ export function getCustomerProductPage(query: any) {
     params: query
   });
 }
+
+// 获取当前导航配置
+
+export function navigationCurrent(query: any) {
+  return request({
+    url: '/mall/navigationConfig/current',
+    method: 'get',
+    params: query
+  });
+}
+
+// 保存导航配置
+
+export function navigationSave(data: any) {
+  return request({
+    url: '/mall/navigationConfig/save',
+    method: 'post',
+    data: data
+  });
+}

+ 0 - 2
src/components/goodsMini/index.vue

@@ -97,7 +97,6 @@ import { listBase } from '@/api/pmsProduct/base';
 import useDiyStore from '@/store/modules/diy';
 import type { TableInstance } from 'element-plus';
 import { getCustomerProductPage } from '@/api/diy/index';
-import { el } from 'element-plus/es/locale/index.mjs';
 const goodsClassify = ref<any>('');
 const diyStore: any = useDiyStore();
 const showDialog = ref(false);
@@ -128,7 +127,6 @@ const props = defineProps<{
   type?: any;
 }>();
 const onOpen = () => {
-  console.log('打开弹窗');
   showDialog.value = true;
   getList();
 };

+ 1 - 1
src/store/modules/diy.ts

@@ -17,7 +17,7 @@ const useDiyStore = defineStore('diy', {
       type: '', // 页面模板
       typeName: '', // 页面模板名称
       templateName: '', // 页面模板标识
-      isDefault: 0, // 是否默认页面
+      isDefault: 1, // 是否默认页面
       predefineColors: [
         '#F4391c',
         '#ff4500',

+ 75 - 86
src/views/diy/components/edit-goods-list.vue

@@ -38,74 +38,51 @@
               <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>
+              <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 label="选择分类" v-if="diyStore.editComponent.source == 2">
+            <el-cascader
+              v-model="diyStore.editComponent.categoryIds"
+              :options="categoryOptions1"
+              :props="cascaderProps"
+              placeholder="全部分类"
+              clearable
+              collapse-tags
+            />
+          </el-form-item>
+          <el-form-item label="品牌" v-if="diyStore.editComponent.source == 3">
+            <el-select
+              v-model="diyStore.editComponent.brandIds"
+              multiple
+              filterable
+              remote
+              reserve-keyword
+              placeholder="请输入品牌名称"
+              :remote-method="remoteMethod"
+              :loading="Brandloading"
+              style="width: 240px"
+            >
+              <el-option v-for="(item, index) in BrandList" :key="index" :label="item.brandName" :value="item.id" />
+              <template #loading>
+                <svg class="circular" viewBox="0 0 50 50">
+                  <circle class="path" cx="25" cy="25" r="20" fill="none" />
+                </svg>
+              </template>
+            </el-select>
           </el-form-item>
-          <el-form-item label="商品数量" v-if="diyStore.editComponent.source == 'all' || diyStore.editComponent.source == 'category'">
+          <el-form-item label="商品数量" v-if="diyStore.editComponent.source == '2' || diyStore.editComponent.source == '3'">
             <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">
@@ -114,12 +91,6 @@
           <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">
@@ -159,12 +130,9 @@
           <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-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>
@@ -227,38 +195,42 @@
       <slot name="style"></slot>
     </div>
 
-    <goods-dialog ref="goodsDialogRef" :categoryOptions="categoryOptions"></goods-dialog>
+    <goods-mini ref="goodsDialogRef" :categoryOptions="categoryOptions"></goods-mini>
   </div>
 </template>
 
 <script lang="ts" setup>
-// import { getCategoryTree } from '@/addon/shop/api/goods';
+import { categoryTree } from '@/api/pmsProduct/base';
+import { listBrand } from '@/api/product/brand';
 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 categoryOptions = ref<any>([]);
+const categoryOptions1 = ref<any>([]);
+const Brandloading = ref(false);
+const BrandList = ref<any>([]);
 const diyStore: any = useDiyStore();
 diyStore.editComponent.ignore = []; // 忽略公共属性
 
-// 组件验证
-diyStore.editComponent.verify = (index: number) => {
-  const res = { code: true, message: '' };
+const cascaderProps = { multiple: true, value: 'id', label: 'label', children: 'children' };
 
-  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 getCategoryTree = async () => {
+  categoryOptions.value = [];
+  categoryOptions1.value = [];
+  const res = await categoryTree({
+    platform: 0,
+    pageNum: 1,
+    pageSize: 9999
+  });
+  const list = res.data || [];
+  categoryOptions.value = [...list];
+  categoryOptions1.value = [...list];
+  categoryOptions.value.unshift({
+    id: '',
+    label: '全部'
+  });
 };
 
 const categoryShowDialog = ref(false);
@@ -268,6 +240,7 @@ const categoryTable = reactive({
   data: []
 });
 onMounted(() => {
+  getCategoryTree();
   loadCategoryList();
   btnStyleList.forEach((item, index, arr) => {
     if (item.type == 'button') {
@@ -394,6 +367,21 @@ const openDialog = () => {
   goodsDialogRef.value.onOpen();
 };
 
+//品牌输入
+const remoteMethod = (query: string) => {
+  if (query) {
+    Brandloading.value = true;
+    listBrand({ pageNum: 1, pageSize: 100, brandName: query }).then((res) => {
+      Brandloading.value = false;
+      if (res.code == 200) {
+        BrandList.value = res.rows || [];
+      }
+    });
+  } else {
+    BrandList.value = [];
+  }
+};
+
 defineExpose({});
 </script>
 <style lang="scss">
@@ -402,6 +390,7 @@ defineExpose({});
     width: 100px;
   }
 }
+
 .data-num {
   width: 100%;
   font-size: 14px;

+ 2 - 2
src/views/diy/components/edit-shop-goods-detail-basic-info.vue

@@ -68,9 +68,9 @@
         <el-form label-width="100px" class="px-[10px]">
           <el-form-item label="是否展示">
             <el-checkbox-group v-model="saleInfo" @change="saleInfoChange">
-              <el-checkbox label="划线价" value="underlined_price" />
-              <el-checkbox label="累计销量" value="sales" />
               <el-checkbox label="库存" value="stock" />
+              <el-checkbox label="定制" value="sales" />
+              <el-checkbox label="起订量" value="underlined_price" />
             </el-checkbox-group>
           </el-form-item>
         </el-form>

+ 6 - 6
src/views/diy/components/edit-shop-goods-detail-purchase-service.vue

@@ -6,13 +6,13 @@
         <el-form label-width="80px" class="px-[10px]">
           <el-form-item label="是否显示">
             <el-checkbox-group v-model="serviceConfig" @change="serviceConfigChange" :min="2">
-              <el-checkbox label="商品服务" value="goods_service" />
-              <el-checkbox label="规格选择" value="spec_select" />
-              <el-checkbox label="配送信息" value="delivery_info" />
-              <el-checkbox label="领券(领取优惠券)" value="coupons" />
-              <el-checkbox label="优惠活动展示" value="activity" />
+              <el-checkbox label="商品编号" value="goods_service" />
+              <!-- <el-checkbox label="规格选择" value="spec_select" /> -->
+              <el-checkbox label="规格/型号" value="delivery_info" />
+              <el-checkbox label="UPC(69)条码" value="coupons" />
+              <el-checkbox label="供货时间" value="activity" />
             </el-checkbox-group>
-            <div class="text-sm text-gray-400">商品服务最少选择2个</div>
+            <!-- <div class="text-sm text-gray-400">商品服务最少选择2个</div> -->
           </el-form-item>
         </el-form>
       </div>

+ 27 - 28
src/views/diy/edit.ts

@@ -658,7 +658,6 @@ export const data1 = {
             'goods_category': '',
             'goods_category_name': '请选择',
             'goodsIds': [],
-            'goods_ids': [],
             'sortWay': 'default',
             'goodsNameStyle': {
               'color': '#303133',
@@ -803,10 +802,10 @@ export const data1 = {
               {
                 'title': '推荐',
                 'desc': '猜你喜欢',
-                'source': 'all',
+                'source': 'custom',
                 'goods_category': '',
                 'goods_category_name': '请选择',
-                'goods_ids': [],
+                'goodsIds': [],
                 'imageUrl': ''
               }
             ],
@@ -842,7 +841,7 @@ export const data1 = {
           'value': {
             'style': 'style-1',
             'styleName': '风格一',
-            'source': 'all',
+            'source': 'custom',
             'num': 6,
             'couponIds': [],
             'btnText': '立即领取',
@@ -896,11 +895,11 @@ export const data1 = {
           'uses': 0,
           'value': {
             'style': 'style-2',
-            'source': 'all',
+            'source': 'custom',
             'num': 10,
             'goods_category': '',
             'goods_category_name': '请选择',
-            'goods_ids': [],
+            'goodsIds': [],
             'sortWay': 'total_order_num',
             'goodsNameStyle': {
               'color': '#333',
@@ -950,8 +949,8 @@ export const data1 = {
             'priceStyle': {
               'mainColor': '#333333'
             },
-            'source': 'all',
-            'goods_ids': [],
+            'source': 'custom',
+            'goodsIds': [],
             'list': [
               {
                 'title': {
@@ -1063,8 +1062,8 @@ export const data1 = {
                 'name': ''
               }
             },
-            'source': 'all',
-            'goods_ids': [],
+            'source': 'custom',
+            'goodsIds': [],
             'imageHeight': 250,
             'list': [
               {
@@ -1154,11 +1153,11 @@ export const data1 = {
               },
               'otherColor': 'rgba(255, 255, 255, 1)'
             },
-            'source': 'all',
+            'source': 'custom',
             'num': 10,
             'goods_category': '',
             'goods_category_name': '请选择',
-            'goods_ids': [],
+            'goodsIds': [],
             'imgElementRounded': 10
           },
           'template': {
@@ -1263,8 +1262,8 @@ export const data1 = {
   },
   // wap/app/pages/index/index?mode=decorate
   'domain_url': {
-    // 'wap_url': 'http://localhost:5173/wap',
-    'wap_url': 'https://v6.site.niucloud.com/wap'
+    'wap_url': 'http://localhost:5173/wap'
+    // 'wap_url': 'https://v6.site.niucloud.com/wap'
   },
   'global': []
 };
@@ -1495,7 +1494,7 @@ export const data2 = {
           'uses': 1,
           'id': '19m7738ofq68',
           'componentName': 'ShopGoodsDetailAttr',
-          'componentTitle': '商品属性',
+          'componentTitle': '售后服务说明',
           'ignore': [],
           'isShow': true,
           'textColor': '#303133',
@@ -2125,7 +2124,7 @@ export const data3 = {
       'ignore': ['componentBgColor', 'componentBgUrl'],
       'style': 'style-3',
       'styleName': '风格3',
-      'source': 'all',
+      'source': 'custom',
       'num': 6,
       'couponIds': [],
       'btnText': '立即领取',
@@ -2172,8 +2171,8 @@ export const data3 = {
       'priceStyle': {
         'mainColor': '#333333'
       },
-      'source': 'all',
-      'goods_ids': [],
+      'source': 'custom',
+      'goodsIds': [],
       'list': [
         {
           'title': {
@@ -2660,8 +2659,8 @@ export const data3 = {
     //       'name': ''
     //     }
     //   },
-    //   'source': 'all',
-    //   'goods_ids': [],
+    //   'source': 'custom',
+    //   'goodsIds': [],
     //   'imageHeight': '250',
     //   'list': [
     //     {
@@ -2822,10 +2821,10 @@ export const data3 = {
         {
           'title': '推荐',
           'desc': '猜你喜欢',
-          'source': 'all',
+          'source': 'custom',
           'goods_category': '',
           'goods_category_name': '请选择',
-          'goods_ids': [],
+          'goodsIds': [],
           'imageUrl': '',
           'id': '67pl1ysjhr40'
         },
@@ -2833,30 +2832,30 @@ export const data3 = {
           'id': '6z59zcmk4jk0',
           'title': '衣鞋包饰',
           'desc': '分类描述',
-          'source': 'all',
+          'source': 'custom',
           'goods_category': '',
           'goods_category_name': '请选择',
-          'goods_ids': [],
+          'goodsIds': [],
           'imageUrl': ''
         },
         {
           'id': '1cfbll6wnmw0',
           'title': '居家百货',
           'desc': '分类描述',
-          'source': 'all',
+          'source': 'custom',
           'goods_category': '',
           'goods_category_name': '请选择',
-          'goods_ids': [],
+          'goodsIds': [],
           'imageUrl': ''
         },
         {
           'id': '49p79g5l5qs0',
           'title': '食品营养',
           'desc': '分类描述',
-          'source': 'all',
+          'source': 'custom',
           'goods_category': '',
           'goods_category_name': '请选择',
-          'goods_ids': [],
+          'goodsIds': [],
           'imageUrl': ''
         }
       ],

+ 2 - 0
src/views/diy/edit.vue

@@ -293,6 +293,7 @@ const save = (type?: number) => {
     title: diyStore.global.title,
     type: diyStore.type,
     is_default: diyStore.isDefault,
+    isDefault:1,
     value: JSON.stringify({
       global: toRaw(diyStore.global),
       value: toRaw(diyStore.value)
@@ -589,3 +590,4 @@ watch(
   }
 }
 </style>
+

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

@@ -262,8 +262,8 @@ onMounted(() => {
     // 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;
   }
 });
 

+ 293 - 0
src/views/diy/navigation copy.vue

@@ -0,0 +1,293 @@
+<template>
+  <div class="main-container">
+    <el-card class="box-card mt-[15px] !border-none" shadow="never" v-loading="loading">
+      <div class="flex">
+        <div class="w-[360px] h-[400px] absolute mr-[30px] border-[1px] border-gray-300">
+          <div
+            class="flex items-center justify-between absolute h-[60px] left-[0px] right-[0px] bottom-[0px] border-[1px] border-primary"
+            :style="{ 'backgroundColor': diyBottomData.value.backgroundColor }"
+          >
+            <div class="flex flex-1 flex-col items-center justify-center" v-for="(item, index) in diyBottomData.value.list" :key="'b' + index">
+              <el-image
+                class="w-[22px] h-[22px] mb-[5px] leading-1"
+                :src="img(item.iconPath)"
+                fit="cover"
+                v-if="['1', '2'].includes(diyBottomData.value.type.toString())"
+              >
+                <template #error>
+                  <div class="image-slot flex justify-center items-center mt-1">
+                    <el-icon>
+                      <Picture class="text-3xl text-gray-500" />
+                    </el-icon>
+                  </div>
+                </template>
+              </el-image>
+              <span
+                class="text-[12px]"
+                v-if="['1', '3'].includes(diyBottomData.value.type.toString())"
+                :style="{ 'color': diyBottomData.value.textColor }"
+                >{{ item.text }}</span
+              >
+            </div>
+          </div>
+        </div>
+        <div class="flex-1 ml-[430px]">
+          <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]">{{ diyBottomData.info.title }}</span>
+            <span class="text-[14px]">底部导航</span>
+            <span class="text-[12px] ml-[8px] text-gray-500">设置至少添加2个导航,最多添加5个导航</span>
+          </div>
+          <el-form :model="diyBottomData.value" label-width="100px" ref="formRef">
+            <el-tabs v-model="activeName" class="demo-tabs mt-[15px]">
+              <el-tab-pane label="导航图片" name="navPicture">
+                <div ref="navItemRef">
+                  <div
+                    v-for="(item, index) in diyBottomData.value.list"
+                    :key="'a' + index"
+                    :data-id="index"
+                    class="item-wrap border-2 border-dashed pt-[18px] m-[10px] mb-[15px] relative list-item"
+                    :class="{ 'not-sort': useDrag }"
+                  >
+                    <el-form-item label="导航图标">
+                      <div class="flex align-center">
+                        <div class="flex flex-col justify-center items-center">
+                          <upload-image v-model="item.iconPath" width="60px" height="60px" :limit="1" />
+                          <span class="mr-[10px] text-sm">未选中</span>
+                        </div>
+                        <div class="flex flex-col justify-center items-center">
+                          <upload-image v-model="item.iconSelectPath" width="60px" height="60px" :limit="1" />
+                          <span class="mr-[10px] text-sm">选中</span>
+                        </div>
+                      </div>
+                    </el-form-item>
+                    <el-form-item label="导航标题">
+                      <el-input class="!w-[215px]" v-model.trim="item.text" placeholder="请输入标题内容" maxlength="5" show-word-limit />
+                    </el-form-item>
+                    <el-form-item label="导航链接">
+                      <diy-link v-model="item.link" :ignore="['DIY_JUMP_OTHER_APPLET']" @confirm="diyLinkFn" />
+                    </el-form-item>
+                    <el-icon class="close-icon cursor-pointer -top-[11px] -right-[8px]" @click="deleteNav(index)">
+                      <CircleCloseFilled />
+                    </el-icon>
+                  </div>
+                </div>
+
+                <el-button type="primary" class="mt-[15px]" v-show="diyBottomData.value.list.length < 5" @click="addNav">添加导航</el-button>
+              </el-tab-pane>
+              <el-tab-pane label="样式设置" name="setStyle">
+                <el-form-item label="导航类型">
+                  <el-radio-group v-model="diyBottomData.value.type" class="ml-4">
+                    <el-radio label="1" size="large">图文</el-radio>
+                    <el-radio label="2" size="large">图片</el-radio>
+                    <el-radio label="3" size="large">文字</el-radio>
+                  </el-radio-group>
+                </el-form-item>
+                <el-form-item label="文字颜色">
+                  <div class="flex align-center">
+                    <el-color-picker v-model="diyBottomData.value.textColor" />
+                    <el-input class="ml-[10px]" v-model.trim="diyBottomData.value.textColor" disabled />
+                    <el-button class="ml-[10px]" type="primary" @click="diyBottomData.value.textColor = '#333333'">重置</el-button>
+                  </div>
+                </el-form-item>
+                <el-form-item label="文字选中颜色">
+                  <div class="flex align-center">
+                    <el-color-picker v-model="diyBottomData.value.textHoverColor" />
+                    <el-input class="ml-[10px]" v-model.trim="diyBottomData.value.textHoverColor" disabled />
+                    <el-button class="ml-[10px]" type="primary" @click="diyBottomData.value.textHoverColor = '#333333'">重置</el-button>
+                  </div>
+                </el-form-item>
+                <el-form-item label="背景颜色">
+                  <div class="flex align-center">
+                    <el-color-picker v-model="diyBottomData.value.backgroundColor" />
+                    <el-input class="ml-[10px]" v-model.trim="diyBottomData.value.backgroundColor" disabled />
+                    <el-button class="ml-[10px]" type="primary" @click="diyBottomData.value.backgroundColor = '#FFFFFF'">重置</el-button>
+                  </div>
+                </el-form-item>
+              </el-tab-pane>
+            </el-tabs>
+          </el-form>
+        </div>
+      </div>
+    </el-card>
+    <div class="fixed-footer-wrap">
+      <div class="fixed-footer">
+        <el-button type="primary" @click="onSave(formRef)">保存</el-button>
+        <el-button @click="back()">返回</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, onMounted, nextTick } from 'vue';
+import { img } from '@/utils/common';
+import type { FormInstance, ElNotification } from 'element-plus';
+import { ArrowLeft } from '@element-plus/icons-vue';
+// import { getDiyBottomConfig, setDiyBottomConfig } from '@/app/api/diy';
+import Sortable from 'sortablejs';
+import { range } from 'lodash-es';
+import { useRoute, useRouter } from 'vue-router';
+
+const route = useRoute();
+const router = useRouter();
+const pageName = route.meta.title;
+
+const activeName = ref<string>('navPicture');
+const loading = ref<boolean>(false);
+route.query.key = route.query.key || '';
+
+// 底部导航数据
+const diyBottomData = reactive({
+  key: '',
+  info: {},
+  value: {
+    backgroundColor: '#FFFFFF',
+    textColor: '#333333',
+    textHoverColor: '#333333',
+    type: '1',
+    list: []
+  }
+});
+
+// 底部导航项数据
+const diyBottomItemData = reactive({
+  text: '',
+  link: {
+    name: '',
+    title: '',
+    parent: '',
+    url: ''
+  },
+  iconSelectPath: '',
+  iconPath: ''
+});
+
+// 添加导航
+const addNav = (): void => {
+  if (diyBottomData.value.list.length >= 5) return;
+  diyBottomData.value.list.push({ ...diyBottomItemData });
+};
+
+addNav();
+
+// 删除导航
+const deleteNav = (index: any): void => {
+  const data = diyBottomData.value.list;
+  data.splice(index, 1);
+};
+
+const formRef = ref<FormInstance>();
+/**
+ * 获取导航数据
+ */
+const getDiyBottomFn = () => {
+  loading.value = true;
+
+  getDiyBottomConfig({
+    key: route.query.key
+  })
+    .then((res) => {
+      loading.value = false;
+      Object.keys(diyBottomData).forEach((item, index) => {
+        diyBottomData[item] = res.data[item];
+      });
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+getDiyBottomFn();
+
+// 保存导航数据
+const onSave = async (formEl: FormInstance | undefined) => {
+  if (verifyFn()) return false;
+
+  if (loading.value || !formEl) return;
+  await formEl.validate(async (valid) => {
+    if (valid) {
+      loading.value = true;
+      setDiyBottomConfig({ key: diyBottomData.key, value: diyBottomData.value })
+        .then((res) => {
+          loading.value = false;
+        })
+        .catch(() => {
+          loading.value = false;
+        });
+    }
+  });
+};
+
+const back = () => {
+  router.push('/diy/tabbar');
+};
+
+// 验证
+const verifyFn = (): boolean => {
+  if (diyBottomData.value.list.length < 2) {
+    ElNotification({
+      type: 'error',
+      message: '至少添加2个导航'
+    });
+    return true;
+  }
+  try {
+    const msg = ref<string>('');
+    diyBottomData.value.list.forEach((item: any, index) => {
+      if (!item.iconPath) msg.value = `请上传第[${index + 1}]个图标`;
+      if (!item.iconSelectPath) msg.value = `请上传第[${index + 1}]个选中图标`;
+      if (!item.text) msg.value = `请输入第[[${index + 1}]个导航标题`;
+      if (!item.link.url) msg.value = `请选择第[${index + 1}]个导航链接`;
+      if (msg.value) {
+        ElNotification({
+          type: 'error',
+          message: msg.value
+        });
+        throw Error();
+      }
+    });
+  } catch (e) {
+    return true;
+  }
+  return false;
+};
+
+const navItemRef = ref();
+
+onMounted(() => {
+  const sortable = Sortable.create(navItemRef.value, {
+    group: 'item-wrap',
+    animation: 200,
+    filter: '.not-sort', // 过滤.not-sort的元素
+    onEnd: (event) => {
+      const temp = diyBottomData.value.list[event.oldIndex!];
+      diyBottomData.value.list.splice(event.oldIndex!, 1);
+      diyBottomData.value.list.splice(event.newIndex!, 0, temp);
+      nextTick(() => {
+        sortable.sort(
+          range(diyBottomData.value.list.length).map((value) => {
+            return value.toString();
+          })
+        );
+      });
+    }
+  });
+});
+
+const useDrag = ref(false);
+const diyLinkFn = (val) => {
+  useDrag.value = val;
+};
+</script>
+<style lang="scss" scoped>
+.close-icon {
+  display: none;
+  position: absolute !important;
+  font-size: 20px !important;
+  color: #7d7b7b !important;
+}
+
+.list-item:hover .close-icon {
+  display: block;
+}
+</style>

+ 21 - 0
src/views/diy/navigation.vue

@@ -0,0 +1,21 @@
+<template>
+  <div class="main-container">
+    1321321
+  </div>
+</template>
+
+<script lang="ts" setup>
+
+</script>
+<style lang="scss" scoped>
+.close-icon {
+  display: none;
+  position: absolute !important;
+  font-size: 20px !important;
+  color: #7d7b7b !important;
+}
+
+.list-item:hover .close-icon {
+  display: block;
+}
+</style>

+ 213 - 0
src/views/mall/navigation/index.vue

@@ -0,0 +1,213 @@
+<template>
+  <div>
+    <el-card class="box-card mt-[15px] !border-none" shadow="never" v-loading="loading">
+      <div class="flex">
+        <div class="w-[360px] h-[400px] absolute mr-[30px] border-[1px] border-gray-300 border-solid">
+          <div
+            class="flex items-center justify-between absolute h-[60px] left-[0px] right-[0px] bottom-[0px] border-[1px] border-primary"
+            :style="{ 'backgroundColor': diyBottomData.value.bottomNavBgColor }"
+          >
+            <div class="flex flex-1 flex-col items-center justify-center" v-for="(item, index) in diyBottomData.value.list" :key="'b' + index">
+              <el-image
+                class="w-[22px] h-[22px] mb-[5px] leading-1"
+                :src="item.iconNormal"
+                fit="cover"
+                v-if="['1', '2'].includes(diyBottomData.value.type.toString())"
+              >
+                <template #error>
+                  <div class="image-slot flex justify-center items-center mt-1">
+                    <el-icon>
+                      <Picture class="text-3xl text-gray-500" />
+                    </el-icon>
+                  </div>
+                </template>
+              </el-image>
+              <span
+                class="text-[12px]"
+                v-if="['1', '3'].includes(diyBottomData.value.type.toString())"
+                :style="{ 'color': diyBottomData.value.bottomNavTextColor }"
+                >{{ item.text }}</span
+              >
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="flex-1 ml-[430px]">
+        <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>
+          <span class="text-[12px] ml-[8px] text-gray-500">设置至少添加2个导航,最多添加5个导航</span>
+        </div>
+        <el-form :model="diyBottomData.value" label-width="100px" ref="formRef">
+          <el-tabs v-model="activeName" class="demo-tabs mt-[15px]">
+            <el-tab-pane label="导航图片" name="navPicture">
+              <div ref="navItemRef">
+                <div
+                  v-for="(item, index) in diyBottomData.value.list"
+                  :key="'a' + index"
+                  :data-id="index"
+                  class="item-wrap border-1 border-dashed pt-[18px] m-[10px] mb-[15px] relative list-item border-gray-300"
+                  :class="{ 'not-sort': useDrag }"
+                >
+                  <el-form-item label="导航图标">
+                    <div class="flex align-center">
+                      <div class="flex flex-col justify-center items-center">
+                        <upload-image v-model="item.iconNormal" width="60px" height="60px" :limit="1" />
+                        <span class="mr-[10px] text-[12px]">未选中</span>
+                      </div>
+                      <div class="flex flex-col justify-center items-center">
+                        <upload-image v-model="item.iconSelected" width="60px" height="60px" :limit="1" />
+                        <span class="mr-[10px] text-[12px]">选中</span>
+                      </div>
+                    </div>
+                  </el-form-item>
+                  <el-form-item label="导航标题">
+                    <el-input class="!w-[215px]" v-model.trim="item.text" placeholder="请输入标题内容" maxlength="5" show-word-limit />
+                  </el-form-item>
+                  <el-form-item label="导航链接">
+                    <div class="!w-[215px]">
+                      <WebLinkInput v-model="item.linkUrl" placeholder="请输入或选择链接" />
+                    </div>
+                  </el-form-item>
+                  <el-icon class="close-icon cursor-pointer -top-[11px] -right-[8px]" @click="deleteNav(index)">
+                    <CircleCloseFilled />
+                  </el-icon>
+                </div>
+              </div>
+              <el-button type="primary" class="mt-[15px]" @click="onSave">保存</el-button>
+              <el-button type="primary" class="mt-[15px]" v-show="diyBottomData.value.list.length < 5" @click="addNav">添加导航</el-button>
+            </el-tab-pane>
+            <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-group>
+              </el-form-item>
+              <el-form-item label="文字颜色">
+                <div class="flex align-center">
+                  <el-color-picker v-model="diyBottomData.value.bottomNavTextColor" />
+                  <el-input class="ml-[10px]" v-model.trim="diyBottomData.value.bottomNavTextColor" disabled />
+                  <el-button class="ml-[10px]" type="primary" @click="diyBottomData.value.bottomNavTextColor = '#333333'">重置</el-button>
+                </div>
+              </el-form-item>
+              <el-form-item label="文字选中颜色">
+                <div class="flex align-center">
+                  <el-color-picker v-model="diyBottomData.value.bottomNavSelectedColor" />
+                  <el-input class="ml-[10px]" v-model.trim="diyBottomData.value.bottomNavSelectedColor" disabled />
+                  <el-button class="ml-[10px]" type="primary" @click="diyBottomData.value.bottomNavSelectedColor = '#333333'">重置</el-button>
+                </div>
+              </el-form-item>
+              <el-form-item label="背景颜色">
+                <div class="flex align-center">
+                  <el-color-picker v-model="diyBottomData.value.bottomNavBgColor" />
+                  <el-input class="ml-[10px]" v-model.trim="diyBottomData.value.bottomNavBgColor" disabled />
+                  <el-button class="ml-[10px]" type="primary" @click="diyBottomData.value.bottomNavBgColor = '#FFFFFF'">重置</el-button>
+                </div>
+              </el-form-item>
+            </el-tab-pane>
+          </el-tabs>
+        </el-form>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { navigationCurrent, navigationSave } from '@/api/diy/index';
+import Sortable from 'sortablejs';
+import { range } from 'lodash-es';
+const loading = ref<boolean>(false);
+const activeName = ref<string>('navPicture');
+const useDrag = ref(false);
+const id = ref<any>(null);
+// 底部导航数据
+const diyBottomData = reactive({
+  key: '',
+  info: {},
+  value: {
+    bottomNavBgColor: '#FFFFFF',
+    bottomNavTextColor: '#333333',
+    bottomNavSelectedColor: '#333333',
+    type: '1',
+    list: []
+  }
+});
+
+// 底部导航项数据
+const diyBottomItemData = reactive({
+  text: '',
+  linkUrl: '',
+  iconNormal: '',
+  iconSelected: ''
+});
+
+const getData = () => {
+  loading.value = true;
+  navigationCurrent({})
+    .then((res) => {
+      loading.value = false;
+      if (res.code == 200) {
+        id.value = res.data.id;
+        diyBottomData.value.list = res.data.navItems;
+        diyBottomData.value.bottomNavBgColor = res.data.bottomNavBgColor;
+        diyBottomData.value.bottomNavTextColor = res.data.bottomNavTextColor;
+        diyBottomData.value.bottomNavSelectedColor = res.data.bottomNavSelectedColor;
+      }
+    })
+    .catch(() => {
+      loading.value = false;
+    });
+};
+
+// 添加导航
+const addNav = (): void => {
+  if (diyBottomData.value.list.length >= 5) return;
+  diyBottomData.value.list.push({ ...diyBottomItemData });
+};
+
+// 删除导航
+const deleteNav = (index: any): void => {
+  const data = diyBottomData.value.list;
+  data.splice(index, 1);
+};
+
+const onSave = () => {
+  const datas = {
+    bottomNavBgColor: diyBottomData.value.bottomNavBgColor,
+    bottomNavTextColor: diyBottomData.value.bottomNavTextColor,
+    bottomNavSelectedColor: diyBottomData.value.bottomNavSelectedColor,
+    navItems: diyBottomData.value.list,
+    id: id.value
+  };
+  navigationSave(datas)
+    .then((res) => {
+      if (res.code == 200) {
+        getData();
+      }
+    })
+    .catch(() => {});
+};
+
+const diyLinkFn = (val) => {
+  useDrag.value = val;
+};
+
+onMounted(() => {
+  getData();
+});
+</script>
+<style lang="scss" scoped>
+.close-icon {
+  display: none;
+  position: absolute !important;
+  font-size: 20px !important;
+  color: #7d7b7b !important;
+}
+
+.list-item:hover .close-icon {
+  display: block;
+}
+</style>