Explorar o código

营销:装修页面适配搜索框

owen hai 1 ano
pai
achega
aedf014407

+ 45 - 0
src/components/DiyEditor/components/ComponentContainer.vue

@@ -0,0 +1,45 @@
+<template>
+  <div
+    :style="{
+      ...style,
+      background: property.bgType === 'color' ? property.bgColor : `url(${property.bgImg})`
+    }"
+  >
+    <slot></slot>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ComponentStyle } from '@/components/DiyEditor/util'
+
+/**
+ * 组件容器
+ * 用于包裹组件,为组件提供 背景、外边距、内边距、边框等样式
+ */
+defineOptions({ name: 'ComponentContainer' })
+
+const props = defineProps<{ property: ComponentStyle }>()
+
+const style = computed(() => {
+  if (!props.property) {
+    return {}
+  }
+  return {
+    marginTop: `${props.property.marginTop || 0}px`,
+    marginBottom: `${props.property.marginBottom || 0}px`,
+    marginLeft: `${props.property.marginLeft || 0}px`,
+    marginRight: `${props.property.marginRight || 0}px`,
+    paddingTop: `${props.property.paddingTop || 0}px`,
+    paddingRight: `${props.property.paddingRight || 0}px`,
+    paddingBottom: `${props.property.paddingBottom || 0}px`,
+    paddingLeft: `${props.property.paddingLeft || 0}px`,
+    borderTopLeftRadius: `${props.property.borderTopLeftRadius || 0}px`,
+    borderTopRightRadius: `${props.property.borderTopRightRadius || 0}px`,
+    borderBottomRightRadius: `${props.property.borderBottomRightRadius || 0}px`,
+    borderBottomLeftRadius: `${props.property.borderBottomLeftRadius || 0}px`,
+    overflow: 'hidden'
+  }
+})
+</script>
+
+<style scoped lang="scss"></style>

+ 164 - 0
src/components/DiyEditor/components/ComponentContainerProperty.vue

@@ -0,0 +1,164 @@
+<template>
+  <el-tabs stretch>
+    <el-tab-pane label="内容">
+      <slot></slot>
+    </el-tab-pane>
+    <el-tab-pane label="样式" lazy>
+      <el-card header="组件样式" class="property-group">
+        <el-form :model="formData" label-width="80px">
+          <el-form-item label="组件背景" prop="bgType">
+            <el-radio-group v-model="formData.bgType">
+              <el-radio label="color">纯色</el-radio>
+              <el-radio label="img">图片</el-radio>
+            </el-radio-group>
+          </el-form-item>
+          <el-form-item label="选择颜色" prop="bgColor" v-if="formData.bgType === 'color'">
+            <ColorInput v-model="formData.bgColor" />
+          </el-form-item>
+          <el-form-item label="上传图片" prop="bgImg" v-else>
+            <UploadImg v-model="formData.bgImg" :limit="1">
+              <template #tip>建议宽度 750px</template>
+            </UploadImg>
+          </el-form-item>
+          <el-tree :data="treeData" :expand-on-click-node="false">
+            <template #default="{ node, data }">
+              <el-form-item
+                :label="data.label"
+                :prop="data.prop"
+                :label-width="node.level === 1 ? '80px' : '62px'"
+                class="tree-form-item w-full m-b-0!"
+              >
+                <el-slider
+                  v-model="formData[data.prop]"
+                  :max="100"
+                  :min="0"
+                  show-input
+                  input-size="small"
+                  :show-input-controls="false"
+                  @input="handleSliderChange(data.prop)"
+                />
+              </el-form-item>
+            </template>
+          </el-tree>
+        </el-form>
+      </el-card>
+    </el-tab-pane>
+  </el-tabs>
+</template>
+
+<script setup lang="ts">
+import { ComponentStyle, usePropertyForm } from '@/components/DiyEditor/util'
+
+/**
+ * 组件容器属性
+ * 用于包裹组件,为组件提供 背景、外边距、内边距、边框等样式
+ */
+defineOptions({ name: 'ComponentContainer' })
+
+const props = defineProps<{ modelValue: ComponentStyle }>()
+const emit = defineEmits(['update:modelValue'])
+const { formData } = usePropertyForm(props.modelValue, emit)
+
+const treeData = [
+  {
+    label: '外部边距',
+    prop: 'margin',
+    children: [
+      {
+        label: '上',
+        prop: 'marginTop'
+      },
+      {
+        label: '右',
+        prop: 'marginRight'
+      },
+      {
+        label: '下',
+        prop: 'marginBottom'
+      },
+      {
+        label: '左',
+        prop: 'marginLeft'
+      }
+    ]
+  },
+  {
+    label: '内部边距',
+    prop: 'padding',
+    children: [
+      {
+        label: '上',
+        prop: 'paddingTop'
+      },
+      {
+        label: '右',
+        prop: 'paddingRight'
+      },
+      {
+        label: '下',
+        prop: 'paddingBottom'
+      },
+      {
+        label: '左',
+        prop: 'paddingLeft'
+      }
+    ]
+  },
+  {
+    label: '边框圆角',
+    prop: 'borderRadius',
+    children: [
+      {
+        label: '上左',
+        prop: 'borderTopLeftRadius'
+      },
+      {
+        label: '上右',
+        prop: 'borderTopRightRadius'
+      },
+      {
+        label: '下右',
+        prop: 'borderBottomRightRadius'
+      },
+      {
+        label: '下左',
+        prop: 'borderBottomLeftRadius'
+      }
+    ]
+  }
+]
+
+const handleSliderChange = (prop: string) => {
+  switch (prop) {
+    case 'margin':
+      formData.value.marginTop = formData.value.margin
+      formData.value.marginRight = formData.value.margin
+      formData.value.marginBottom = formData.value.margin
+      formData.value.marginLeft = formData.value.margin
+      break
+    case 'padding':
+      formData.value.paddingTop = formData.value.padding
+      formData.value.paddingRight = formData.value.padding
+      formData.value.paddingBottom = formData.value.padding
+      formData.value.paddingLeft = formData.value.padding
+      break
+    case 'borderRadius':
+      formData.value.borderTopLeftRadius = formData.value.borderRadius
+      formData.value.borderTopRightRadius = formData.value.borderRadius
+      formData.value.borderBottomRightRadius = formData.value.borderRadius
+      formData.value.borderBottomLeftRadius = formData.value.borderRadius
+      break
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.tree-form-item {
+  :deep(.el-slider__runway) {
+    margin-right: 16px;
+  }
+  :deep(.el-input-number) {
+    width: 50px;
+  }
+}
+</style>

+ 1 - 1
src/components/DiyEditor/components/ComponentLibrary.vue

@@ -1,5 +1,5 @@
 <template>
-  <el-aside class="editor-left" width="260px">
+  <el-aside class="editor-left" width="261px">
     <el-scrollbar>
       <el-collapse v-model="extendGroups">
         <el-collapse-item

+ 1 - 1
src/components/DiyEditor/components/mobile/NavigationBar/config.ts

@@ -29,7 +29,7 @@ export const component = {
     title: '页面标题',
     description: '',
     navBarHeight: 35,
-    backgroundColor: '#f5f5f5',
+    backgroundColor: '#fff',
     backgroundImage: '',
     styleType: 'default',
     alwaysShow: true,

+ 14 - 6
src/components/DiyEditor/components/mobile/SearchBar/config.ts

@@ -1,4 +1,4 @@
-import { DiyComponent } from '@/components/DiyEditor/util'
+import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
 
 /** 搜索框属性 */
 export interface SearchProperty {
@@ -7,10 +7,10 @@ export interface SearchProperty {
   borderRadius: number // 框体样式
   placeholder: string // 占位文字
   placeholderPosition: PlaceholderPosition // 占位文字位置
-  backgroundColor: string // 背景颜色
-  borderColor: string // 框体颜色
+  backgroundColor: string // 框体颜色
   textColor: string // 字体颜色
   hotKeywords: string[] // 热词
+  style: ComponentStyle
 }
 
 // 文字位置
@@ -27,9 +27,17 @@ export const component = {
     borderRadius: 0,
     placeholder: '搜索商品',
     placeholderPosition: 'left',
-    backgroundColor: 'rgb(249, 249, 249)',
-    borderColor: 'rgb(255, 255, 255)',
+    backgroundColor: 'rgb(238, 238, 238)',
     textColor: 'rgb(150, 151, 153)',
-    hotKeywords: []
+    hotKeywords: [],
+    style: {
+      bgType: 'color',
+      bgColor: '#fff',
+      marginBottom: 8,
+      paddingTop: 8,
+      paddingRight: 8,
+      paddingBottom: 8,
+      paddingLeft: 8
+    } as ComponentStyle
   }
 } as DiyComponent<SearchProperty>

+ 2 - 6
src/components/DiyEditor/components/mobile/SearchBar/index.vue

@@ -2,8 +2,6 @@
   <div
     class="search-bar"
     :style="{
-      background: property.backgroundColor,
-      border: `1px solid ${property.backgroundColor}`,
       color: property.textColor
     }"
   >
@@ -12,7 +10,7 @@
       class="inner"
       :style="{
         height: `${property.height}px`,
-        background: property.borderColor,
+        background: property.backgroundColor,
         borderRadius: `${property.borderRadius}px`
       }"
     >
@@ -44,13 +42,11 @@ defineProps<{ property: SearchProperty }>()
 
 <style scoped lang="scss">
 .search-bar {
-  position: relative;
+  width: 375px;
   /* 搜索框 */
   .inner {
     position: relative;
-    width: calc(100% - 16px);
     min-height: 28px;
-    margin: 5px auto;
     display: flex;
     align-items: center;
     font-size: 14px;

+ 72 - 73
src/components/DiyEditor/components/mobile/SearchBar/property.vue

@@ -1,78 +1,77 @@
 <template>
-  <el-text tag="p"> 搜索热词 </el-text>
-  <el-text type="info" size="small"> 拖动左侧的小圆点可以调整热词顺序 </el-text>
+  <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>
-        </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 label="框体颜色" prop="borderColor">
-      <ColorInput v-model="formData.borderColor" />
-    </el-form-item>
-    <el-form-item class="lef" label="文本颜色" prop="textColor">
-      <ColorInput v-model="formData.textColor" />
-    </el-form-item>
-  </el-form>
+    <!-- 表单 -->
+    <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>
+          </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>
+    </el-form>
+  </ComponentContainerProperty>
 </template>
 
 <script setup lang="ts">

+ 5 - 3
src/components/DiyEditor/index.vue

@@ -427,11 +427,13 @@ $phone-width: 375px;
         padding: 8px 16px;
       }
       /* 属性面板分组 */
-      .property-group {
-        /* 属性分组 */
-        :deep(.el-card__header) {
+      :deep(.property-group) {
+        margin: 0 -20px;
+        /* 属性分组名称 */
+        .el-card__header {
           border: none;
           background: var(--el-bg-color-page);
+          padding: 8px 32px;
         }
       }
     }

+ 28 - 0
src/components/DiyEditor/util.ts

@@ -16,6 +16,34 @@ export interface DiyComponentLibrary {
   components: string[]
 }
 
+// 组件样式
+export interface ComponentStyle {
+  // 背景类型
+  bgType: 'color' | 'img'
+  // 背景颜色
+  bgColor: string
+  // 背景图片
+  bgImg: string
+  // 外边距
+  margin: number
+  marginTop: number
+  marginRight: number
+  marginBottom: number
+  marginLeft: number
+  // 内边距
+  padding: number
+  paddingTop: number
+  paddingRight: number
+  paddingBottom: number
+  paddingLeft: number
+  // 边框圆角
+  borderRadius: number
+  borderTopLeftRadius: number
+  borderTopRightRadius: number
+  borderBottomRightRadius: number
+  borderBottomLeftRadius: number
+}
+
 // 页面配置
 export interface PageConfig {
   // 页面属性