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
Unverified
Commit
ead30e8c
authored
Nov 06, 2023
by
芋道源码
Committed by
Gitee
Nov 06, 2023
Browse files
Options
Browse Files
Download
Plain Diff
!312 商城装修
Merge pull request !312 from 疯狂的世界/dev
parents
47bb88d8
4253173a
Show whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
949 additions
and
427 deletions
+949
-427
src/components/DiyEditor/components/ComponentContainer.vue
+222
-0
src/components/DiyEditor/components/ComponentContainerProperty.vue
+163
-0
src/components/DiyEditor/components/ComponentLibrary.vue
+2
-1
src/components/DiyEditor/components/mobile/Carousel/config.ts
+32
-26
src/components/DiyEditor/components/mobile/Carousel/index.vue
+24
-56
src/components/DiyEditor/components/mobile/Carousel/property.vue
+79
-62
src/components/DiyEditor/components/mobile/ImageBar/config.ts
+27
-0
src/components/DiyEditor/components/mobile/ImageBar/index.vue
+24
-0
src/components/DiyEditor/components/mobile/ImageBar/property.vue
+34
-0
src/components/DiyEditor/components/mobile/NavigationBar/config.ts
+1
-1
src/components/DiyEditor/components/mobile/SearchBar/config.ts
+14
-6
src/components/DiyEditor/components/mobile/SearchBar/index.vue
+1
-6
src/components/DiyEditor/components/mobile/SearchBar/property.vue
+3
-4
src/components/DiyEditor/components/mobile/VideoPlayer/config.ts
+37
-0
src/components/DiyEditor/components/mobile/VideoPlayer/index.vue
+30
-0
src/components/DiyEditor/components/mobile/VideoPlayer/property.vue
+55
-0
src/components/DiyEditor/index.vue
+55
-196
src/components/DiyEditor/util.ts
+62
-1
src/components/UploadFile/src/UploadFile.vue
+39
-16
src/components/VerticalButtonGroup/index.vue
+40
-0
src/layout/components/Footer/src/Footer.vue
+1
-1
src/views/mall/promotion/diy/page/decorate.vue
+2
-26
src/views/mall/promotion/diy/template/decorate.vue
+2
-25
No files found.
src/components/DiyEditor/components/ComponentContainer.vue
0 → 100644
View file @
ead30e8c
<
template
>
<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
,
DiyComponent
}
from
'@/components/DiyEditor/util'
import
{
propTypes
}
from
'@/utils/propTypes'
import
{
object
}
from
'vue-types'
/**
* 组件容器
* 用于包裹组件,为组件提供 背景、外边距、内边距、边框等样式
*/
defineOptions
({
name
:
'ComponentContainer'
})
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
(()
=>
{
let
componentStyle
=
props
.
component
.
property
.
style
if
(
!
componentStyle
)
{
return
{}
}
return
{
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
:
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"
>
$
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/components/ComponentContainerProperty.vue
0 → 100644
View file @
ead30e8c
<
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=
"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>
<slot
name=
"style"
:formData=
"formData"
></slot>
</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"
>
:deep
(
.el-slider__runway
)
{
margin-right
:
16px
;
}
:deep
(
.el-input-number
)
{
width
:
50px
;
}
</
style
>
src/components/DiyEditor/components/ComponentLibrary.vue
View file @
ead30e8c
<
template
>
<
template
>
<el-aside
class=
"editor-left"
width=
"26
0
px"
>
<el-aside
class=
"editor-left"
width=
"26
1
px"
>
<el-scrollbar>
<el-scrollbar>
<el-collapse
v-model=
"extendGroups"
>
<el-collapse
v-model=
"extendGroups"
>
<el-collapse-item
<el-collapse-item
...
@@ -11,6 +11,7 @@
...
@@ -11,6 +11,7 @@
<draggable
<draggable
class=
"component-container"
class=
"component-container"
ghost-class=
"draggable-ghost"
ghost-class=
"draggable-ghost"
item-key=
"index"
:list=
"group.components"
:list=
"group.components"
:sort=
"false"
:sort=
"false"
:group=
"
{ name: 'component', pull: 'clone', put: false }"
:group=
"
{ name: 'component', pull: 'clone', put: false }"
...
...
src/components/DiyEditor/components/mobile/Carousel/config.ts
View file @
ead30e8c
import
{
DiyComponent
}
from
'@/components/DiyEditor/util'
import
{
ComponentStyle
,
DiyComponent
}
from
'@/components/DiyEditor/util'
/** 轮播图属性 */
/** 轮播图属性 */
export
interface
CarouselProperty
{
export
interface
CarouselProperty
{
// 选择模板
// 类型:默认 | 卡片
swiperType
:
number
type
:
'default'
|
'card'
// 图片圆角
// 指示器样式:点 | 数字
borderRadius
:
number
indicator
:
'dot'
|
'number'
// 页面边距
// 是否自动播放
pageMargin
:
number
autoplay
:
boolean
// 图片边距
// 播放间隔
imageMargin
:
number
interval
:
number
// 分页类型
// 轮播内容
pagingType
:
'bullets'
|
'fraction'
|
'progressbar'
// 一行个数
rowIndividual
:
number
// 添加图片
items
:
CarouselItemProperty
[]
items
:
CarouselItemProperty
[]
// 组件样式
style
:
ComponentStyle
}
}
// 轮播内容属性
export
interface
CarouselItemProperty
{
export
interface
CarouselItemProperty
{
title
:
string
// 类型:图片 | 视频
type
:
'img'
|
'video'
// 图片链接
imgUrl
:
string
imgUrl
:
string
link
:
string
// 视频链接
videoUrl
:
string
// 跳转链接
url
:
string
}
}
// 定义组件
// 定义组件
...
@@ -30,15 +33,18 @@ export const component = {
...
@@ -30,15 +33,18 @@ export const component = {
name
:
'轮播图'
,
name
:
'轮播图'
,
icon
:
'system-uicons:carousel'
,
icon
:
'system-uicons:carousel'
,
property
:
{
property
:
{
swiperType
:
0
,
// 选择模板
type
:
'default'
,
borderRadius
:
0
,
// 图片圆角
indicator
:
'dot'
,
pageMargin
:
0
,
// 页面边距
autoplay
:
false
,
imageMargin
:
0
,
// 图片边距
interval
:
3
,
pagingType
:
'bullets'
,
// 分页类型
rowIndividual
:
2
,
// 一行个数
items
:
[
items
:
[
{
imgUrl
:
'https://static.iocoder.cn/mall/banner-01.jpg'
},
{
type
:
'img'
,
imgUrl
:
'https://static.iocoder.cn/mall/banner-01.jpg'
,
videoUrl
:
''
},
{
imgUrl
:
'https://static.iocoder.cn/mall/banner-02.jpg'
}
{
type
:
'img'
,
imgUrl
:
'https://static.iocoder.cn/mall/banner-02.jpg'
,
videoUrl
:
''
}
]
as
CarouselItemProperty
[]
]
as
CarouselItemProperty
[],
style
:
{
bgType
:
'color'
,
bgColor
:
'#fff'
,
marginBottom
:
8
}
as
ComponentStyle
}
}
}
as
DiyComponent
<
CarouselProperty
>
}
as
DiyComponent
<
CarouselProperty
>
src/components/DiyEditor/components/mobile/Carousel/index.vue
View file @
ead30e8c
...
@@ -6,70 +6,38 @@
...
@@ -6,70 +6,38 @@
>
>
<Icon
icon=
"tdesign:image"
class=
"text-gray-8 text-120px!"
/>
<Icon
icon=
"tdesign:image"
class=
"text-gray-8 text-120px!"
/>
</div>
</div>
<!-- 一行一个 -->
<div
v-else
class=
"relative"
>
<div
<el-carousel
v-if=
"property.swiperType === 0"
height=
"174px"
class=
"flex flex-col"
:type=
"property.type === 'card' ? 'card' : ''"
:style=
"
{
:autoplay=
"property.autoplay"
paddingLeft: property.pageMargin + 'px',
:interval=
"property.interval * 1000"
paddingRight: property.pageMargin + 'px'
:indicator-position=
"property.indicator === 'number' ? 'none' : undefined"
}"
@
change=
"handleIndexChange"
>
<div
v-for=
"(item, index) in property.items"
:key=
"index"
>
<div
class=
"img-item"
:style=
"
{
marginBottom: property.imageMargin + 'px',
borderRadius: property.borderRadius + 'px'
}"
>
>
<img
alt=
""
:src=
"item.imgUrl"
/>
<div
v-if=
"item.title"
class=
"title"
>
{{
item
.
title
}}
</div>
</div>
</div>
</div>
<el-carousel
height=
"174px"
v-else
:type=
"property.swiperType === 3 ? 'card' : ''"
>
<el-carousel-item
v-for=
"(item, index) in property.items"
:key=
"index"
>
<el-carousel-item
v-for=
"(item, index) in property.items"
:key=
"index"
>
<div
class=
"img-item"
:style=
"
{ borderRadius: property.borderRadius + 'px' }">
<el-image
class=
"h-full w-full"
:src=
"item.imgUrl"
/>
<img
alt=
""
:src=
"item.imgUrl"
/>
<div
v-if=
"item.title"
class=
"title"
>
{{
item
.
title
}}
</div>
</div>
</el-carousel-item>
</el-carousel-item>
</el-carousel>
</el-carousel>
<div
v-if=
"property.indicator === 'number'"
class=
"absolute p-y-2px bottom-10px right-10px rounded-xl bg-black p-x-8px text-10px text-white opacity-40"
>
{{
currentIndex
}}
/
{{
property
.
items
.
length
}}
</div
>
</div>
</
template
>
</
template
>
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
CarouselProperty
}
from
'./config'
import
{
CarouselProperty
}
from
'./config'
/**
页面顶部导航栏
*/
/**
轮播图
*/
defineOptions
({
name
:
'
NavigationBar
'
})
defineOptions
({
name
:
'
Carousel
'
})
const
props
=
defineProps
<
{
property
:
CarouselProperty
}
>
()
defineProps
<
{
property
:
CarouselProperty
}
>
()
</
script
>
<
style
scoped
lang=
"scss"
>
const
currentIndex
=
ref
(
0
)
.img-item
{
const
handleIndexChange
=
(
index
:
number
)
=>
{
width
:
100%
;
currentIndex
.
value
=
index
+
1
position
:
relative
;
overflow
:
hidden
;
&:last-child
{
margin
:
0
!important
;
}
/* 图片 */
img
{
width
:
100%
;
height
:
100%
;
display
:
block
;
}
.title
{
height
:
36px
;
width
:
100%
;
background-color
:
rgba
(
51
,
51
,
51
,
0.8
);
text-align
:
center
;
line-height
:
36px
;
color
:
#fff
;
position
:
absolute
;
bottom
:
0
;
left
:
0
;
}
}
}
</
style
>
</
script
>
<
style
scoped
lang=
"scss"
></
style
>
src/components/DiyEditor/components/mobile/Carousel/property.vue
View file @
ead30e8c
<
template
>
<
template
>
<ComponentContainerProperty
v-model=
"formData.style"
>
<el-form
label-width=
"80px"
:model=
"formData"
>
<el-form
label-width=
"80px"
:model=
"formData"
>
<el-form-item
label=
"选择模板"
prop=
"swiperType"
>
<el-card
header=
"样式设置"
class=
"property-group"
shadow=
"never"
>
<el-radio-group
v-model=
"formData.swiperType"
>
<el-form-item
label=
"样式"
prop=
"type"
>
<el-tooltip
class=
"item"
content=
"一行一个"
placement=
"bottom"
>
<el-radio-group
v-model=
"formData.type"
>
<el-radio-button
:label=
"0"
>
<el-tooltip
class=
"item"
content=
"默认"
placement=
"bottom"
>
<Icon
icon=
"icon-park-twotone:multi-picture-carousel"
/>
<el-radio-button
label=
"default"
>
</el-radio-button>
</el-tooltip>
<el-tooltip
class=
"item"
content=
"轮播海报"
placement=
"bottom"
>
<el-radio-button
:label=
"1"
>
<Icon
icon=
"system-uicons:carousel"
/>
<Icon
icon=
"system-uicons:carousel"
/>
</el-radio-button>
</el-radio-button>
</el-tooltip>
</el-tooltip>
<el-tooltip
class=
"item"
content=
"多图单行"
placement=
"bottom"
>
<el-tooltip
class=
"item"
content=
"卡片"
placement=
"bottom"
>
<el-radio-button
:label=
"2"
>
<el-radio-button
label=
"card"
>
<Icon
icon=
"icon-park-twotone:carousel"
/>
</el-radio-button>
</el-tooltip>
<el-tooltip
class=
"item"
content=
"立体轮播"
placement=
"bottom"
>
<el-radio-button
:label=
"3"
>
<Icon
icon=
"ic:round-view-carousel"
/>
<Icon
icon=
"ic:round-view-carousel"
/>
</el-radio-button>
</el-radio-button>
</el-tooltip>
</el-tooltip>
</el-radio-group>
</el-radio-group>
</el-form-item>
</el-form-item>
<el-form-item
label=
"指示器"
prop=
"indicator"
>
<el-text
tag=
"p"
>
添加图片
</el-text>
<el-radio-group
v-model=
"formData.indicator"
>
<el-radio
label=
"dot"
>
小圆点
</el-radio>
<el-radio
label=
"number"
>
数字
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label=
"是否轮播"
prop=
"autoplay"
>
<el-switch
v-model=
"formData.autoplay"
/>
</el-form-item>
<el-form-item
label=
"播放间隔"
prop=
"interval"
v-if=
"formData.autoplay"
>
<el-slider
v-model=
"formData.interval"
:max=
"10"
:min=
"0.5"
:step=
"0.5"
show-input
input-size=
"small"
:show-input-controls=
"false"
/>
<el-text
type=
"info"
>
单位:秒
</el-text>
</el-form-item>
</el-card>
<el-card
header=
"内容设置"
class=
"property-group"
shadow=
"never"
>
<el-text
type=
"info"
size=
"small"
>
拖动左上角的小圆点可对其排序
</el-text>
<el-text
type=
"info"
size=
"small"
>
拖动左上角的小圆点可对其排序
</el-text>
<template
v-if=
"formData.items[0]"
>
<!-- 图片广告 -->
<div
v-if=
"formData.items[0]"
>
<draggable
<draggable
:list=
"formData.items"
:list=
"formData.items"
:force-fallback=
"true"
:force-fallback=
"true"
:animation=
"200"
:animation=
"200"
handle=
".drag-icon"
handle=
".drag-icon"
class=
"m-t-8px"
class=
"m-t-8px"
item-key=
"index"
>
>
<template
#
item=
"
{ element, index }">
<template
#
item=
"
{ element, index }">
<div
class=
"mb-4px flex flex-row gap-4px rounded bg-gray-100 p-8px"
>
<div
class=
"content mb-4px flex flex-col gap-4px rounded bg-gray-50 p-8px"
>
<div
class=
"flex flex-col items-start justify-between"
>
<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=
"ic:round-drag-indicator"
class=
"drag-icon cursor-move"
/>
<Icon
<Icon
icon=
"ep:delete"
icon=
"ep:delete"
...
@@ -48,7 +62,18 @@
...
@@ -48,7 +62,18 @@
v-if=
"formData.items.length > 1"
v-if=
"formData.items.length > 1"
/>
/>
</div>
</div>
<div
class=
"flex flex-1 flex-col items-center justify-between gap-8px"
>
<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
<UploadImg
v-model=
"element.imgUrl"
v-model=
"element.imgUrl"
draggable=
"false"
draggable=
"false"
...
@@ -56,48 +81,40 @@
...
@@ -56,48 +81,40 @@
width=
"100%"
width=
"100%"
class=
"min-w-80px"
class=
"min-w-80px"
/>
/>
<!-- 标题 -->
<el-input
v-model=
"element.title"
placeholder=
"标题,选填"
/>
<!-- 输入链接 -->
<el-input
placeholder=
"链接,选填"
v-model=
"element.link"
/>
</div>
</div>
</
template
>
</draggable>
</div>
<el-button
@
click=
"handleAddImage"
type=
"primary"
plain
class=
"w-full"
>
添加图片
</el-button>
<el-form-item
label=
"一行个数"
prop=
"rowIndividual"
v-show=
"formData.swiperType === 2"
>
<!-- 单选框 -->
<el-radio-group
v-model=
"formData.rowIndividual"
>
<el-radio
:label=
"2"
>
2个
</el-radio>
<el-radio
:label=
"3"
>
3个
</el-radio>
<el-radio
:label=
"4"
>
4个
</el-radio>
<el-radio
:label=
"5"
>
5个
</el-radio>
<el-radio
:label=
"6"
>
6个
</el-radio>
</el-radio-group>
</el-form-item>
</el-form-item>
<el-form-item
label=
"分页类型"
prop=
"pagingType"
>
<template
v-else
>
<el-radio-group
v-model=
"formData.pagingType"
>
<el-form-item
label=
"封面"
class=
"m-b-8px!"
label-width=
"50px"
>
<el-radio
:label=
"0"
>
不显示
</el-radio>
<UploadImg
<el-radio
label=
"bullets"
>
样式一
</el-radio>
v-model=
"element.imgUrl"
<el-radio
label=
"fraction"
>
样式二
</el-radio>
draggable=
"false"
<el-radio
label=
"progressbar"
>
样式三
</el-radio>
height=
"80px"
</el-radio-group>
width=
"100%"
</el-form-item>
class=
"min-w-80px"
<el-form-item
label=
"图片圆角"
prop=
"borderRadius"
>
/>
<el-slider
v-model=
"formData.borderRadius"
:max=
"30"
/>
</el-form-item>
</el-form-item>
<el-form-item
label=
"页面边距"
prop=
"pageMargin"
v-show=
"formData.swiperType === 0"
>
<el-form-item
label=
"视频"
class=
"m-b-8px!"
label-width=
"50px"
>
<el-slider
v-model=
"formData.pageMargin"
:max=
"20"
/>
<UploadFile
v-model=
"element.videoUrl"
:file-type=
"['mp4']"
:limit=
"1"
:file-size=
"100"
class=
"min-w-80px"
/>
</el-form-item>
</el-form-item>
<el-form-item
</
template
>
label=
"图片边距"
<el-form-item
label=
"链接"
class=
"m-b-8px!"
label-width=
"50px"
>
prop=
"imageMargin"
<el-input
placeholder=
"链接"
v-model=
"element.url"
/>
v-show=
"formData.swiperType === 0 || formData.swiperType === 2"
>
<el-slider
v-model=
"formData.imageMargin"
:max=
"20"
/>
</el-form-item>
</el-form-item>
</div>
</template>
</draggable>
</template>
<el-button
@
click=
"handleAddImage"
type=
"primary"
plain
class=
"w-full"
>
添加图片
</el-button>
</el-card>
</el-form>
</el-form>
</ComponentContainerProperty>
</template>
</template>
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
...
@@ -117,7 +134,7 @@ const handleAddImage = () => {
...
@@ -117,7 +134,7 @@ const handleAddImage = () => {
formData
.
value
.
items
.
push
({}
as
CarouselItemProperty
)
formData
.
value
.
items
.
push
({}
as
CarouselItemProperty
)
}
}
// 删除图片
// 删除图片
const
handleDeleteImage
=
(
index
)
=>
{
const
handleDeleteImage
=
(
index
:
number
)
=>
{
formData
.
value
.
items
.
splice
(
index
,
1
)
formData
.
value
.
items
.
splice
(
index
,
1
)
}
}
</
script
>
</
script
>
...
...
src/components/DiyEditor/components/mobile/ImageBar/config.ts
0 → 100644
View file @
ead30e8c
import
{
ComponentStyle
,
DiyComponent
}
from
'@/components/DiyEditor/util'
/** 图片展示属性 */
export
interface
ImageBarProperty
{
// 图片链接
imgUrl
:
string
// 跳转链接
url
:
string
// 组件样式
style
:
ComponentStyle
}
// 定义组件
export
const
component
=
{
id
:
'ImageBar'
,
name
:
'图片展示'
,
icon
:
'ep:picture'
,
property
:
{
imgUrl
:
''
,
url
:
''
,
style
:
{
bgType
:
'color'
,
bgColor
:
'#fff'
,
marginBottom
:
8
}
as
ComponentStyle
}
}
as
DiyComponent
<
ImageBarProperty
>
src/components/DiyEditor/components/mobile/ImageBar/index.vue
0 → 100644
View file @
ead30e8c
<
template
>
<!-- 无图片 -->
<div
class=
"h-50px flex items-center justify-center bg-gray-3"
v-if=
"!property.imgUrl"
>
<Icon
icon=
"ep:picture"
class=
"text-gray-8 text-30px!"
/>
</div>
<el-image
class=
"min-h-30px"
v-else
:src=
"property.imgUrl"
/>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
ImageBarProperty
}
from
'./config'
/** 图片展示 */
defineOptions
({
name
:
'ImageBar'
})
defineProps
<
{
property
:
ImageBarProperty
}
>
()
</
script
>
<
style
scoped
lang=
"scss"
>
/* 图片 */
img
{
width
:
100%
;
height
:
100%
;
display
:
block
;
}
</
style
>
src/components/DiyEditor/components/mobile/ImageBar/property.vue
0 → 100644
View file @
ead30e8c
<
template
>
<ComponentContainerProperty
v-model=
"formData.style"
>
<el-form
label-width=
"80px"
:model=
"formData"
>
<el-form-item
label=
"上传图片"
prop=
"imgUrl"
>
<UploadImg
v-model=
"formData.imgUrl"
draggable=
"false"
height=
"80px"
width=
"100%"
class=
"min-w-80px"
>
<template
#
tip
>
建议宽度750
</
template
>
</UploadImg>
</el-form-item>
<el-form-item
label=
"链接"
prop=
"url"
>
<el-input
placeholder=
"链接"
v-model=
"formData.url"
/>
</el-form-item>
</el-form>
</ComponentContainerProperty>
</template>
<
script
setup
lang=
"ts"
>
import
{
ImageBarProperty
}
from
'./config'
import
{
usePropertyForm
}
from
'@/components/DiyEditor/util'
// 图片展示属性面板
defineOptions
({
name
:
'ImageBarProperty'
})
const
props
=
defineProps
<
{
modelValue
:
ImageBarProperty
}
>
()
const
emit
=
defineEmits
([
'update:modelValue'
])
const
{
formData
}
=
usePropertyForm
(
props
.
modelValue
,
emit
)
</
script
>
<
style
scoped
lang=
"scss"
></
style
>
src/components/DiyEditor/components/mobile/NavigationBar/config.ts
View file @
ead30e8c
...
@@ -29,7 +29,7 @@ export const component = {
...
@@ -29,7 +29,7 @@ export const component = {
title
:
'页面标题'
,
title
:
'页面标题'
,
description
:
''
,
description
:
''
,
navBarHeight
:
35
,
navBarHeight
:
35
,
backgroundColor
:
'#f
5f5f5
'
,
backgroundColor
:
'#f
ff
'
,
backgroundImage
:
''
,
backgroundImage
:
''
,
styleType
:
'default'
,
styleType
:
'default'
,
alwaysShow
:
true
,
alwaysShow
:
true
,
...
...
src/components/DiyEditor/components/mobile/SearchBar/config.ts
View file @
ead30e8c
import
{
DiyComponent
}
from
'@/components/DiyEditor/util'
import
{
ComponentStyle
,
DiyComponent
}
from
'@/components/DiyEditor/util'
/** 搜索框属性 */
/** 搜索框属性 */
export
interface
SearchProperty
{
export
interface
SearchProperty
{
...
@@ -7,10 +7,10 @@ export interface SearchProperty {
...
@@ -7,10 +7,10 @@ export interface SearchProperty {
borderRadius
:
number
// 框体样式
borderRadius
:
number
// 框体样式
placeholder
:
string
// 占位文字
placeholder
:
string
// 占位文字
placeholderPosition
:
PlaceholderPosition
// 占位文字位置
placeholderPosition
:
PlaceholderPosition
// 占位文字位置
backgroundColor
:
string
// 背景颜色
backgroundColor
:
string
// 框体颜色
borderColor
:
string
// 框体颜色
textColor
:
string
// 字体颜色
textColor
:
string
// 字体颜色
hotKeywords
:
string
[]
// 热词
hotKeywords
:
string
[]
// 热词
style
:
ComponentStyle
}
}
// 文字位置
// 文字位置
...
@@ -27,9 +27,17 @@ export const component = {
...
@@ -27,9 +27,17 @@ export const component = {
borderRadius
:
0
,
borderRadius
:
0
,
placeholder
:
'搜索商品'
,
placeholder
:
'搜索商品'
,
placeholderPosition
:
'left'
,
placeholderPosition
:
'left'
,
backgroundColor
:
'rgb(249, 249, 249)'
,
backgroundColor
:
'rgb(238, 238, 238)'
,
borderColor
:
'rgb(255, 255, 255)'
,
textColor
:
'rgb(150, 151, 153)'
,
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
>
}
as
DiyComponent
<
SearchProperty
>
src/components/DiyEditor/components/mobile/SearchBar/index.vue
View file @
ead30e8c
...
@@ -2,8 +2,6 @@
...
@@ -2,8 +2,6 @@
<div
<div
class=
"search-bar"
class=
"search-bar"
:style=
"
{
:style=
"
{
background: property.backgroundColor,
border: `1px solid ${property.backgroundColor}`,
color: property.textColor
color: property.textColor
}"
}"
>
>
...
@@ -12,7 +10,7 @@
...
@@ -12,7 +10,7 @@
class=
"inner"
class=
"inner"
:style=
"
{
:style=
"
{
height: `${property.height}px`,
height: `${property.height}px`,
background: property.b
order
Color,
background: property.b
ackground
Color,
borderRadius: `${property.borderRadius}px`
borderRadius: `${property.borderRadius}px`
}"
}"
>
>
...
@@ -44,13 +42,10 @@ defineProps<{ property: SearchProperty }>()
...
@@ -44,13 +42,10 @@ defineProps<{ property: SearchProperty }>()
<
style
scoped
lang=
"scss"
>
<
style
scoped
lang=
"scss"
>
.search-bar
{
.search-bar
{
position
:
relative
;
/* 搜索框 */
/* 搜索框 */
.inner
{
.inner
{
position
:
relative
;
position
:
relative
;
width
:
calc
(
100%
-
16px
);
min-height
:
28px
;
min-height
:
28px
;
margin
:
5px
auto
;
display
:
flex
;
display
:
flex
;
align-items
:
center
;
align-items
:
center
;
font-size
:
14px
;
font-size
:
14px
;
...
...
src/components/DiyEditor/components/mobile/SearchBar/property.vue
View file @
ead30e8c
<
template
>
<
template
>
<ComponentContainerProperty
v-model=
"formData.style"
>
<el-text
tag=
"p"
>
搜索热词
</el-text>
<el-text
tag=
"p"
>
搜索热词
</el-text>
<el-text
type=
"info"
size=
"small"
>
拖动左侧的小圆点可以调整热词顺序
</el-text>
<el-text
type=
"info"
size=
"small"
>
拖动左侧的小圆点可以调整热词顺序
</el-text>
...
@@ -63,16 +64,14 @@
...
@@ -63,16 +64,14 @@
<el-form-item
label=
"框体高度"
prop=
"height"
>
<el-form-item
label=
"框体高度"
prop=
"height"
>
<el-slider
v-model=
"formData!.height"
:max=
"50"
:min=
"28"
show-input
input-size=
"small"
/>
<el-slider
v-model=
"formData!.height"
:max=
"50"
:min=
"28"
show-input
input-size=
"small"
/>
</el-form-item>
</el-form-item>
<el-form-item
label=
"背景
颜色"
prop=
"backgroundColor"
>
<el-form-item
label=
"框体
颜色"
prop=
"backgroundColor"
>
<ColorInput
v-model=
"formData.backgroundColor"
/>
<ColorInput
v-model=
"formData.backgroundColor"
/>
</el-form-item>
</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"
>
<el-form-item
class=
"lef"
label=
"文本颜色"
prop=
"textColor"
>
<ColorInput
v-model=
"formData.textColor"
/>
<ColorInput
v-model=
"formData.textColor"
/>
</el-form-item>
</el-form-item>
</el-form>
</el-form>
</ComponentContainerProperty>
</template>
</template>
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
...
...
src/components/DiyEditor/components/mobile/VideoPlayer/config.ts
0 → 100644
View file @
ead30e8c
import
{
ComponentStyle
,
DiyComponent
}
from
'@/components/DiyEditor/util'
/** 视频播放属性 */
export
interface
VideoPlayerProperty
{
// 视频链接
videoUrl
:
string
// 封面链接
posterUrl
:
string
// 是否自动播放
autoplay
:
boolean
// 组件样式
style
:
VideoPlayerStyle
}
// 视频播放样式
export
interface
VideoPlayerStyle
extends
ComponentStyle
{
// 视频高度
height
:
number
}
// 定义组件
export
const
component
=
{
id
:
'VideoPlayer'
,
name
:
'视频播放'
,
icon
:
'ep:video-play'
,
property
:
{
videoUrl
:
''
,
posterUrl
:
''
,
autoplay
:
false
,
style
:
{
bgType
:
'color'
,
bgColor
:
'#fff'
,
marginBottom
:
8
,
height
:
300
}
as
ComponentStyle
}
}
as
DiyComponent
<
VideoPlayerProperty
>
src/components/DiyEditor/components/mobile/VideoPlayer/index.vue
0 → 100644
View file @
ead30e8c
<
template
>
<div
class=
"w-full"
:style=
"
{ height: `${property.style.height}px` }">
<el-image
class=
"w-full w-full"
:src=
"property.posterUrl"
v-if=
"property.posterUrl"
/>
<video
v-else
class=
"w-full w-full"
:src=
"property.videoUrl"
:poster=
"property.posterUrl"
:autoplay=
"property.autoplay"
controls
></video>
</div>
</
template
>
<
script
setup
lang=
"ts"
>
import
{
VideoPlayerProperty
}
from
'./config'
/** 视频播放 */
defineOptions
({
name
:
'VideoPlayer'
})
defineProps
<
{
property
:
VideoPlayerProperty
}
>
()
</
script
>
<
style
scoped
lang=
"scss"
>
/* 图片 */
img
{
width
:
100%
;
height
:
100%
;
display
:
block
;
}
</
style
>
src/components/DiyEditor/components/mobile/VideoPlayer/property.vue
0 → 100644
View file @
ead30e8c
<
template
>
<ComponentContainerProperty
v-model=
"formData.style"
>
<template
#
style=
"
{ formData }">
<el-form-item
label=
"高度"
prop=
"height"
>
<el-slider
v-model=
"formData.height"
:max=
"500"
:min=
"100"
show-input
input-size=
"small"
:show-input-controls=
"false"
/>
</el-form-item>
</
template
>
<el-form
label-width=
"80px"
:model=
"formData"
>
<el-form-item
label=
"上传视频"
prop=
"videoUrl"
>
<UploadFile
v-model=
"formData.videoUrl"
:file-type=
"['mp4']"
:limit=
"1"
:file-size=
"100"
class=
"min-w-80px"
/>
</el-form-item>
<el-form-item
label=
"上传封面"
prop=
"posterUrl"
>
<UploadImg
v-model=
"formData.posterUrl"
draggable=
"false"
height=
"80px"
width=
"100%"
class=
"min-w-80px"
>
<
template
#
tip
>
建议宽度750
</
template
>
</UploadImg>
</el-form-item>
<el-form-item
label=
"自动播放"
prop=
"autoplay"
>
<el-switch
v-model=
"formData.autoplay"
/>
</el-form-item>
</el-form>
</ComponentContainerProperty>
</template>
<
script
setup
lang=
"ts"
>
import
{
VideoPlayerProperty
}
from
'./config'
import
{
usePropertyForm
}
from
'@/components/DiyEditor/util'
// 视频播放属性面板
defineOptions
({
name
:
'VideoPlayerProperty'
})
const
props
=
defineProps
<
{
modelValue
:
VideoPlayerProperty
}
>
()
const
emit
=
defineEmits
([
'update:modelValue'
])
const
{
formData
}
=
usePropertyForm
(
props
.
modelValue
,
emit
)
</
script
>
<
style
scoped
lang=
"scss"
></
style
>
src/components/DiyEditor/index.vue
View file @
ead30e8c
...
@@ -33,28 +33,26 @@
...
@@ -33,28 +33,26 @@
<ComponentLibrary
ref=
"componentLibrary"
:list=
"libs"
v-if=
"libs && libs.length > 0"
/>
<ComponentLibrary
ref=
"componentLibrary"
:list=
"libs"
v-if=
"libs && libs.length > 0"
/>
<!-- 中心设计区域 -->
<!-- 中心设计区域 -->
<div
class=
"editor-center page-prop-area"
@
click=
"handlePageSelected"
>
<div
class=
"editor-center page-prop-area"
@
click=
"handlePageSelected"
>
<div
class=
"editor-design"
>
<!-- 手机顶部 -->
<!-- 手机顶部 -->
<div
class=
"editor-design-top"
>
<div
class=
"editor-design-top"
>
<!-- 手机顶部状态栏 -->
<!-- 手机顶部状态栏 -->
<img
src=
"@/assets/imgs/diy/statusBar.png"
alt=
""
class=
"status-bar"
/>
<img
src=
"@/assets/imgs/diy/statusBar.png"
alt=
""
class=
"status-bar"
/>
<!-- 手机顶部导航栏 -->
<!-- 手机顶部导航栏 -->
<NavigationBa
r
<ComponentContaine
r
v-if=
"showNavigationBar"
v-if=
"showNavigationBar"
:property=
"navigationBarComponent.property"
:component=
"navigationBarComponent"
:show-toolbar=
"false"
:active=
"selectedComponent?.id === navigationBarComponent.id"
@
click=
"handleNavigationBarSelected"
@
click=
"handleNavigationBarSelected"
:class=
"[
class=
"cursor-pointer!"
'component',
'cursor-pointer!',
{ active: selectedComponent?.id === navigationBarComponent.id }
]"
/>
/>
</div>
</div>
<!-- 手机页面编辑区域 -->
<!-- 手机页面编辑区域 -->
<el-scrollbar
class=
"editor-design-center"
height=
"100%"
view-class=
"page-prop-area"
>
<el-scrollbar
<div
height=
"100%"
class=
"phone-container"
wrap-class=
"editor-design-center page-prop-area"
:style=
"
{
view-class=
"phone-container"
:view-style=
"
{
backgroundColor: pageConfigComponent.property.backgroundColor,
backgroundColor: pageConfigComponent.property.backgroundColor,
backgroundImage: `url(${pageConfigComponent.property.backgroundImage})`
backgroundImage: `url(${pageConfigComponent.property.backgroundImage})`
}"
}"
...
@@ -71,73 +69,27 @@
...
@@ -71,73 +69,27 @@
@
change=
"handleComponentChange"
@
change=
"handleComponentChange"
>
>
<template
#
item=
"
{ element, index }">
<template
#
item=
"
{ element, index }">
<div
class=
"component-container"
@
click=
"handleComponentSelected(element, index)"
>
<ComponentContainer
<!-- 左侧组件名 -->
:component=
"element"
<div
:active=
"selectedComponentIndex === index"
:class=
"['component-name',
{ active: selectedComponentIndex === index }]"
:can-move-up=
"index > 0"
v-if="element.name"
:can-move-down=
"index
<
pageComponents
.
length
-
1
"
>
@
move=
"(direction) => handleMoveComponent(index, direction)"
{{
element
.
name
}}
@
copy=
"handleCopyComponent(index)"
</div>
@
delete=
"handleDeleteComponent(index)"
<!-- 组件内容区 -->
@
click=
"handleComponentSelected(element, index)"
<div
:class=
"['component',
{ active: selectedComponentIndex === index }]">
<component
:is=
"element.id"
:property=
"element.property"
:data-type=
"element.id"
/>
/>
</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>
</
template
>
</
template
>
</draggable>
</draggable>
</div>
</el-scrollbar>
</el-scrollbar>
<!-- 手机底部导航 -->
<!-- 手机底部导航 -->
<div
<div
v-if=
"showTabBar"
:class=
"['editor-design-bottom', 'component', 'cursor-pointer!']"
>
v-if=
"showTabBar"
<ComponentContainer
:class=
"[
:component=
"tabBarComponent"
'editor-design-bottom',
:show-toolbar=
"false"
'component',
:active=
"selectedComponent?.id === tabBarComponent.id"
'cursor-pointer!',
@
click=
"handleTabBarSelected"
{ active: selectedComponent?.id === tabBarComponent.id }
/>
]"
>
<TabBar
:property=
"tabBarComponent.property"
@
click=
"handleTabBarSelected"
/>
</div>
</div>
</div>
</div>
</div>
<!-- 右侧属性面板 -->
<!-- 右侧属性面板 -->
...
@@ -178,8 +130,6 @@ export default {
...
@@ -178,8 +130,6 @@ export default {
<
script
lang=
"ts"
setup
>
<
script
lang=
"ts"
setup
>
import
draggable
from
'vuedraggable'
import
draggable
from
'vuedraggable'
import
ComponentLibrary
from
'./components/ComponentLibrary.vue'
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
{
cloneDeep
,
includes
}
from
'lodash-es'
import
{
component
as
PAGE_CONFIG_COMPONENT
}
from
'@/components/DiyEditor/components/mobile/PageConfig/config'
import
{
component
as
PAGE_CONFIG_COMPONENT
}
from
'@/components/DiyEditor/components/mobile/PageConfig/config'
import
{
component
as
NAVIGATION_BAR_COMPONENT
}
from
'./components/mobile/NavigationBar/config'
import
{
component
as
NAVIGATION_BAR_COMPONENT
}
from
'./components/mobile/NavigationBar/config'
...
@@ -256,6 +206,9 @@ const handleSave = () => {
...
@@ -256,6 +206,9 @@ const handleSave = () => {
return
{
id
:
component
.
id
,
property
:
component
.
property
}
return
{
id
:
component
.
id
,
property
:
component
.
property
}
})
})
}
as
PageConfig
}
as
PageConfig
if
(
!
props
.
showTabBar
)
{
delete
pageConfig
.
tabBar
}
// 发送数据更新通知
// 发送数据更新通知
const
modelValue
=
isString
(
props
.
modelValue
)
?
JSON
.
stringify
(
pageConfig
)
:
pageConfig
const
modelValue
=
isString
(
props
.
modelValue
)
?
JSON
.
stringify
(
pageConfig
)
:
pageConfig
emits
(
'update:modelValue'
,
modelValue
)
emits
(
'update:modelValue'
,
modelValue
)
...
@@ -383,6 +336,7 @@ onMounted(() => setDefaultSelectedComponent())
...
@@ -383,6 +336,7 @@ onMounted(() => setDefaultSelectedComponent())
<
style
lang=
"scss"
scoped
>
<
style
lang=
"scss"
scoped
>
/* 手机宽度 */
/* 手机宽度 */
$
phone-width
:
375px
;
$
phone-width
:
375px
;
$
toolbar-height
:
42px
;
/* 根节点样式 */
/* 根节点样式 */
.editor
{
.editor
{
height
:
100%
;
height
:
100%
;
...
@@ -394,7 +348,7 @@ $phone-width: 375px;
...
@@ -394,7 +348,7 @@ $phone-width: 375px;
display
:
flex
;
display
:
flex
;
align-items
:
center
;
align-items
:
center
;
justify-content
:
space-between
;
justify-content
:
space-between
;
height
:
auto
;
height
:
$
toolbar-height
;
padding
:
0
;
padding
:
0
;
border-bottom
:
solid
1px
var
(
--el-border-color
);
border-bottom
:
solid
1px
var
(
--el-border-color
);
background-color
:
var
(
--el-bg-color
);
background-color
:
var
(
--el-bg-color
);
...
@@ -416,60 +370,54 @@ $phone-width: 375px;
...
@@ -416,60 +370,54 @@ $phone-width: 375px;
/* 中心操作区 */
/* 中心操作区 */
.editor-container
{
.editor-container
{
height
:
calc
(
height
:
calc
(
100vh
-
var
(
--top-tool-height
)
-
var
(
--tags-view-height
)
-
var
(
--app-footer-height
)
-
42px
100vh
-
var
(
--top-tool-height
)
-
var
(
--tags-view-height
)
-
var
(
--app-footer-height
)
-
$
toolbar-height
);
);
/* 右侧属性面板 */
/* 右侧属性面板 */
.editor-right
{
.editor-right
{
flex-shrink
:
0
;
flex-shrink
:
0
;
box-shadow
:
-8px
0
8px
-8px
rgba
(
0
,
0
,
0
,
0.12
);
box-shadow
:
-8px
0
8px
-8px
rgba
(
0
,
0
,
0
,
0.12
);
overflow
:
hidden
;
/* 属性面板顶部:减少内边距 */
/* 属性面板顶部:减少内边距 */
:deep(.el-card__header)
{
:deep(.el-card__header)
{
padding
:
8px
16px
;
padding
:
8px
16px
;
}
}
/* 属性面板分组 */
/* 属性面板分组 */
.property-group
{
:deep
(
.property-group
)
{
/* 属性分组 */
margin
:
0
-20px
;
:deep(.el-card__header)
{
&.el-card
{
border
:
none
;
}
/* 属性分组名称 */
.el-card__header
{
border
:
none
;
border
:
none
;
background
:
var
(
--el-bg-color-page
);
background
:
var
(
--el-bg-color-page
);
padding
:
8px
32px
;
}
.el-card__body
{
border
:
none
;
}
}
}
}
}
}
/* 中心区域 */
/* 中心区域 */
.editor-center
{
.editor-center
{
position
:
relative
;
flex
:
1
1
0
;
flex
:
1
1
0
;
padding
:
16px
0
;
background-color
:
var
(
--app-content-bg-color
);
background-color
:
var
(
--app-content-bg-color
);
display
:
flex
;
display
:
flex
;
justify-content
:
center
;
/* 中心设计区域 */
.editor-design
{
position
:
relative
;
height
:
100%
;
width
:
100%
;
display
:
flex
;
flex-direction
:
column
;
flex-direction
:
column
;
align-items
:
center
;
justify-content
:
center
;
margin
:
16px
0
0
0
;
overflow
:
hidden
;
overflow
:
hidden
;
width
:
100%
;
/* 组件 */
.component
{
border
:
1px
solid
#fff
;
width
:
$
phone-width
;
cursor
:
move
;
/* 鼠标放到组件上时 */
&:hover
{
border
:
1px
dashed
var
(
--el-color-primary
);
}
}
/* 组件选中 */
.component.active
{
border
:
2px
solid
var
(
--el-color-primary
);
}
/* 手机顶部 */
/* 手机顶部 */
.editor-design-top
{
.editor-design-top
{
width
:
$
phone-width
;
width
:
$
phone-width
;
margin
:
0
auto
;
display
:
flex
;
flex-direction
:
column
;
/* 手机顶部状态栏 */
/* 手机顶部状态栏 */
.status-bar
{
.status-bar
{
height
:
20px
;
height
:
20px
;
...
@@ -480,112 +428,23 @@ $phone-width: 375px;
...
@@ -480,112 +428,23 @@ $phone-width: 375px;
/* 手机底部导航 */
/* 手机底部导航 */
.editor-design-bottom
{
.editor-design-bottom
{
width
:
$
phone-width
;
width
:
$
phone-width
;
margin
:
0
auto
;
}
}
/* 手机页面编辑区域 */
/* 手机页面编辑区域 */
.editor-design-center
{
:deep
(
.editor-design-center
)
{
width
:
100%
;
width
:
100%
;
flex
:
1
1
0
;
:deep(.el-scrollbar__view)
{
height
:
100%
;
}
/* 主体内容 */
/* 主体内容 */
.phone-container
{
.phone-container
{
height
:
100%
;
box-sizing
:
border-box
;
position
:
relative
;
position
:
relative
;
background-repeat
:
no-repeat
;
background-repeat
:
no-repeat
;
background-size
:
100%
100%
;
background-size
:
100%
100%
;
height
:
100%
;
width
:
$
phone-width
;
width
:
$
phone-width
;
margin
:
0
auto
;
margin
:
0
auto
;
.drag-area
{
.drag-area
{
height
:
100%
;
height
:
100%
;
}
/* 组件容器(左侧:组件名称,中间:组件,右侧:操作工具栏) */
.component-container
{
width
:
100%
;
width
:
100%
;
position
:
relative
;
/* 左侧:组件名称 */
.component-name
{
position
:
absolute
;
width
:
80px
;
text-align
:
center
;
line-height
:
25px
;
height
:
25px
;
background
:
#fff
;
font-size
:
12px
;
left
:
-85px
;
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
:
-57px
;
/* 左侧小三角 */
&: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 @
ead30e8c
...
@@ -3,19 +3,56 @@ import { PageConfigProperty } from '@/components/DiyEditor/components/mobile/Pag
...
@@ -3,19 +3,56 @@ import { PageConfigProperty } from '@/components/DiyEditor/components/mobile/Pag
import
{
NavigationBarProperty
}
from
'@/components/DiyEditor/components/mobile/NavigationBar/config'
import
{
NavigationBarProperty
}
from
'@/components/DiyEditor/components/mobile/NavigationBar/config'
import
{
TabBarProperty
}
from
'@/components/DiyEditor/components/mobile/TabBar/config'
import
{
TabBarProperty
}
from
'@/components/DiyEditor/components/mobile/TabBar/config'
// 页面装修组件
export
interface
DiyComponent
<
T
>
{
export
interface
DiyComponent
<
T
>
{
// 组件唯一标识
id
:
string
id
:
string
// 组件名称
name
:
string
name
:
string
// 组件图标
icon
:
string
icon
:
string
// 组件属性
property
:
T
property
:
T
}
}
// 页面装修组件库
export
interface
DiyComponentLibrary
{
export
interface
DiyComponentLibrary
{
// 组件库名称
name
:
string
name
:
string
// 是否展开
extended
:
boolean
extended
:
boolean
// 组件列表
components
:
string
[]
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
{
export
interface
PageConfig
{
// 页面属性
// 页面属性
...
@@ -23,7 +60,7 @@ export interface PageConfig {
...
@@ -23,7 +60,7 @@ export interface PageConfig {
// 顶部导航栏属性
// 顶部导航栏属性
navigationBar
:
NavigationBarProperty
navigationBar
:
NavigationBarProperty
// 底部导航菜单属性
// 底部导航菜单属性
tabBar
:
TabBarProperty
tabBar
?
:
TabBarProperty
// 页面组件列表
// 页面组件列表
components
:
PageComponent
[]
components
:
PageComponent
[]
}
}
...
@@ -57,3 +94,27 @@ export function usePropertyForm<T>(modelValue: T, emit: Function): { formData: R
...
@@ -57,3 +94,27 @@ export function usePropertyForm<T>(modelValue: T, emit: Function): { formData: R
return
{
formData
}
return
{
formData
}
}
}
// 页面组件库
export
const
PAGE_LIBS
=
[
{
name
:
'基础组件'
,
extended
:
true
,
components
:
[
'SearchBar'
,
'NoticeBar'
,
'GridNavigation'
,
'ListNavigation'
,
'Divider'
,
'TitleBar'
]
},
{
name
:
'图文组件'
,
extended
:
true
,
components
:
[
'ImageBar'
,
'Carousel'
,
'VideoPlayer'
]
},
{
name
:
'商品组件'
,
extended
:
true
,
components
:
[
'ProductCard'
]
},
{
name
:
'会员组件'
,
extended
:
true
,
components
:
[
'UserCard'
,
'OrderCard'
,
'WalletCard'
,
'CouponCard'
]
},
{
name
:
'营销组件'
,
extended
:
true
,
components
:
[
'Combination'
,
'Seckill'
,
'Point'
,
'Coupon'
]
}
]
as
DiyComponentLibrary
[]
src/components/UploadFile/src/UploadFile.vue
View file @
ead30e8c
...
@@ -33,11 +33,10 @@
...
@@ -33,11 +33,10 @@
</div>
</div>
</template>
</template>
<
script
lang=
"ts"
setup
>
<
script
lang=
"ts"
setup
>
import
{
PropType
}
from
'vue'
import
{
propTypes
}
from
'@/utils/propTypes'
import
{
propTypes
}
from
'@/utils/propTypes'
import
{
getAccessToken
,
getTenantId
}
from
'@/utils/auth'
import
{
getAccessToken
,
getTenantId
}
from
'@/utils/auth'
import
type
{
UploadInstance
,
UploadUserFile
,
UploadProps
,
UploadRawFile
}
from
'element-plus'
import
type
{
UploadInstance
,
UploadUserFile
,
UploadProps
,
UploadRawFile
}
from
'element-plus'
import
{
isArray
,
isString
}
from
'@/utils/is'
defineOptions
({
name
:
'UploadFile'
})
defineOptions
({
name
:
'UploadFile'
})
...
@@ -45,10 +44,7 @@ const message = useMessage() // 消息弹窗
...
@@ -45,10 +44,7 @@ const message = useMessage() // 消息弹窗
const
emit
=
defineEmits
([
'update:modelValue'
])
const
emit
=
defineEmits
([
'update:modelValue'
])
const
props
=
defineProps
({
const
props
=
defineProps
({
modelValue
:
{
modelValue
:
propTypes
.
oneOfType
<
string
|
string
[]
>
([
String
,
Array
<
String
>
]).
isRequired
,
type
:
Array
as
PropType
<
UploadUserFile
[]
>
,
required
:
true
},
title
:
propTypes
.
string
.
def
(
'文件上传'
),
title
:
propTypes
.
string
.
def
(
'文件上传'
),
updateUrl
:
propTypes
.
string
.
def
(
import
.
meta
.
env
.
VITE_UPLOAD_URL
),
updateUrl
:
propTypes
.
string
.
def
(
import
.
meta
.
env
.
VITE_UPLOAD_URL
),
fileType
:
propTypes
.
array
.
def
([
'doc'
,
'xls'
,
'ppt'
,
'txt'
,
'pdf'
]),
// 文件类型, 例如['png', 'jpg', 'jpeg']
fileType
:
propTypes
.
array
.
def
([
'doc'
,
'xls'
,
'ppt'
,
'txt'
,
'pdf'
]),
// 文件类型, 例如['png', 'jpg', 'jpeg']
...
@@ -62,7 +58,7 @@ const props = defineProps({
...
@@ -62,7 +58,7 @@ const props = defineProps({
const
valueRef
=
ref
(
props
.
modelValue
)
const
valueRef
=
ref
(
props
.
modelValue
)
const
uploadRef
=
ref
<
UploadInstance
>
()
const
uploadRef
=
ref
<
UploadInstance
>
()
const
uploadList
=
ref
<
UploadUserFile
[]
>
([])
const
uploadList
=
ref
<
UploadUserFile
[]
>
([])
const
fileList
=
ref
<
UploadUserFile
[]
>
(
props
.
modelValue
)
const
fileList
=
ref
<
UploadUserFile
[]
>
(
[]
)
const
uploadNumber
=
ref
<
number
>
(
0
)
const
uploadNumber
=
ref
<
number
>
(
0
)
const
uploadHeaders
=
ref
({
const
uploadHeaders
=
ref
({
Authorization
:
'Bearer '
+
getAccessToken
(),
Authorization
:
'Bearer '
+
getAccessToken
(),
...
@@ -109,7 +105,7 @@ const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
...
@@ -109,7 +105,7 @@ const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
fileList
.
value
=
fileList
.
value
.
concat
(
uploadList
.
value
)
fileList
.
value
=
fileList
.
value
.
concat
(
uploadList
.
value
)
uploadList
.
value
=
[]
uploadList
.
value
=
[]
uploadNumber
.
value
=
0
uploadNumber
.
value
=
0
emit
(
'update:modelValue'
,
listToString
(
fileList
.
value
)
)
emit
UpdateModelValue
(
)
}
}
}
}
// 文件数超出提示
// 文件数超出提示
...
@@ -125,20 +121,47 @@ const handleRemove = (file) => {
...
@@ -125,20 +121,47 @@ const handleRemove = (file) => {
const
findex
=
fileList
.
value
.
map
((
f
)
=>
f
.
name
).
indexOf
(
file
.
name
)
const
findex
=
fileList
.
value
.
map
((
f
)
=>
f
.
name
).
indexOf
(
file
.
name
)
if
(
findex
>
-
1
)
{
if
(
findex
>
-
1
)
{
fileList
.
value
.
splice
(
findex
,
1
)
fileList
.
value
.
splice
(
findex
,
1
)
emit
(
'update:modelValue'
,
listToString
(
fileList
.
value
)
)
emit
UpdateModelValue
(
)
}
}
}
}
const
handlePreview
:
UploadProps
[
'onPreview'
]
=
(
uploadFile
)
=>
{
const
handlePreview
:
UploadProps
[
'onPreview'
]
=
(
uploadFile
)
=>
{
console
.
log
(
uploadFile
)
console
.
log
(
uploadFile
)
}
}
// 对象转成指定字符串分隔
const
listToString
=
(
list
:
UploadUserFile
[],
separator
?:
string
)
=>
{
// 监听模型绑定值变动
let
strs
=
''
watch
(
separator
=
separator
||
','
()
=>
props
.
modelValue
,
for
(
let
i
in
list
)
{
()
=>
{
strs
+=
list
[
i
].
url
+
separator
const
files
:
string
[]
=
[]
// 情况1:字符串
if
(
isString
(
props
.
modelValue
))
{
// 情况1.1:逗号分隔的多值
if
(
props
.
modelValue
.
includes
(
','
))
{
files
.
concat
(
props
.
modelValue
.
split
(
','
))
}
else
if
(
props
.
modelValue
.
length
>
0
)
{
files
.
push
(
props
.
modelValue
)
}
}
else
if
(
isArray
(
props
.
modelValue
))
{
// 情况2:字符串
files
.
concat
(
props
.
modelValue
)
}
else
{
throw
new
Error
(
'不支持的 modelValue 类型'
)
}
fileList
.
value
=
files
.
map
((
url
:
string
)
=>
{
return
{
url
,
name
:
url
.
substring
(
url
.
lastIndexOf
(
'/'
)
+
1
)
}
as
UploadUserFile
})
},
{
immediate
:
true
}
)
// 发送文件链接列表更新
const
emitUpdateModelValue
=
()
=>
{
// 情况1:数组结果
let
result
:
string
|
string
[]
=
fileList
.
value
.
map
((
file
)
=>
file
.
url
!
)
// 情况2:逗号分隔的字符串
if
(
isString
(
props
.
modelValue
))
{
result
=
result
.
join
(
','
)
}
}
return
strs
!=
''
?
strs
.
substr
(
0
,
strs
.
length
-
1
)
:
''
emit
(
'update:modelValue'
,
result
)
}
}
</
script
>
</
script
>
<
style
scoped
lang=
"scss"
>
<
style
scoped
lang=
"scss"
>
...
...
src/components/VerticalButtonGroup/index.vue
0 → 100644
View file @
ead30e8c
<
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
>
src/layout/components/Footer/src/Footer.vue
View file @
ead30e8c
...
@@ -19,6 +19,6 @@ const title = computed(() => appStore.getTitle)
...
@@ -19,6 +19,6 @@ const title = computed(() => appStore.getTitle)
:class=
"prefixCls"
:class=
"prefixCls"
class=
"h-[var(--app-footer-height)] bg-[var(--app-content-bg-color)] text-center leading-[var(--app-footer-height)] text-[var(--el-text-color-placeholder)] dark:bg-[var(--el-bg-color)]"
class=
"h-[var(--app-footer-height)] bg-[var(--app-content-bg-color)] text-center leading-[var(--app-footer-height)] text-[var(--el-text-color-placeholder)] dark:bg-[var(--el-bg-color)]"
>
>
<
p
style=
"font-size: 14px"
>
Copyright ©2022-
{{
title
}}
</p
>
<
span
class=
"text-14px"
>
Copyright ©2022-
{{
title
}}
</span
>
</div>
</div>
</
template
>
</
template
>
src/views/mall/promotion/diy/page/decorate.vue
View file @
ead30e8c
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
v-if=
"formData && !formLoading"
v-if=
"formData && !formLoading"
v-model=
"formData.property"
v-model=
"formData.property"
:title=
"formData.name"
:title=
"formData.name"
:libs=
"
componentLibs
"
:libs=
"
PAGE_LIBS
"
:show-page-config=
"true"
:show-page-config=
"true"
:show-navigation-bar=
"true"
:show-navigation-bar=
"true"
:show-tab-bar=
"false"
:show-tab-bar=
"false"
...
@@ -13,35 +13,11 @@
...
@@ -13,35 +13,11 @@
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
*
as
DiyPageApi
from
'@/api/mall/promotion/diy/page'
import
*
as
DiyPageApi
from
'@/api/mall/promotion/diy/page'
import
{
useTagsViewStore
}
from
'@/store/modules/tagsView'
import
{
useTagsViewStore
}
from
'@/store/modules/tagsView'
import
{
DiyComponentLibrary
}
from
'@/components/DiyEditor/util'
import
{
PAGE_LIBS
}
from
'@/components/DiyEditor/util'
/** 装修页面表单 */
/** 装修页面表单 */
defineOptions
({
name
:
'DiyPageDecorate'
})
defineOptions
({
name
:
'DiyPageDecorate'
})
// 组件库
const
componentLibs
=
[
{
name
:
'基础组件'
,
extended
:
true
,
components
:
[
'SearchBar'
,
'NoticeBar'
,
'GridNavigation'
,
'ListNavigation'
,
'Divider'
,
'TitleBar'
]
},
{
name
:
'图文组件'
,
extended
:
true
,
components
:
[
'Carousel'
]
},
{
name
:
'商品组件'
,
extended
:
true
,
components
:
[
'ProductCard'
]
},
{
name
:
'会员组件'
,
extended
:
true
,
components
:
[
'UserCard'
,
'OrderCard'
,
'WalletCard'
,
'CouponCard'
]
},
{
name
:
'营销组件'
,
extended
:
true
,
components
:
[
'Combination'
,
'Seckill'
,
'Point'
,
'Coupon'
]
}
]
as
DiyComponentLibrary
[]
const
message
=
useMessage
()
// 消息弹窗
const
message
=
useMessage
()
// 消息弹窗
const
formLoading
=
ref
(
false
)
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const
formLoading
=
ref
(
false
)
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
...
...
src/views/mall/promotion/diy/template/decorate.vue
View file @
ead30e8c
...
@@ -28,7 +28,7 @@
...
@@ -28,7 +28,7 @@
import
*
as
DiyTemplateApi
from
'@/api/mall/promotion/diy/template'
import
*
as
DiyTemplateApi
from
'@/api/mall/promotion/diy/template'
import
*
as
DiyPageApi
from
'@/api/mall/promotion/diy/page'
import
*
as
DiyPageApi
from
'@/api/mall/promotion/diy/page'
import
{
useTagsViewStore
}
from
'@/store/modules/tagsView'
import
{
useTagsViewStore
}
from
'@/store/modules/tagsView'
import
{
DiyComponentLibrary
}
from
'@/components/DiyEditor/util'
import
{
DiyComponentLibrary
,
PAGE_LIBS
}
from
'@/components/DiyEditor/util'
/** 装修模板表单 */
/** 装修模板表单 */
defineOptions
({
name
:
'DiyTemplateDecorate'
})
defineOptions
({
name
:
'DiyTemplateDecorate'
})
...
@@ -62,29 +62,6 @@ const getPageDetail = async (id: any) => {
...
@@ -62,29 +62,6 @@ const getPageDetail = async (id: any) => {
// 模板组件库
// 模板组件库
const
templateLibs
=
[]
as
DiyComponentLibrary
[]
const
templateLibs
=
[]
as
DiyComponentLibrary
[]
// 页面组件库
const
pageLibs
=
[
{
name
:
'基础组件'
,
extended
:
true
,
components
:
[
'SearchBar'
,
'NoticeBar'
,
'GridNavigation'
,
'ListNavigation'
,
'Divider'
,
'TitleBar'
]
},
{
name
:
'图文组件'
,
extended
:
true
,
components
:
[
'Carousel'
]
},
{
name
:
'商品组件'
,
extended
:
true
,
components
:
[
'ProductCard'
]
},
{
name
:
'会员组件'
,
extended
:
true
,
components
:
[
'UserCard'
,
'OrderCard'
,
'WalletCard'
,
'CouponCard'
]
},
{
name
:
'营销组件'
,
extended
:
true
,
components
:
[
'Combination'
,
'Seckill'
,
'Point'
,
'Coupon'
]
}
]
as
DiyComponentLibrary
[]
// 当前组件库
// 当前组件库
const
libs
=
ref
<
DiyComponentLibrary
[]
>
(
templateLibs
)
const
libs
=
ref
<
DiyComponentLibrary
[]
>
(
templateLibs
)
// 模板选项切换
// 模板选项切换
...
@@ -97,7 +74,7 @@ const handleTemplateItemChange = () => {
...
@@ -97,7 +74,7 @@ const handleTemplateItemChange = () => {
}
}
// 编辑页面
// 编辑页面
libs
.
value
=
pageLibs
libs
.
value
=
PAGE_LIBS
currentFormData
.
value
=
formData
.
value
!
.
pages
.
find
(
currentFormData
.
value
=
formData
.
value
!
.
pages
.
find
(
(
page
:
DiyPageApi
.
DiyPageVO
)
=>
page
.
name
===
templateItems
[
selectedTemplateItem
.
value
].
name
(
page
:
DiyPageApi
.
DiyPageVO
)
=>
page
.
name
===
templateItems
[
selectedTemplateItem
.
value
].
name
)
)
...
...
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