Skip to content

Commit ead30e8

Browse files
YunaiVgitee-org
authored andcommitted
!312 商城装修
Merge pull request !312 from 疯狂的世界/dev
2 parents 47bb88d + 4253173 commit ead30e8

File tree

23 files changed

+1111
-589
lines changed

23 files changed

+1111
-589
lines changed
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
<template>
2+
<div :class="['component', { active: active }]">
3+
<div
4+
:style="{
5+
...style
6+
}"
7+
>
8+
<component :is="component.id" :property="component.property" />
9+
</div>
10+
<div class="component-wrap">
11+
<!-- 左侧组件名 -->
12+
<div class="component-name" v-if="component.name">
13+
{{ component.name }}
14+
</div>
15+
<!-- 左侧:组件操作工具栏 -->
16+
<div class="component-toolbar" v-if="showToolbar && component.name && active">
17+
<VerticalButtonGroup type="primary">
18+
<el-tooltip content="上移" placement="right">
19+
<el-button :disabled="!canMoveUp" @click.stop="handleMoveComponent(-1)">
20+
<Icon icon="ep:arrow-up" />
21+
</el-button>
22+
</el-tooltip>
23+
<el-tooltip content="下移" placement="right">
24+
<el-button :disabled="!canMoveDown" @click.stop="handleMoveComponent(1)">
25+
<Icon icon="ep:arrow-down" />
26+
</el-button>
27+
</el-tooltip>
28+
<el-tooltip content="复制" placement="right">
29+
<el-button @click.stop="handleCopyComponent()">
30+
<Icon icon="ep:copy-document" />
31+
</el-button>
32+
</el-tooltip>
33+
<el-tooltip content="删除" placement="right">
34+
<el-button @click.stop="handleDeleteComponent()">
35+
<Icon icon="ep:delete" />
36+
</el-button>
37+
</el-tooltip>
38+
</VerticalButtonGroup>
39+
</div>
40+
</div>
41+
</div>
42+
</template>
43+
44+
<script lang="ts">
45+
// 注册所有的组件
46+
import { components } from '../components/mobile/index'
47+
export default {
48+
components: { ...components }
49+
}
50+
</script>
51+
<script setup lang="ts">
52+
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
53+
import { propTypes } from '@/utils/propTypes'
54+
import { object } from 'vue-types'
55+
56+
/**
57+
* 组件容器
58+
* 用于包裹组件,为组件提供 背景、外边距、内边距、边框等样式
59+
*/
60+
defineOptions({ name: 'ComponentContainer' })
61+
62+
type DiyComponentWithStyle = DiyComponent<any> & { property: { style?: ComponentStyle } }
63+
const props = defineProps({
64+
component: object<DiyComponentWithStyle>().isRequired,
65+
active: propTypes.bool.def(false),
66+
canMoveUp: propTypes.bool.def(false),
67+
canMoveDown: propTypes.bool.def(false),
68+
showToolbar: propTypes.bool.def(true)
69+
})
70+
71+
/**
72+
* 组件样式
73+
*/
74+
const style = computed(() => {
75+
let componentStyle = props.component.property.style
76+
if (!componentStyle) {
77+
return {}
78+
}
79+
return {
80+
marginTop: `${componentStyle.marginTop || 0}px`,
81+
marginBottom: `${componentStyle.marginBottom || 0}px`,
82+
marginLeft: `${componentStyle.marginLeft || 0}px`,
83+
marginRight: `${componentStyle.marginRight || 0}px`,
84+
paddingTop: `${componentStyle.paddingTop || 0}px`,
85+
paddingRight: `${componentStyle.paddingRight || 0}px`,
86+
paddingBottom: `${componentStyle.paddingBottom || 0}px`,
87+
paddingLeft: `${componentStyle.paddingLeft || 0}px`,
88+
borderTopLeftRadius: `${componentStyle.borderTopLeftRadius || 0}px`,
89+
borderTopRightRadius: `${componentStyle.borderTopRightRadius || 0}px`,
90+
borderBottomRightRadius: `${componentStyle.borderBottomRightRadius || 0}px`,
91+
borderBottomLeftRadius: `${componentStyle.borderBottomLeftRadius || 0}px`,
92+
overflow: 'hidden',
93+
background:
94+
componentStyle.bgType === 'color' ? componentStyle.bgColor : `url(${componentStyle.bgImg})`
95+
}
96+
})
97+
98+
const emits = defineEmits<{
99+
(e: 'move', direction: number): void
100+
(e: 'copy'): void
101+
(e: 'delete'): void
102+
}>()
103+
/**
104+
* 移动组件
105+
* @param direction 移动方向
106+
*/
107+
const handleMoveComponent = (direction: number) => {
108+
emits('move', direction)
109+
}
110+
/**
111+
* 复制组件
112+
*/
113+
const handleCopyComponent = () => {
114+
emits('copy')
115+
}
116+
/**
117+
* 删除组件
118+
*/
119+
const handleDeleteComponent = () => {
120+
emits('delete')
121+
}
122+
</script>
123+
124+
<style scoped lang="scss">
125+
$active-border-width: 2px;
126+
$hover-border-width: 1px;
127+
$name-position: -85px;
128+
$toolbar-position: -55px;
129+
/* 组件 */
130+
.component {
131+
position: relative;
132+
cursor: move;
133+
.component-wrap {
134+
display: block;
135+
position: absolute;
136+
left: -$active-border-width;
137+
top: 0;
138+
width: 100%;
139+
height: 100%;
140+
/* 鼠标放到组件上时 */
141+
&:hover {
142+
border: $hover-border-width dashed var(--el-color-primary);
143+
box-shadow: 0 0 5px 0 rgba(24, 144, 255, 0.3);
144+
.component-name {
145+
/* 防止加了边框之后,位置移动 */
146+
left: $name-position - $hover-border-width;
147+
top: $hover-border-width;
148+
}
149+
}
150+
/* 左侧:组件名称 */
151+
.component-name {
152+
display: block;
153+
position: absolute;
154+
width: 80px;
155+
text-align: center;
156+
line-height: 25px;
157+
height: 25px;
158+
background: #fff;
159+
font-size: 12px;
160+
left: $name-position;
161+
top: $active-border-width;
162+
box-shadow:
163+
0 0 4px #00000014,
164+
0 2px 6px #0000000f,
165+
0 4px 8px 2px #0000000a;
166+
/* 右侧小三角 */
167+
&:after {
168+
position: absolute;
169+
top: 7.5px;
170+
right: -10px;
171+
content: ' ';
172+
height: 0;
173+
width: 0;
174+
border: 5px solid transparent;
175+
border-left-color: #fff;
176+
}
177+
}
178+
/* 右侧:组件操作工具栏 */
179+
.component-toolbar {
180+
display: none;
181+
position: absolute;
182+
top: 0;
183+
right: $toolbar-position;
184+
/* 左侧小三角 */
185+
&:before {
186+
position: absolute;
187+
top: 10px;
188+
left: -10px;
189+
content: ' ';
190+
height: 0;
191+
width: 0;
192+
border: 5px solid transparent;
193+
border-right-color: #2d8cf0;
194+
}
195+
}
196+
}
197+
/* 组件选中时 */
198+
&.active {
199+
margin-bottom: 4px;
200+
201+
.component-wrap {
202+
border: $active-border-width solid var(--el-color-primary) !important;
203+
box-shadow: 0 0 10px 0 rgba(24, 144, 255, 0.3);
204+
margin-bottom: $active-border-width + $active-border-width;
205+
206+
.component-name {
207+
background: var(--el-color-primary);
208+
color: #fff;
209+
/* 防止加了边框之后,位置移动 */
210+
left: $name-position - $active-border-width !important;
211+
top: 0 !important;
212+
&:after {
213+
border-left-color: var(--el-color-primary);
214+
}
215+
}
216+
.component-toolbar {
217+
display: block;
218+
}
219+
}
220+
}
221+
}
222+
</style>
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<template>
2+
<el-tabs stretch>
3+
<el-tab-pane label="内容">
4+
<slot></slot>
5+
</el-tab-pane>
6+
<el-tab-pane label="样式" lazy>
7+
<el-card header="组件样式" class="property-group">
8+
<el-form :model="formData" label-width="80px">
9+
<el-form-item label="组件背景" prop="bgType">
10+
<el-radio-group v-model="formData.bgType">
11+
<el-radio label="color">纯色</el-radio>
12+
<el-radio label="img">图片</el-radio>
13+
</el-radio-group>
14+
</el-form-item>
15+
<el-form-item label="选择颜色" prop="bgColor" v-if="formData.bgType === 'color'">
16+
<ColorInput v-model="formData.bgColor" />
17+
</el-form-item>
18+
<el-form-item label="上传图片" prop="bgImg" v-else>
19+
<UploadImg v-model="formData.bgImg" :limit="1">
20+
<template #tip>建议宽度 750px</template>
21+
</UploadImg>
22+
</el-form-item>
23+
<el-tree :data="treeData" :expand-on-click-node="false">
24+
<template #default="{ node, data }">
25+
<el-form-item
26+
:label="data.label"
27+
:prop="data.prop"
28+
:label-width="node.level === 1 ? '80px' : '62px'"
29+
class="w-full m-b-0!"
30+
>
31+
<el-slider
32+
v-model="formData[data.prop]"
33+
:max="100"
34+
:min="0"
35+
show-input
36+
input-size="small"
37+
:show-input-controls="false"
38+
@input="handleSliderChange(data.prop)"
39+
/>
40+
</el-form-item>
41+
</template>
42+
</el-tree>
43+
<slot name="style" :formData="formData"></slot>
44+
</el-form>
45+
</el-card>
46+
</el-tab-pane>
47+
</el-tabs>
48+
</template>
49+
50+
<script setup lang="ts">
51+
import { ComponentStyle, usePropertyForm } from '@/components/DiyEditor/util'
52+
53+
/**
54+
* 组件容器属性
55+
* 用于包裹组件,为组件提供 背景、外边距、内边距、边框等样式
56+
*/
57+
defineOptions({ name: 'ComponentContainer' })
58+
59+
const props = defineProps<{ modelValue: ComponentStyle }>()
60+
const emit = defineEmits(['update:modelValue'])
61+
const { formData } = usePropertyForm(props.modelValue, emit)
62+
63+
const treeData = [
64+
{
65+
label: '外部边距',
66+
prop: 'margin',
67+
children: [
68+
{
69+
label: '',
70+
prop: 'marginTop'
71+
},
72+
{
73+
label: '',
74+
prop: 'marginRight'
75+
},
76+
{
77+
label: '',
78+
prop: 'marginBottom'
79+
},
80+
{
81+
label: '',
82+
prop: 'marginLeft'
83+
}
84+
]
85+
},
86+
{
87+
label: '内部边距',
88+
prop: 'padding',
89+
children: [
90+
{
91+
label: '',
92+
prop: 'paddingTop'
93+
},
94+
{
95+
label: '',
96+
prop: 'paddingRight'
97+
},
98+
{
99+
label: '',
100+
prop: 'paddingBottom'
101+
},
102+
{
103+
label: '',
104+
prop: 'paddingLeft'
105+
}
106+
]
107+
},
108+
{
109+
label: '边框圆角',
110+
prop: 'borderRadius',
111+
children: [
112+
{
113+
label: '上左',
114+
prop: 'borderTopLeftRadius'
115+
},
116+
{
117+
label: '上右',
118+
prop: 'borderTopRightRadius'
119+
},
120+
{
121+
label: '下右',
122+
prop: 'borderBottomRightRadius'
123+
},
124+
{
125+
label: '下左',
126+
prop: 'borderBottomLeftRadius'
127+
}
128+
]
129+
}
130+
]
131+
132+
const handleSliderChange = (prop: string) => {
133+
switch (prop) {
134+
case 'margin':
135+
formData.value.marginTop = formData.value.margin
136+
formData.value.marginRight = formData.value.margin
137+
formData.value.marginBottom = formData.value.margin
138+
formData.value.marginLeft = formData.value.margin
139+
break
140+
case 'padding':
141+
formData.value.paddingTop = formData.value.padding
142+
formData.value.paddingRight = formData.value.padding
143+
formData.value.paddingBottom = formData.value.padding
144+
formData.value.paddingLeft = formData.value.padding
145+
break
146+
case 'borderRadius':
147+
formData.value.borderTopLeftRadius = formData.value.borderRadius
148+
formData.value.borderTopRightRadius = formData.value.borderRadius
149+
formData.value.borderBottomRightRadius = formData.value.borderRadius
150+
formData.value.borderBottomLeftRadius = formData.value.borderRadius
151+
break
152+
}
153+
}
154+
</script>
155+
156+
<style scoped lang="scss">
157+
:deep(.el-slider__runway) {
158+
margin-right: 16px;
159+
}
160+
:deep(.el-input-number) {
161+
width: 50px;
162+
}
163+
</style>

0 commit comments

Comments
 (0)