diff --git a/src/components/DiyEditor/components/ComponentContainer.vue b/src/components/DiyEditor/components/ComponentContainer.vue new file mode 100644 index 00000000..95eb647a --- /dev/null +++ b/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> diff --git a/src/components/DiyEditor/components/ComponentContainerProperty.vue b/src/components/DiyEditor/components/ComponentContainerProperty.vue new file mode 100644 index 00000000..0cfbbe1e --- /dev/null +++ b/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> diff --git a/src/components/DiyEditor/components/ComponentLibrary.vue b/src/components/DiyEditor/components/ComponentLibrary.vue index 8e918fa9..abeb40b5 100644 --- a/src/components/DiyEditor/components/ComponentLibrary.vue +++ b/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 diff --git a/src/components/DiyEditor/components/mobile/NavigationBar/config.ts b/src/components/DiyEditor/components/mobile/NavigationBar/config.ts index b250f5f1..f722d525 100644 --- a/src/components/DiyEditor/components/mobile/NavigationBar/config.ts +++ b/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, diff --git a/src/components/DiyEditor/components/mobile/SearchBar/config.ts b/src/components/DiyEditor/components/mobile/SearchBar/config.ts index 1241748d..ef47b27c 100644 --- a/src/components/DiyEditor/components/mobile/SearchBar/config.ts +++ b/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> diff --git a/src/components/DiyEditor/components/mobile/SearchBar/index.vue b/src/components/DiyEditor/components/mobile/SearchBar/index.vue index e120405a..27db1c51 100644 --- a/src/components/DiyEditor/components/mobile/SearchBar/index.vue +++ b/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; diff --git a/src/components/DiyEditor/components/mobile/SearchBar/property.vue b/src/components/DiyEditor/components/mobile/SearchBar/property.vue index 9123ebe5..d121a1e3 100644 --- a/src/components/DiyEditor/components/mobile/SearchBar/property.vue +++ b/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"> diff --git a/src/components/DiyEditor/index.vue b/src/components/DiyEditor/index.vue index d0d49812..e45dc9d3 100644 --- a/src/components/DiyEditor/index.vue +++ b/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; } } } diff --git a/src/components/DiyEditor/util.ts b/src/components/DiyEditor/util.ts index 407efa30..b781fe3c 100644 --- a/src/components/DiyEditor/util.ts +++ b/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 { // 页面属性