Bladeren bron

封装拖拽组件,提供移动、新增、删除功能

owen 1 jaar geleden
bovenliggende
commit
59ff6b13fb

+ 47 - 83
src/components/DiyEditor/components/mobile/Carousel/property.vue

@@ -39,87 +39,60 @@
         </el-form-item>
       </el-card>
       <el-card header="内容设置" class="property-group" shadow="never">
-        <el-text type="info" size="small"> 拖动左上角的小圆点可对其排序 </el-text>
-        <template v-if="formData.items[0]">
-          <draggable
-            :list="formData.items"
-            :force-fallback="true"
-            :animation="200"
-            handle=".drag-icon"
-            class="m-t-8px"
-            item-key="index"
-          >
-            <template #item="{ element, index }">
-              <div class="content mb-4px flex flex-col gap-4px rounded bg-gray-50 p-8px">
-                <div
-                  class="m--8px m-b-8px flex flex-row items-center justify-between bg-gray-100 p-8px"
-                >
-                  <Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
-                  <Icon
-                    icon="ep:delete"
-                    class="cursor-pointer text-red-5"
-                    @click="handleDeleteImage(index)"
-                    v-if="formData.items.length > 1"
-                  />
-                </div>
-                <el-form-item label="类型" prop="type" class="m-b-8px!" label-width="50px">
-                  <el-radio-group v-model="element.type">
-                    <el-radio label="img">图片</el-radio>
-                    <el-radio label="video">视频</el-radio>
-                  </el-radio-group>
-                </el-form-item>
-                <el-form-item
-                  label="图片"
-                  class="m-b-8px!"
-                  label-width="50px"
-                  v-if="element.type === 'img'"
-                >
-                  <UploadImg
-                    v-model="element.imgUrl"
-                    draggable="false"
-                    height="80px"
-                    width="100%"
-                    class="min-w-80px"
-                  />
-                </el-form-item>
-                <template v-else>
-                  <el-form-item label="封面" class="m-b-8px!" label-width="50px">
-                    <UploadImg
-                      v-model="element.imgUrl"
-                      draggable="false"
-                      height="80px"
-                      width="100%"
-                      class="min-w-80px"
-                    />
-                  </el-form-item>
-                  <el-form-item label="视频" class="m-b-8px!" label-width="50px">
-                    <UploadFile
-                      v-model="element.videoUrl"
-                      :file-type="['mp4']"
-                      :limit="1"
-                      :file-size="100"
-                      class="min-w-80px"
-                    />
-                  </el-form-item>
-                </template>
-                <el-form-item label="链接" class="m-b-8px!" label-width="50px">
-                  <AppLinkInput v-model="element.url" />
-                </el-form-item>
-              </div>
+        <Draggable v-model="formData.items" :empty-item="{ type: 'img' }">
+          <template #default="{ element }">
+            <el-form-item label="类型" prop="type" class="m-b-8px!" label-width="40px">
+              <el-radio-group v-model="element.type">
+                <el-radio label="img">图片</el-radio>
+                <el-radio label="video">视频</el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item
+              label="图片"
+              class="m-b-8px!"
+              label-width="40px"
+              v-if="element.type === 'img'"
+            >
+              <UploadImg
+                v-model="element.imgUrl"
+                draggable="false"
+                height="80px"
+                width="100%"
+                class="min-w-80px"
+              />
+            </el-form-item>
+            <template v-else>
+              <el-form-item label="封面" class="m-b-8px!" label-width="40px">
+                <UploadImg
+                  v-model="element.imgUrl"
+                  draggable="false"
+                  height="80px"
+                  width="100%"
+                  class="min-w-80px"
+                />
+              </el-form-item>
+              <el-form-item label="视频" class="m-b-8px!" label-width="40px">
+                <UploadFile
+                  v-model="element.videoUrl"
+                  :file-type="['mp4']"
+                  :limit="1"
+                  :file-size="100"
+                  class="min-w-80px"
+                />
+              </el-form-item>
             </template>
-          </draggable>
-        </template>
-        <el-button @click="handleAddImage" type="primary" plain class="w-full">
-          添加图片
-        </el-button>
+            <el-form-item label="链接" class="m-b-8px!" label-width="40px">
+              <AppLinkInput v-model="element.url" />
+            </el-form-item>
+          </template>
+        </Draggable>
       </el-card>
     </el-form>
   </ComponentContainerProperty>
 </template>
 
 <script setup lang="ts">
-import draggable from 'vuedraggable' //拖拽组件
-import { CarouselItemProperty, CarouselProperty } from './config'
+import { CarouselProperty } from './config'
 import { usePropertyForm } from '@/components/DiyEditor/util'
 
 // 轮播图属性面板
@@ -128,15 +101,6 @@ defineOptions({ name: 'CarouselProperty' })
 const props = defineProps<{ modelValue: CarouselProperty }>()
 const emit = defineEmits(['update:modelValue'])
 const { formData } = usePropertyForm(props.modelValue, emit)
-
-// 添加图片
-const handleAddImage = () => {
-  formData.value.items.push({} as CarouselItemProperty)
-}
-// 删除图片
-const handleDeleteImage = (index: number) => {
-  formData.value.items.splice(index, 1)
-}
 </script>
 
 <style scoped lang="scss"></style>

+ 31 - 62
src/components/DiyEditor/components/mobile/MenuGrid/property.vue

@@ -9,72 +9,50 @@
         </el-radio-group>
       </el-form-item>
 
-      <el-text tag="p"> 菜单设置 </el-text>
-      <el-text type="info" size="small"> 拖动左侧的小圆点可以调整顺序 </el-text>
-      <template v-if="formData.list.length">
-        <VueDraggable
-          class="m-t-8px"
-          :list="formData.list"
-          item-key="index"
-          handle=".drag-icon"
-          :forceFallback="true"
-          :animation="200"
-        >
-          <template #item="{ element, index }">
-            <div class="mb-4px flex flex-col gap-4px rounded bg-gray-100 p-8px">
-              <div class="flex flex-row justify-between">
-                <Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
-                <Icon icon="ep:delete" class="text-red-500" @click="handleDeleteMenu(index)" />
-              </div>
-              <el-form-item label="图标" prop="iconUrl">
-                <UploadImg v-model="element.iconUrl" height="80px" width="80px">
-                  <template #tip> 建议尺寸:44 * 44 </template>
-                </UploadImg>
+      <el-card header="菜单设置" class="property-group" shadow="never">
+        <Draggable v-model="formData.list" :empty-item="EMPTY_MENU_GRID_ITEM_PROPERTY">
+          <template #default="{ element }">
+            <el-form-item label="图标" prop="iconUrl">
+              <UploadImg v-model="element.iconUrl" height="80px" width="80px">
+                <template #tip> 建议尺寸:44 * 44 </template>
+              </UploadImg>
+            </el-form-item>
+            <el-form-item label="标题" prop="title">
+              <InputWithColor v-model="element.title" v-model:color="element.titleColor" />
+            </el-form-item>
+            <el-form-item label="副标题" prop="subtitle">
+              <InputWithColor v-model="element.subtitle" v-model:color="element.subtitleColor" />
+            </el-form-item>
+            <el-form-item label="链接" prop="url">
+              <AppLinkInput v-model="element.url" />
+            </el-form-item>
+            <el-form-item label="显示角标" prop="badge.show">
+              <el-switch v-model="element.badge.show" />
+            </el-form-item>
+            <template v-if="element.badge.show">
+              <el-form-item label="角标内容" prop="badge.text">
+                <InputWithColor
+                  v-model="element.badge.text"
+                  v-model:color="element.badge.textColor"
+                />
               </el-form-item>
-              <el-form-item label="标题" prop="title">
-                <InputWithColor v-model="element.title" v-model:color="element.titleColor" />
+              <el-form-item label="背景颜色" prop="badge.bgColor">
+                <ColorInput v-model="element.badge.bgColor" />
               </el-form-item>
-              <el-form-item label="副标题" prop="subtitle">
-                <InputWithColor v-model="element.subtitle" v-model:color="element.subtitleColor" />
-              </el-form-item>
-              <el-form-item label="链接" prop="url">
-                <AppLinkInput v-model="element.url" />
-              </el-form-item>
-              <el-form-item label="显示角标" prop="badge.show">
-                <el-switch v-model="element.badge.show" />
-              </el-form-item>
-              <template v-if="element.badge.show">
-                <el-form-item label="角标内容" prop="badge.text">
-                  <InputWithColor
-                    v-model="element.badge.text"
-                    v-model:color="element.badge.textColor"
-                  />
-                </el-form-item>
-                <el-form-item label="背景颜色" prop="badge.bgColor">
-                  <ColorInput v-model="element.badge.bgColor" />
-                </el-form-item>
-              </template>
-            </div>
+            </template>
           </template>
-        </VueDraggable>
-      </template>
-      <el-form-item label-width="0">
-        <el-button @click="handleAddMenu" type="primary" plain class="m-t-8px w-full">
-          <Icon icon="ep:plus" class="mr-5px" /> 添加菜单
-        </el-button>
-      </el-form-item>
+        </Draggable>
+      </el-card>
     </el-form>
   </ComponentContainerProperty>
 </template>
 
 <script setup lang="ts">
-import VueDraggable from 'vuedraggable'
 import { usePropertyForm } from '@/components/DiyEditor/util'
 import {
   EMPTY_MENU_GRID_ITEM_PROPERTY,
   MenuGridProperty
 } from '@/components/DiyEditor/components/mobile/MenuGrid/config'
-import { cloneDeep } from 'lodash-es'
 
 /** 宫格导航属性面板 */
 defineOptions({ name: 'MenuGridProperty' })
@@ -82,15 +60,6 @@ defineOptions({ name: 'MenuGridProperty' })
 const props = defineProps<{ modelValue: MenuGridProperty }>()
 const emit = defineEmits(['update:modelValue'])
 const { formData } = usePropertyForm(props.modelValue, emit)
-
-/* 添加菜单 */
-const handleAddMenu = () => {
-  formData.value.list.push(cloneDeep(EMPTY_MENU_GRID_ITEM_PROPERTY))
-}
-/* 删除菜单 */
-const handleDeleteMenu = (index: number) => {
-  formData.value.list.splice(index, 1)
-}
 </script>
 
 <style scoped lang="scss"></style>

+ 18 - 48
src/components/DiyEditor/components/mobile/MenuList/property.vue

@@ -5,55 +5,34 @@
 
     <!-- 表单 -->
     <el-form label-width="60px" :model="formData" class="m-t-8px">
-      <div v-if="formData.list.length">
-        <VueDraggable
-          :list="formData.list"
-          item-key="index"
-          handle=".drag-icon"
-          :forceFallback="true"
-          :animation="200"
-        >
-          <template #item="{ element, index }">
-            <div class="mb-4px flex flex-col gap-4px rounded bg-gray-100 p-8px">
-              <div class="flex flex-row justify-between">
-                <Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
-                <Icon icon="ep:delete" class="text-red-500" @click="handleDeleteMenu(index)" />
-              </div>
-              <el-form-item label="图标" prop="iconUrl">
-                <UploadImg v-model="element.iconUrl" height="80px" width="80px">
-                  <template #tip> 建议尺寸:44 * 44 </template>
-                </UploadImg>
-              </el-form-item>
-              <el-form-item label="标题" prop="title">
-                <InputWithColor v-model="element.title" v-model:color="element.titleColor" />
-              </el-form-item>
-              <el-form-item label="副标题" prop="subtitle">
-                <InputWithColor v-model="element.subtitle" v-model:color="element.subtitleColor" />
-              </el-form-item>
-              <el-form-item label="链接" prop="url">
-                <AppLinkInput v-model="element.url" />
-              </el-form-item>
-            </div>
-          </template>
-        </VueDraggable>
-      </div>
-      <el-form-item label-width="0">
-        <el-button @click="handleAddMenu" type="primary" plain class="m-t-8px w-full">
-          <Icon icon="ep:plus" class="mr-5px" /> 添加菜单
-        </el-button>
-      </el-form-item>
+      <Draggable v-model="formData.list" :empty-item="EMPTY_MENU_LIST_ITEM_PROPERTY">
+        <template #default="{ element }">
+          <el-form-item label="图标" prop="iconUrl">
+            <UploadImg v-model="element.iconUrl" height="80px" width="80px">
+              <template #tip> 建议尺寸:44 * 44 </template>
+            </UploadImg>
+          </el-form-item>
+          <el-form-item label="标题" prop="title">
+            <InputWithColor v-model="element.title" v-model:color="element.titleColor" />
+          </el-form-item>
+          <el-form-item label="副标题" prop="subtitle">
+            <InputWithColor v-model="element.subtitle" v-model:color="element.subtitleColor" />
+          </el-form-item>
+          <el-form-item label="链接" prop="url">
+            <AppLinkInput v-model="element.url" />
+          </el-form-item>
+        </template>
+      </Draggable>
     </el-form>
   </ComponentContainerProperty>
 </template>
 
 <script setup lang="ts">
-import VueDraggable from 'vuedraggable'
 import { usePropertyForm } from '@/components/DiyEditor/util'
 import {
   EMPTY_MENU_LIST_ITEM_PROPERTY,
   MenuListProperty
 } from '@/components/DiyEditor/components/mobile/MenuList/config'
-import { cloneDeep } from 'lodash-es'
 
 /** 列表导航属性面板 */
 defineOptions({ name: 'MenuListProperty' })
@@ -61,15 +40,6 @@ defineOptions({ name: 'MenuListProperty' })
 const props = defineProps<{ modelValue: MenuListProperty }>()
 const emit = defineEmits(['update:modelValue'])
 const { formData } = usePropertyForm(props.modelValue, emit)
-
-/* 添加菜单 */
-const handleAddMenu = () => {
-  formData.value.list.push(cloneDeep(EMPTY_MENU_LIST_ITEM_PROPERTY))
-}
-/* 删除菜单 */
-const handleDeleteMenu = (index: number) => {
-  formData.value.list.splice(index, 1)
-}
 </script>
 
 <style scoped lang="scss"></style>

+ 28 - 58
src/components/DiyEditor/components/mobile/MenuSwiper/property.vue

@@ -22,63 +22,42 @@
         </el-radio-group>
       </el-form-item>
 
-      <el-text tag="p"> 菜单设置 </el-text>
-      <el-text type="info" size="small"> 拖动左侧的小圆点可以调整顺序 </el-text>
-      <template v-if="formData.list.length">
-        <VueDraggable
-          class="m-t-8px"
-          :list="formData.list"
-          item-key="index"
-          handle=".drag-icon"
-          :forceFallback="true"
-          :animation="200"
-        >
-          <template #item="{ element, index }">
-            <div class="mb-4px flex flex-col gap-4px rounded bg-gray-100 p-8px">
-              <div class="flex flex-row justify-between">
-                <Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
-                <Icon icon="ep:delete" class="text-red-500" @click="handleDeleteMenu(index)" />
-              </div>
-              <el-form-item label="图标" prop="iconUrl">
-                <UploadImg v-model="element.iconUrl" height="80px" width="80px">
-                  <template #tip> 建议尺寸:98 * 98 </template>
-                </UploadImg>
+      <el-card header="菜单设置" class="property-group" shadow="never">
+        <Draggable v-model="formData.list" :empty-item="cloneDeep(EMPTY_MENU_SWIPER_ITEM_PROPERTY">
+          <template #default="{ element }">
+            <el-form-item label="图标" prop="iconUrl">
+              <UploadImg v-model="element.iconUrl" height="80px" width="80px">
+                <template #tip> 建议尺寸:98 * 98 </template>
+              </UploadImg>
+            </el-form-item>
+            <el-form-item label="标题" prop="title">
+              <InputWithColor v-model="element.title" v-model:color="element.titleColor" />
+            </el-form-item>
+            <el-form-item label="链接" prop="url">
+              <AppLinkInput v-model="element.url" />
+            </el-form-item>
+            <el-form-item label="显示角标" prop="badge.show">
+              <el-switch v-model="element.badge.show" />
+            </el-form-item>
+            <template v-if="element.badge.show">
+              <el-form-item label="角标内容" prop="badge.text">
+                <InputWithColor
+                  v-model="element.badge.text"
+                  v-model:color="element.badge.textColor"
+                />
               </el-form-item>
-              <el-form-item label="标题" prop="title">
-                <InputWithColor v-model="element.title" v-model:color="element.titleColor" />
+              <el-form-item label="背景颜色" prop="badge.bgColor">
+                <ColorInput v-model="element.badge.bgColor" />
               </el-form-item>
-              <el-form-item label="链接" prop="url">
-                <AppLinkInput v-model="element.url" />
-              </el-form-item>
-              <el-form-item label="显示角标" prop="badge.show">
-                <el-switch v-model="element.badge.show" />
-              </el-form-item>
-              <template v-if="element.badge.show">
-                <el-form-item label="角标内容" prop="badge.text">
-                  <InputWithColor
-                    v-model="element.badge.text"
-                    v-model:color="element.badge.textColor"
-                  />
-                </el-form-item>
-                <el-form-item label="背景颜色" prop="badge.bgColor">
-                  <ColorInput v-model="element.badge.bgColor" />
-                </el-form-item>
-              </template>
-            </div>
+            </template>
           </template>
-        </VueDraggable>
-      </template>
-      <el-form-item label-width="0">
-        <el-button @click="handleAddMenu" type="primary" plain class="m-t-8px w-full">
-          <Icon icon="ep:plus" class="mr-5px" /> 添加菜单
-        </el-button>
-      </el-form-item>
+        </Draggable>
+      </el-card>
     </el-form>
   </ComponentContainerProperty>
 </template>
 
 <script setup lang="ts">
-import VueDraggable from 'vuedraggable'
 import { usePropertyForm } from '@/components/DiyEditor/util'
 import {
   EMPTY_MENU_SWIPER_ITEM_PROPERTY,
@@ -92,15 +71,6 @@ defineOptions({ name: 'MenuSwiperProperty' })
 const props = defineProps<{ modelValue: MenuSwiperProperty }>()
 const emit = defineEmits(['update:modelValue'])
 const { formData } = usePropertyForm(props.modelValue, emit)
-
-/* 添加菜单 */
-const handleAddMenu = () => {
-  formData.value.list.push(cloneDeep(EMPTY_MENU_SWIPER_ITEM_PROPERTY))
-}
-/* 删除菜单 */
-const handleDeleteMenu = (index: number) => {
-  formData.value.list.splice(index, 1)
-}
 </script>
 
 <style scoped lang="scss"></style>

+ 9 - 2
src/components/DiyEditor/components/mobile/NoticeBar/config.ts

@@ -1,4 +1,4 @@
-import { DiyComponent } from '@/components/DiyEditor/util'
+import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
 
 /** 公告栏属性 */
 export interface NoticeBarProperty {
@@ -10,6 +10,8 @@ export interface NoticeBarProperty {
   backgroundColor: string
   // 文字颜色
   textColor: string
+  // 组件样式
+  style: ComponentStyle
 }
 
 /** 内容属性 */
@@ -34,6 +36,11 @@ export const component = {
       }
     ],
     backgroundColor: '#fff',
-    textColor: '#333'
+    textColor: '#333',
+    style: {
+      bgType: 'color',
+      bgColor: '#fff',
+      marginBottom: 8
+    } as ComponentStyle
   }
 } as DiyComponent<NoticeBarProperty>

+ 27 - 57
src/components/DiyEditor/components/mobile/NoticeBar/property.vue

@@ -1,58 +1,37 @@
 <template>
-  <el-form label-width="80px" :model="formData" :rules="rules">
-    <el-form-item label="公告图标" prop="iconUrl">
-      <UploadImg v-model="formData.iconUrl" height="48px">
-        <template #tip>建议尺寸:24 * 24</template>
-      </UploadImg>
-    </el-form-item>
-    <el-form-item label="背景颜色" prop="backgroundColor">
-      <ColorInput v-model="formData.backgroundColor" />
-    </el-form-item>
-    <el-form-item label="文字颜色" prop="文字颜色">
-      <ColorInput v-model="formData.textColor" />
-    </el-form-item>
-    <el-text tag="p"> 公告内容 </el-text>
-    <el-text type="info" size="small"> 拖动左上角的小圆点可以调整热词顺序 </el-text>
-    <template v-if="formData.contents.length">
-      <VueDraggable
-        :list="formData.contents"
-        item-key="index"
-        handle=".drag-icon"
-        :forceFallback="true"
-        :animation="200"
-        class="m-t-8px"
-      >
-        <template #item="{ element, index }">
-          <div class="mb-4px flex flex-row gap-4px rounded bg-gray-100 p-8px">
-            <div class="flex flex-col items-start justify-between">
-              <Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
-              <Icon
-                icon="ep:delete"
-                class="cursor-pointer text-red-5"
-                @click="handleDeleteContent(index)"
-                v-if="formData.contents.length > 1"
-              />
-            </div>
-            <div class="w-full flex flex-col gap-8px">
+  <ComponentContainerProperty v-model="formData.style">
+    <el-form label-width="80px" :model="formData" :rules="rules">
+      <el-form-item label="公告图标" prop="iconUrl">
+        <UploadImg v-model="formData.iconUrl" height="48px">
+          <template #tip>建议尺寸:24 * 24</template>
+        </UploadImg>
+      </el-form-item>
+      <el-form-item label="背景颜色" prop="backgroundColor">
+        <ColorInput v-model="formData.backgroundColor" />
+      </el-form-item>
+      <el-form-item label="文字颜色" prop="文字颜色">
+        <ColorInput v-model="formData.textColor" />
+      </el-form-item>
+
+      <el-card header="公告内容" class="property-group" shadow="never">
+        <Draggable v-model="formData.contents">
+          <template #default="{ element }">
+            <el-form-item label="公告" prop="text" label-width="40px">
               <el-input v-model="element.text" placeholder="请输入公告" />
+            </el-form-item>
+            <el-form-item label="链接" prop="url" label-width="40px">
               <AppLinkInput v-model="element.url" />
-            </div>
-          </div>
-        </template>
-      </VueDraggable>
-    </template>
-    <el-form-item label-width="0">
-      <el-button @click="handleAddContent" type="primary" plain class="m-t-8px w-full">
-        添加内容
-      </el-button>
-    </el-form-item>
-  </el-form>
+            </el-form-item>
+          </template>
+        </Draggable>
+      </el-card>
+    </el-form>
+  </ComponentContainerProperty>
 </template>
 
 <script setup lang="ts">
-import { NoticeBarProperty, NoticeContentProperty } from './config'
+import { NoticeBarProperty } from './config'
 import { usePropertyForm } from '@/components/DiyEditor/util'
-import VueDraggable from 'vuedraggable'
 // 通知栏属性面板
 defineOptions({ name: 'NoticeBarProperty' })
 // 表单校验
@@ -63,15 +42,6 @@ const rules = {
 const props = defineProps<{ modelValue: NoticeBarProperty }>()
 const emit = defineEmits(['update:modelValue'])
 const { formData } = usePropertyForm(props.modelValue, emit)
-
-/* 添加公告 */
-const handleAddContent = () => {
-  formData.value.contents.push({} as NoticeContentProperty)
-}
-/* 删除公告 */
-const handleDeleteContent = (index: number) => {
-  formData.value.contents.splice(index, 1)
-}
 </script>
 
 <style scoped lang="scss"></style>

+ 51 - 77
src/components/DiyEditor/components/mobile/SearchBar/property.vue

@@ -1,81 +1,64 @@
 <template>
   <ComponentContainerProperty v-model="formData.style">
-    <el-text tag="p"> 搜索热词 </el-text>
-    <el-text type="info" size="small"> 拖动左侧的小圆点可以调整热词顺序 </el-text>
-
     <!-- 表单 -->
     <el-form label-width="80px" :model="formData" class="m-t-8px">
-      <div v-if="formData.hotKeywords.length">
-        <VueDraggable
-          :list="formData.hotKeywords"
-          item-key="index"
-          handle=".drag-icon"
-          :forceFallback="true"
-          :animation="200"
-        >
-          <template #item="{ index }">
-            <div class="mb-4px flex flex-row items-center gap-4px rounded bg-gray-100 p-8px">
-              <Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
-              <el-input v-model="formData.hotKeywords[index]" placeholder="请输入热词" />
-              <Icon icon="ep:delete" class="text-red-500" @click="deleteHotWord(index)" />
-            </div>
+      <el-card header="搜索热词" class="property-group" shadow="never">
+        <Draggable v-model="formData.hotKeywords" :empty-item="''">
+          <template #default="{ index }">
+            <el-input v-model="formData.hotKeywords[index]" placeholder="请输入热词" />
           </template>
-        </VueDraggable>
-      </div>
-      <el-form-item label-width="0">
-        <el-button @click="handleAddHotWord" type="primary" plain class="m-t-8px w-full">
-          添加热词
-        </el-button>
-      </el-form-item>
-      <el-form-item label="框体样式">
-        <el-radio-group v-model="formData!.borderRadius">
-          <el-tooltip content="方形" placement="top">
-            <el-radio-button :label="0">
-              <Icon icon="tabler:input-search" />
-            </el-radio-button>
-          </el-tooltip>
-          <el-tooltip content="圆形" placement="top">
-            <el-radio-button :label="10">
-              <Icon icon="iconoir:input-search" />
-            </el-radio-button>
-          </el-tooltip>
-        </el-radio-group>
-      </el-form-item>
-      <el-form-item label="提示文字" prop="placeholder">
-        <el-input v-model="formData.placeholder" />
-      </el-form-item>
-      <el-form-item label="文本位置" prop="placeholderPosition">
-        <el-radio-group v-model="formData!.placeholderPosition">
-          <el-tooltip content="居左" placement="top">
-            <el-radio-button label="left">
-              <Icon icon="ant-design:align-left-outlined" />
-            </el-radio-button>
-          </el-tooltip>
-          <el-tooltip content="居中" placement="top">
-            <el-radio-button label="center">
-              <Icon icon="ant-design:align-center-outlined" />
-            </el-radio-button>
-          </el-tooltip>
-        </el-radio-group>
-      </el-form-item>
-      <el-form-item label="扫一扫" prop="showScan">
-        <el-switch v-model="formData!.showScan" />
-      </el-form-item>
-      <el-form-item label="框体高度" prop="height">
-        <el-slider v-model="formData!.height" :max="50" :min="28" show-input input-size="small" />
-      </el-form-item>
-      <el-form-item label="框体颜色" prop="backgroundColor">
-        <ColorInput v-model="formData.backgroundColor" />
-      </el-form-item>
-      <el-form-item class="lef" label="文本颜色" prop="textColor">
-        <ColorInput v-model="formData.textColor" />
-      </el-form-item>
+        </Draggable>
+      </el-card>
+      <el-card header="搜索样式" class="property-group" shadow="never">
+        <el-form-item label="框体样式">
+          <el-radio-group v-model="formData!.borderRadius">
+            <el-tooltip content="方形" placement="top">
+              <el-radio-button :label="0">
+                <Icon icon="tabler:input-search" />
+              </el-radio-button>
+            </el-tooltip>
+            <el-tooltip content="圆形" placement="top">
+              <el-radio-button :label="10">
+                <Icon icon="iconoir:input-search" />
+              </el-radio-button>
+            </el-tooltip>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="提示文字" prop="placeholder">
+          <el-input v-model="formData.placeholder" />
+        </el-form-item>
+        <el-form-item label="文本位置" prop="placeholderPosition">
+          <el-radio-group v-model="formData!.placeholderPosition">
+            <el-tooltip content="居左" placement="top">
+              <el-radio-button label="left">
+                <Icon icon="ant-design:align-left-outlined" />
+              </el-radio-button>
+            </el-tooltip>
+            <el-tooltip content="居中" placement="top">
+              <el-radio-button label="center">
+                <Icon icon="ant-design:align-center-outlined" />
+              </el-radio-button>
+            </el-tooltip>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="扫一扫" prop="showScan">
+          <el-switch v-model="formData!.showScan" />
+        </el-form-item>
+        <el-form-item label="框体高度" prop="height">
+          <el-slider v-model="formData!.height" :max="50" :min="28" show-input input-size="small" />
+        </el-form-item>
+        <el-form-item label="框体颜色" prop="backgroundColor">
+          <ColorInput v-model="formData.backgroundColor" />
+        </el-form-item>
+        <el-form-item class="lef" label="文本颜色" prop="textColor">
+          <ColorInput v-model="formData.textColor" />
+        </el-form-item>
+      </el-card>
     </el-form>
   </ComponentContainerProperty>
 </template>
 
 <script setup lang="ts">
-import VueDraggable from 'vuedraggable'
 import { usePropertyForm } from '@/components/DiyEditor/util'
 import { SearchProperty } from '@/components/DiyEditor/components/mobile/SearchBar/config'
 
@@ -85,15 +68,6 @@ defineOptions({ name: 'SearchProperty' })
 const props = defineProps<{ modelValue: SearchProperty }>()
 const emit = defineEmits(['update:modelValue'])
 const { formData } = usePropertyForm(props.modelValue, emit)
-
-/* 添加热词 */
-const handleAddHotWord = () => {
-  formData.value.hotKeywords.push('')
-}
-/* 删除热词 */
-const deleteHotWord = (index: number) => {
-  formData.value.hotKeywords.splice(index, 1)
-}
 </script>
 
 <style scoped lang="scss"></style>

+ 9 - 2
src/components/DiyEditor/components/mobile/TabBar/index.vue

@@ -12,7 +12,13 @@
       }"
     >
       <div v-for="(item, index) in property.items" :key="index" class="tab-bar-item">
-        <img :src="index === 0 ? item.activeIconUrl : item.iconUrl" alt="" />
+        <el-image :src="index === 0 ? item.activeIconUrl : item.iconUrl">
+          <template #error>
+            <div class="h-full w-full flex items-center justify-center">
+              <Icon icon="ep:picture" />
+            </div>
+          </template>
+        </el-image>
         <span :style="{ color: index === 0 ? property.style.activeColor : property.style.color }">
           {{ item.text }}
         </span>
@@ -48,7 +54,8 @@ defineProps<{ property: TabBarProperty }>()
       align-items: center;
       justify-content: center;
 
-      img {
+      :deep(img),
+      .el-icon {
         width: 26px;
         height: 26px;
         border-radius: 4px;

+ 28 - 73
src/components/DiyEditor/components/mobile/TabBar/property.vue

@@ -42,80 +42,44 @@
 
       <el-text tag="p">图标设置</el-text>
       <el-text type="info" size="small"> 拖动左上角的小圆点可对其排序, 图标建议尺寸 44*44 </el-text>
-      <draggable
-        :list="formData!.items"
-        item-key="index"
-        :forceFallback="true"
-        :animation="200"
-        handle=".drag-icon"
-        class="m-t-8px"
-      >
-        <template #item="{ element, index }">
-          <div class="mb-4px flex flex-row gap-4px rounded bg-gray-100 p-8px">
-            <div class="flex flex-col items-start justify-between">
-              <Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
-              <Icon
-                icon="ep:delete"
-                class="cursor-pointer text-red-5"
-                @click="handleDeleteItem(index)"
-                v-if="formData.items.length > 1"
+      <Draggable v-model="formData.items" :limit="5">
+        <template #default="{ element }">
+          <div class="m-b-8px flex items-center justify-around">
+            <div class="flex flex-col items-center justify-between">
+              <UploadImg
+                v-model="element.iconUrl"
+                width="40px"
+                height="40px"
+                :show-delete="false"
+                :show-btn-text="false"
               />
+              <el-text size="small">未选中</el-text>
             </div>
-            <div class="w-full flex flex-col">
-              <div class="m-b-8px flex items-center justify-around">
-                <div class="flex flex-col items-center justify-between">
-                  <UploadImg
-                    v-model="element.iconUrl"
-                    width="40px"
-                    height="40px"
-                    :show-delete="false"
-                    :show-btn-text="false"
-                  />
-                  <el-text size="small">默认图片</el-text>
-                </div>
-                <div>
-                  <UploadImg
-                    v-model="element.activeIconUrl"
-                    width="40px"
-                    height="40px"
-                    :show-delete="false"
-                    :show-btn-text="false"
-                  />
-                  <el-text>选中图片</el-text>
-                </div>
-              </div>
-              <el-form-item prop="text" label-width="0" class="m-b-8px!">
-                <el-input v-model="element.text" placeholder="请输入文字" />
-              </el-form-item>
-              <el-form-item prop="url" label-width="0" class="m-b-0!">
-                <AppLinkInput v-model="element.url" />
-              </el-form-item>
+            <div>
+              <UploadImg
+                v-model="element.activeIconUrl"
+                width="40px"
+                height="40px"
+                :show-delete="false"
+                :show-btn-text="false"
+              />
+              <el-text>已选中</el-text>
             </div>
           </div>
+          <el-form-item prop="text" label="文字" label-width="48px" class="m-b-8px!">
+            <el-input v-model="element.text" placeholder="请输入文字" />
+          </el-form-item>
+          <el-form-item prop="url" label="链接" label-width="48px" class="m-b-0!">
+            <AppLinkInput v-model="element.url" />
+          </el-form-item>
         </template>
-      </draggable>
-
-      <el-form-item label-width="0">
-        <!-- 添加导航按钮 -->
-        <el-tooltip content="最多添加5个">
-          <el-button
-            @click="handleAddItem"
-            class="m-b-16px w-full"
-            type="primary"
-            plain
-            :disabled="formData!.items.length >= 5"
-          >
-            添加导航
-          </el-button>
-        </el-tooltip>
-      </el-form-item>
+      </Draggable>
     </el-form>
   </div>
 </template>
 
 <script setup lang="ts">
-import draggable from 'vuedraggable' //拖拽组件
-import { TabBarItemProperty, TabBarProperty, THEME_LIST } from './config'
+import { TabBarProperty, THEME_LIST } from './config'
 import { usePropertyForm } from '@/components/DiyEditor/util'
 // 底部导航栏
 defineOptions({ name: 'TabBarProperty' })
@@ -124,15 +88,6 @@ const props = defineProps<{ modelValue: TabBarProperty }>()
 const emit = defineEmits(['update:modelValue'])
 const { formData } = usePropertyForm(props.modelValue, emit)
 
-/** 添加导航项 */
-const handleAddItem = () => {
-  formData?.value?.items?.push({} as TabBarItemProperty)
-}
-/** 删除导航项 */
-const handleDeleteItem = (index: number) => {
-  formData?.value?.items?.splice(index, 1)
-}
-
 // 要的主题
 const handleThemeChange = () => {
   const theme = THEME_LIST.find((theme) => theme.id === formData.value.theme)

+ 77 - 0
src/components/Draggable/index.vue

@@ -0,0 +1,77 @@
+<template>
+  <el-text type="info" size="small"> 拖动左上角的小圆点可对其排序 </el-text>
+  <VueDraggable
+    :list="formData"
+    :force-fallback="true"
+    :animation="200"
+    handle=".drag-icon"
+    class="m-t-8px"
+    item-key="index"
+  >
+    <template #item="{ element, index }">
+      <div
+        class="mb-4px flex flex-col gap-4px border border-gray-2 border-rounded rounded border-solid p-8px"
+      >
+        <!-- 操作按钮区 -->
+        <div class="m--8px m-b-4px flex flex-row items-center justify-between bg-gray-1 p-8px">
+          <el-tooltip content="拖动排序">
+            <Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
+          </el-tooltip>
+          <el-tooltip content="删除">
+            <Icon
+              icon="ep:delete"
+              class="cursor-pointer text-red-5"
+              v-if="formData.length > 1"
+              @click="handleDelete(index)"
+            />
+          </el-tooltip>
+        </div>
+        <!-- 内容区 -->
+        <slot :element="element" :index="index"></slot>
+      </div>
+    </template>
+  </VueDraggable>
+  <el-tooltip :disabled="limit < 1" :content="`最多添加${limit}个`">
+    <el-button
+      type="primary"
+      plain
+      class="m-t-4px w-full"
+      :disabled="limit > 0 && formData.length >= limit"
+      @click="handleAdd"
+    >
+      <Icon icon="ep:plus" /><span>添加</span>
+    </el-button>
+  </el-tooltip>
+</template>
+
+<script setup lang="ts">
+// 拖拽组件
+import VueDraggable from 'vuedraggable'
+import { usePropertyForm } from '@/components/DiyEditor/util'
+import { any, array } from 'vue-types'
+import { propTypes } from '@/utils/propTypes'
+import { cloneDeep } from 'lodash-es'
+
+// 拖拽组件封装
+defineOptions({ name: 'Draggable' })
+
+// 定义属性
+const props = defineProps({
+  // 绑定值
+  modelValue: array<any>().isRequired,
+  // 空的元素:点击添加按钮时,创建元素并添加到列表;默认为空对象
+  emptyItem: any<unknown>().def({}),
+  // 数量限制:默认为0,表示不限制
+  limit: propTypes.number.def(0)
+})
+// 定义事件
+const emit = defineEmits(['update:modelValue'])
+const { formData } = usePropertyForm(props.modelValue, emit)
+
+// 处理添加
+const handleAdd = () => formData.value.push(cloneDeep(props.emptyItem || {}))
+// 处理删除
+const handleDelete = (index: number) => formData.value.splice(index, 1)
+</script>
+
+<style scoped lang="scss"></style>