Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
434aa864
authored
Nov 05, 2023
by
owen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
营销:优化装修编辑器
parent
0b0ba1f3
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
267 additions
and
208 deletions
+267
-208
src/components/DiyEditor/components/ComponentContainer.vue
+199
-23
src/components/DiyEditor/index.vue
+27
-184
src/components/DiyEditor/util.ts
+1
-1
src/components/VerticalButtonGroup/index.vue
+40
-0
No files found.
src/components/DiyEditor/components/ComponentContainer.vue
View file @
434aa864
<
template
>
<div
:style=
"
{
...style
}"
>
<slot></slot>
<div
:class=
"['component',
{ active: active }]">
<div
:style=
"
{
...style
}"
>
<component
:is=
"component.id"
:property=
"component.property"
/>
</div>
<div
class=
"component-wrap"
>
<!-- 左侧组件名 -->
<div
class=
"component-name"
v-if=
"component.name"
>
{{
component
.
name
}}
</div>
<!-- 左侧:组件操作工具栏 -->
<div
class=
"component-toolbar"
v-if=
"showToolbar && component.name && active"
>
<VerticalButtonGroup
type=
"primary"
>
<el-tooltip
content=
"上移"
placement=
"right"
>
<el-button
:disabled=
"!canMoveUp"
@
click
.
stop=
"handleMoveComponent(-1)"
>
<Icon
icon=
"ep:arrow-up"
/>
</el-button>
</el-tooltip>
<el-tooltip
content=
"下移"
placement=
"right"
>
<el-button
:disabled=
"!canMoveDown"
@
click
.
stop=
"handleMoveComponent(1)"
>
<Icon
icon=
"ep:arrow-down"
/>
</el-button>
</el-tooltip>
<el-tooltip
content=
"复制"
placement=
"right"
>
<el-button
@
click
.
stop=
"handleCopyComponent()"
>
<Icon
icon=
"ep:copy-document"
/>
</el-button>
</el-tooltip>
<el-tooltip
content=
"删除"
placement=
"right"
>
<el-button
@
click
.
stop=
"handleDeleteComponent()"
>
<Icon
icon=
"ep:delete"
/>
</el-button>
</el-tooltip>
</VerticalButtonGroup>
</div>
</div>
</div>
</
template
>
<
script
lang=
"ts"
>
// 注册所有的组件
import
{
components
}
from
'../components/mobile/index'
export
default
{
components
:
{
...
components
}
}
</
script
>
<
script
setup
lang=
"ts"
>
import
{
ComponentStyle
}
from
'@/components/DiyEditor/util'
import
{
ComponentStyle
,
DiyComponent
}
from
'@/components/DiyEditor/util'
import
{
propTypes
}
from
'@/utils/propTypes'
import
{
object
}
from
'vue-types'
/**
* 组件容器
...
...
@@ -17,30 +59,164 @@ import { ComponentStyle } from '@/components/DiyEditor/util'
*/
defineOptions
({
name
:
'ComponentContainer'
})
const
props
=
defineProps
<
{
property
:
ComponentStyle
|
undefined
}
>
()
type
DiyComponentWithStyle
=
DiyComponent
<
any
>
&
{
property
:
{
style
?:
ComponentStyle
}
}
const
props
=
defineProps
({
component
:
object
<
DiyComponentWithStyle
>
().
isRequired
,
active
:
propTypes
.
bool
.
def
(
false
),
canMoveUp
:
propTypes
.
bool
.
def
(
false
),
canMoveDown
:
propTypes
.
bool
.
def
(
false
),
showToolbar
:
propTypes
.
bool
.
def
(
true
)
})
/**
* 组件样式
*/
const
style
=
computed
(()
=>
{
if
(
!
props
.
property
)
{
let
componentStyle
=
props
.
component
.
property
.
style
if
(
!
componentStyle
)
{
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`
,
marginTop
:
`
${
componentStyle
.
marginTop
||
0
}
px`
,
marginBottom
:
`
${
componentStyle
.
marginBottom
||
0
}
px`
,
marginLeft
:
`
${
componentStyle
.
marginLeft
||
0
}
px`
,
marginRight
:
`
${
componentStyle
.
marginRight
||
0
}
px`
,
paddingTop
:
`
${
componentStyle
.
paddingTop
||
0
}
px`
,
paddingRight
:
`
${
componentStyle
.
paddingRight
||
0
}
px`
,
paddingBottom
:
`
${
componentStyle
.
paddingBottom
||
0
}
px`
,
paddingLeft
:
`
${
componentStyle
.
paddingLeft
||
0
}
px`
,
borderTopLeftRadius
:
`
${
componentStyle
.
borderTopLeftRadius
||
0
}
px`
,
borderTopRightRadius
:
`
${
componentStyle
.
borderTopRightRadius
||
0
}
px`
,
borderBottomRightRadius
:
`
${
componentStyle
.
borderBottomRightRadius
||
0
}
px`
,
borderBottomLeftRadius
:
`
${
componentStyle
.
borderBottomLeftRadius
||
0
}
px`
,
overflow
:
'hidden'
,
background
:
props
.
property
.
bgType
===
'color'
?
props
.
property
.
bgColor
:
`url(
${
props
.
property
.
bgImg
}
)`
componentStyle
.
bgType
===
'color'
?
componentStyle
.
bgColor
:
`url(
${
componentStyle
.
bgImg
}
)`
}
})
const
emits
=
defineEmits
<
{
(
e
:
'move'
,
direction
:
number
):
void
(
e
:
'copy'
):
void
(
e
:
'delete'
):
void
}
>
()
/**
* 移动组件
* @param direction 移动方向
*/
const
handleMoveComponent
=
(
direction
:
number
)
=>
{
emits
(
'move'
,
direction
)
}
/**
* 复制组件
*/
const
handleCopyComponent
=
()
=>
{
emits
(
'copy'
)
}
/**
* 删除组件
*/
const
handleDeleteComponent
=
()
=>
{
emits
(
'delete'
)
}
</
script
>
<
style
scoped
lang=
"scss"
></
style
>
<
style
scoped
lang=
"scss"
>
$
active-border-width
:
2px
;
$
hover-border-width
:
1px
;
$
name-position
:
-85px
;
$
toolbar-position
:
-55px
;
/* 组件 */
.component
{
position
:
relative
;
cursor
:
move
;
.component-wrap
{
display
:
block
;
position
:
absolute
;
left
:
-
$
active-border-width
;
top
:
0
;
width
:
100%
;
height
:
100%
;
/* 鼠标放到组件上时 */
&:hover
{
border
:
$
hover-border-width
dashed
var
(
--el-color-primary
);
box-shadow
:
0
0
5px
0
rgba
(
24
,
144
,
255
,
0.3
);
.component-name
{
/* 防止加了边框之后,位置移动 */
left
:
$
name-position
-
$
hover-border-width
;
top
:
$
hover-border-width
;
}
}
/* 左侧:组件名称 */
.component-name
{
display
:
block
;
position
:
absolute
;
width
:
80px
;
text-align
:
center
;
line-height
:
25px
;
height
:
25px
;
background
:
#fff
;
font-size
:
12px
;
left
:
$
name-position
;
top
:
$
active-border-width
;
box-shadow
:
0
0
4px
#00000014
,
0
2px
6px
#0000000
f
,
0
4px
8px
2px
#0000000
a
;
/* 右侧小三角 */
&:after
{
position
:
absolute
;
top
:
7.5px
;
right
:
-10px
;
content
:
' '
;
height
:
0
;
width
:
0
;
border
:
5px
solid
transparent
;
border-left-color
:
#fff
;
}
}
/* 右侧:组件操作工具栏 */
.component-toolbar
{
display
:
none
;
position
:
absolute
;
top
:
0
;
right
:
$
toolbar-position
;
/* 左侧小三角 */
&:before
{
position
:
absolute
;
top
:
10px
;
left
:
-10px
;
content
:
' '
;
height
:
0
;
width
:
0
;
border
:
5px
solid
transparent
;
border-right-color
:
#2d8cf0
;
}
}
}
/* 组件选中时 */
&
.active
{
margin-bottom
:
4px
;
.component-wrap
{
border
:
$
active-border-width
solid
var
(
--el-color-primary
)
!important
;
box-shadow
:
0
0
10px
0
rgba
(
24
,
144
,
255
,
0.3
);
margin-bottom
:
$
active-border-width
+
$
active-border-width
;
.component-name
{
background
:
var
(
--el-color-primary
);
color
:
#fff
;
/* 防止加了边框之后,位置移动 */
left
:
$
name-position
-
$
active-border-width
!important
;
top
:
0
!important
;
&:after
{
border-left-color
:
var
(
--el-color-primary
);
}
}
.component-toolbar
{
display
:
block
;
}
}
}
}
</
style
>
src/components/DiyEditor/index.vue
View file @
434aa864
...
...
@@ -38,15 +38,13 @@
<!-- 手机顶部状态栏 -->
<img
src=
"@/assets/imgs/diy/statusBar.png"
alt=
""
class=
"status-bar"
/>
<!-- 手机顶部导航栏 -->
<
NavigationBa
r
<
ComponentContaine
r
v-if=
"showNavigationBar"
:property=
"navigationBarComponent.property"
:component=
"navigationBarComponent"
:show-toolbar=
"false"
:active=
"selectedComponent?.id === navigationBarComponent.id"
@
click=
"handleNavigationBarSelected"
:class=
"[
'component',
'cursor-pointer!',
{ active: selectedComponent?.id === navigationBarComponent.id }
]"
class=
"cursor-pointer!"
/>
</div>
<!-- 手机页面编辑区域 -->
...
...
@@ -71,73 +69,27 @@
@
change=
"handleComponentChange"
>
<template
#
item=
"
{ element, index }">
<div
class=
"component"
@
click=
"handleComponentSelected(element, index)"
>
<!-- 组件内容区 -->
<ComponentContainer
:property=
"element.property.style"
>
<component
:is=
"element.id"
:property=
"element.property"
:data-type=
"element.id"
/>
</ComponentContainer>
<div
:class=
"['component-wrap',
{ active: selectedComponentIndex === index }]">
<!-- 左侧组件名 -->
<div
:class=
"['component-name',
{ active: selectedComponentIndex === index }]"
v-if="element.name"
>
{{
element
.
name
}}
</div>
<!-- 左侧:组件操作工具栏 -->
<div
class=
"component-toolbar"
v-if=
"element.name && selectedComponentIndex === index"
>
<el-button-group
type=
"primary"
>
<el-tooltip
content=
"上移"
placement=
"right"
>
<el-button
:disabled=
"index === 0"
@
click
.
stop=
"handleMoveComponent(index, -1)"
>
<Icon
icon=
"ep:arrow-up"
/>
</el-button>
</el-tooltip>
<el-tooltip
content=
"下移"
placement=
"right"
>
<el-button
:disabled=
"index === pageComponents.length - 1"
@
click
.
stop=
"handleMoveComponent(index, 1)"
>
<Icon
icon=
"ep:arrow-down"
/>
</el-button>
</el-tooltip>
<el-tooltip
content=
"复制"
placement=
"right"
>
<el-button
@
click
.
stop=
"handleCopyComponent(index)"
>
<Icon
icon=
"ep:copy-document"
/>
</el-button>
</el-tooltip>
<el-tooltip
content=
"删除"
placement=
"right"
>
<el-button
@
click
.
stop=
"handleDeleteComponent(index)"
>
<Icon
icon=
"ep:delete"
/>
</el-button>
</el-tooltip>
</el-button-group>
</div>
</div>
</div>
<ComponentContainer
:component=
"element"
:active=
"selectedComponentIndex === index"
:can-move-up=
"index > 0"
:can-move-down=
"index
<
pageComponents
.
length
-
1
"
@
move=
"(direction) => handleMoveComponent(index, direction)"
@
copy=
"handleCopyComponent(index)"
@
delete=
"handleDeleteComponent(index)"
@
click=
"handleComponentSelected(element, index)"
/>
</
template
>
</draggable>
</el-scrollbar>
<!-- 手机底部导航 -->
<div
v-if=
"showTabBar"
:class=
"[
'editor-design-bottom',
'component',
'cursor-pointer!',
{ active: selectedComponent?.id === tabBarComponent.id }
]"
>
<TabBar
:property=
"tabBarComponent.property"
@
click=
"handleTabBarSelected"
/>
<div
v-if=
"showTabBar"
:class=
"['editor-design-bottom', 'component', 'cursor-pointer!']"
>
<ComponentContainer
:component=
"tabBarComponent"
:show-toolbar=
"false"
:active=
"selectedComponent?.id === tabBarComponent.id"
@
click=
"handleTabBarSelected"
/>
</div>
</div>
<!-- 右侧属性面板 -->
...
...
@@ -178,8 +130,6 @@ export default {
<
script
lang=
"ts"
setup
>
import
draggable
from
'vuedraggable'
import
ComponentLibrary
from
'./components/ComponentLibrary.vue'
import
NavigationBar
from
'./components/mobile/NavigationBar/index.vue'
import
TabBar
from
'./components/mobile/TabBar/index.vue'
import
{
cloneDeep
,
includes
}
from
'lodash-es'
import
{
component
as
PAGE_CONFIG_COMPONENT
}
from
'@/components/DiyEditor/components/mobile/PageConfig/config'
import
{
component
as
NAVIGATION_BAR_COMPONENT
}
from
'./components/mobile/NavigationBar/config'
...
...
@@ -256,6 +206,9 @@ const handleSave = () => {
return
{
id
:
component
.
id
,
property
:
component
.
property
}
})
}
as
PageConfig
if
(
!
props
.
showTabBar
)
{
delete
pageConfig
.
tabBar
}
// 发送数据更新通知
const
modelValue
=
isString
(
props
.
modelValue
)
?
JSON
.
stringify
(
pageConfig
)
:
pageConfig
emits
(
'update:modelValue'
,
modelValue
)
...
...
@@ -453,24 +406,12 @@ $toolbar-height: 42px;
overflow
:
hidden
;
width
:
100%
;
/* 组件 */
.component
{
width
:
$
phone-width
;
cursor
:
move
;
/* 鼠标放到组件上时 */
&:hover
{
border
:
1px
dashed
var
(
--el-color-primary
);
box-shadow
:
0
0
5px
0
rgba
(
24
,
144
,
255
,
0.3
);
}
}
/* 组件选中 */
.component.active
{
border
:
2px
solid
var
(
--el-color-primary
);
}
/* 手机顶部 */
.editor-design-top
{
width
:
$
phone-width
;
margin
:
0
auto
;
display
:
flex
;
flex-direction
:
column
;
/* 手机顶部状态栏 */
.status-bar
{
height
:
20px
;
...
...
@@ -499,104 +440,6 @@ $toolbar-height: 42px;
height
:
100%
;
width
:
100%
;
}
.component
{
position
:
relative
;
cursor
:
move
;
.component-wrap
{
display
:
none
;
position
:
absolute
;
left
:
-2px
;
top
:
0
;
width
:
100%
;
height
:
100%
;
&.active
{
display
:
block
;
border
:
2px
solid
var
(
--el-color-primary
);
box-shadow
:
0
0
10px
0
rgba
(
24
,
144
,
255
,
0.3
);
}
/* 左侧:组件名称 */
.component-name
{
position
:
absolute
;
width
:
80px
;
text-align
:
center
;
line-height
:
25px
;
height
:
25px
;
background
:
#fff
;
font-size
:
12px
;
left
:
-88px
;
top
:
0
;
box-shadow
:
0
0
4px
#00000014
,
0
2px
6px
#0000000
f
,
0
4px
8px
2px
#0000000
a
;
/* 右侧小三角 */
&:after
{
position
:
absolute
;
top
:
7.5px
;
right
:
-10px
;
content
:
' '
;
height
:
0
;
width
:
0
;
border
:
5px
solid
transparent
;
border-left-color
:
#fff
;
}
}
/* 组件选中按钮 */
.component-name.active
{
background
:
var
(
--el-color-primary
);
color
:
#fff
;
&:after
{
border-left-color
:
var
(
--el-color-primary
);
}
}
/* 右侧:组件操作工具栏 */
.component-toolbar
{
position
:
absolute
;
top
:
0
;
right
:
-55px
;
/* 左侧小三角 */
&:before
{
position
:
absolute
;
top
:
10px
;
left
:
-10px
;
content
:
' '
;
height
:
0
;
width
:
0
;
border
:
5px
solid
transparent
;
border-right-color
:
#2d8cf0
;
}
/* 重写 Element 按钮组的样式(官方只支持水平显示,增加垂直显示的样式) */
.el-button-group
{
display
:
inline-flex
;
flex-direction
:
column
;
}
.el-button-group
>
.el-button
:first-child
{
border-bottom-left-radius
:
0
;
border-bottom-right-radius
:
0
;
border-top-right-radius
:
var
(
--el-border-radius-base
);
border-bottom-color
:
var
(
--el-button-divide-border-color
);
}
.el-button-group
>
.el-button
:last-child
{
border-top-left-radius
:
0
;
border-top-right-radius
:
0
;
border-bottom-left-radius
:
var
(
--el-border-radius-base
);
border-top-color
:
var
(
--el-button-divide-border-color
);
}
.el-button-group
.el-button--primary
:not
(
:first-child
)
:not
(
:last-child
)
{
border-top-color
:
var
(
--el-button-divide-border-color
);
border-bottom-color
:
var
(
--el-button-divide-border-color
);
}
.el-button-group
>
.el-button
:not
(
:last-child
)
{
margin-bottom
:
-1px
;
margin-right
:
0
;
}
}
}
}
}
}
}
...
...
src/components/DiyEditor/util.ts
View file @
434aa864
...
...
@@ -51,7 +51,7 @@ export interface PageConfig {
// 顶部导航栏属性
navigationBar
:
NavigationBarProperty
// 底部导航菜单属性
tabBar
:
TabBarProperty
tabBar
?
:
TabBarProperty
// 页面组件列表
components
:
PageComponent
[]
}
...
...
src/components/VerticalButtonGroup/index.vue
0 → 100644
View file @
434aa864
<
template
>
<el-button-group
v-bind=
"$attrs"
>
<slot></slot>
</el-button-group>
</
template
>
<
script
setup
lang=
"ts"
>
/**
* 垂直按钮组
* Element官方的按钮组只支持水平显示,通过重写样式实现垂直布局
*/
defineOptions
({
name
:
'VerticalButtonGroup'
})
</
script
>
<
style
scoped
lang=
"scss"
>
.el-button-group
{
display
:
inline-flex
;
flex-direction
:
column
;
}
.el-button-group
>
:deep
(
.el-button
:first-child
)
{
border-bottom-left-radius
:
0
;
border-bottom-right-radius
:
0
;
border-top-right-radius
:
var
(
--el-border-radius-base
);
border-bottom-color
:
var
(
--el-button-divide-border-color
);
}
.el-button-group
>
:deep
(
.el-button
:last-child
)
{
border-top-left-radius
:
0
;
border-top-right-radius
:
0
;
border-bottom-left-radius
:
var
(
--el-border-radius-base
);
border-top-color
:
var
(
--el-button-divide-border-color
);
}
.el-button-group
:deep
(
.el-button--primary
:not
(
:first-child
)
:not
(
:last-child
))
{
border-top-color
:
var
(
--el-button-divide-border-color
);
border-bottom-color
:
var
(
--el-button-divide-border-color
);
}
.el-button-group
>
:deep
(
.el-button
:not
(
:last-child
))
{
margin-bottom
:
-1px
;
margin-right
:
0
;
}
</
style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment