Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
jinli gu
RuoYi Vue
Commits
d00dc3b0
Unverified
Commit
d00dc3b0
authored
Feb 12, 2022
by
若依
Committed by
Gitee
Feb 12, 2022
Browse files
!426 修正单词拼写错误
Merge pull request !426 from 稚屿/master
parents
f5c69bae
c99eb980
Changes
22
Hide whitespace changes
Inline
Side-by-side
ruoyi-ui/src/views/tool/build/TreeNodeDialog.vue
View file @
d00dc3b0
<
template
>
<div>
<el-dialog
v-bind=
"$attrs"
:close-on-click-modal=
"false"
:modal-append-to-body=
"false"
v-on=
"$listeners"
@
open=
"onOpen"
@
close=
"onClose"
>
<el-row
:gutter=
"0"
>
<el-form
ref=
"elForm"
:model=
"formData"
:rules=
"rules"
size=
"small"
label-width=
"100px"
>
<el-col
:span=
"24"
>
<el-form-item
label=
"选项名"
prop=
"label"
>
<el-input
v-model=
"formData.label"
placeholder=
"请输入选项名"
clearable
/>
</el-form-item>
</el-col>
<el-col
:span=
"24"
>
<el-form-item
label=
"选项值"
prop=
"value"
>
<el-input
v-model=
"formData.value"
placeholder=
"请输入选项值"
clearable
>
<el-select
slot=
"append"
v-model=
"dataType"
:style=
"
{width: '100px'}"
>
<el-option
v-for=
"(item, index) in dataTypeOptions"
:key=
"index"
:label=
"item.label"
:value=
"item.value"
:disabled=
"item.disabled"
/>
</el-select>
</el-input>
</el-form-item>
</el-col>
</el-form>
</el-row>
<div
slot=
"footer"
>
<el-button
type=
"primary"
@
click=
"hand
e
lConfirm"
>
确定
</el-button>
<el-button
@
click=
"close"
>
取消
</el-button>
</div>
</el-dialog>
</div>
</
template
>
<
script
>
import
{
isNumberStr
}
from
'
@/utils/index
'
export
default
{
components
:
{},
inheritAttrs
:
false
,
props
:
[],
data
()
{
return
{
id
:
100
,
formData
:
{
label
:
undefined
,
value
:
undefined
},
rules
:
{
label
:
[
{
required
:
true
,
message
:
'
请输入选项名
'
,
trigger
:
'
blur
'
}
],
value
:
[
{
required
:
true
,
message
:
'
请输入选项值
'
,
trigger
:
'
blur
'
}
]
},
dataType
:
'
string
'
,
dataTypeOptions
:
[
{
label
:
'
字符串
'
,
value
:
'
string
'
},
{
label
:
'
数字
'
,
value
:
'
number
'
}
]
}
},
computed
:
{},
watch
:
{
// eslint-disable-next-line func-names
'
formData.value
'
:
function
(
val
)
{
this
.
dataType
=
isNumberStr
(
val
)
?
'
number
'
:
'
string
'
}
},
created
()
{},
mounted
()
{},
methods
:
{
onOpen
()
{
this
.
formData
=
{
label
:
undefined
,
value
:
undefined
}
},
onClose
()
{},
close
()
{
this
.
$emit
(
'
update:visible
'
,
false
)
},
hand
e
lConfirm
()
{
this
.
$refs
.
elForm
.
validate
(
valid
=>
{
if
(
!
valid
)
return
if
(
this
.
dataType
===
'
number
'
)
{
this
.
formData
.
value
=
parseFloat
(
this
.
formData
.
value
)
}
this
.
formData
.
id
=
this
.
id
++
this
.
$emit
(
'
commit
'
,
this
.
formData
)
this
.
close
()
})
}
}
}
</
script
>
<
template
>
<div>
<el-dialog
v-bind=
"$attrs"
:close-on-click-modal=
"false"
:modal-append-to-body=
"false"
v-on=
"$listeners"
@
open=
"onOpen"
@
close=
"onClose"
>
<el-row
:gutter=
"0"
>
<el-form
ref=
"elForm"
:model=
"formData"
:rules=
"rules"
size=
"small"
label-width=
"100px"
>
<el-col
:span=
"24"
>
<el-form-item
label=
"选项名"
prop=
"label"
>
<el-input
v-model=
"formData.label"
placeholder=
"请输入选项名"
clearable
/>
</el-form-item>
</el-col>
<el-col
:span=
"24"
>
<el-form-item
label=
"选项值"
prop=
"value"
>
<el-input
v-model=
"formData.value"
placeholder=
"请输入选项值"
clearable
>
<el-select
slot=
"append"
v-model=
"dataType"
:style=
"
{width: '100px'}"
>
<el-option
v-for=
"(item, index) in dataTypeOptions"
:key=
"index"
:label=
"item.label"
:value=
"item.value"
:disabled=
"item.disabled"
/>
</el-select>
</el-input>
</el-form-item>
</el-col>
</el-form>
</el-row>
<div
slot=
"footer"
>
<el-button
type=
"primary"
@
click=
"handl
e
Confirm"
>
确定
</el-button>
<el-button
@
click=
"close"
>
取消
</el-button>
</div>
</el-dialog>
</div>
</
template
>
<
script
>
import
{
isNumberStr
}
from
'
@/utils/index
'
export
default
{
components
:
{},
inheritAttrs
:
false
,
props
:
[],
data
()
{
return
{
id
:
100
,
formData
:
{
label
:
undefined
,
value
:
undefined
},
rules
:
{
label
:
[
{
required
:
true
,
message
:
'
请输入选项名
'
,
trigger
:
'
blur
'
}
],
value
:
[
{
required
:
true
,
message
:
'
请输入选项值
'
,
trigger
:
'
blur
'
}
]
},
dataType
:
'
string
'
,
dataTypeOptions
:
[
{
label
:
'
字符串
'
,
value
:
'
string
'
},
{
label
:
'
数字
'
,
value
:
'
number
'
}
]
}
},
computed
:
{},
watch
:
{
// eslint-disable-next-line func-names
'
formData.value
'
:
function
(
val
)
{
this
.
dataType
=
isNumberStr
(
val
)
?
'
number
'
:
'
string
'
}
},
created
()
{},
mounted
()
{},
methods
:
{
onOpen
()
{
this
.
formData
=
{
label
:
undefined
,
value
:
undefined
}
},
onClose
()
{},
close
()
{
this
.
$emit
(
'
update:visible
'
,
false
)
},
handl
e
Confirm
()
{
this
.
$refs
.
elForm
.
validate
(
valid
=>
{
if
(
!
valid
)
return
if
(
this
.
dataType
===
'
number
'
)
{
this
.
formData
.
value
=
parseFloat
(
this
.
formData
.
value
)
}
this
.
formData
.
id
=
this
.
id
++
this
.
$emit
(
'
commit
'
,
this
.
formData
)
this
.
close
()
})
}
}
}
</
script
>
ruoyi-ui/src/views/tool/build/index.vue
View file @
d00dc3b0
<
template
>
<div
class=
"container"
>
<div
class=
"left-board"
>
<div
class=
"logo-wrapper"
>
<div
class=
"logo"
>
<img
:src=
"logo"
alt=
"logo"
>
Form Generator
</div>
</div>
<el-scrollbar
class=
"left-scrollbar"
>
<div
class=
"components-list"
>
<div
class=
"components-title"
>
<svg-icon
icon-class=
"component"
/>
输入型组件
</div>
<draggable
class=
"components-draggable"
:list=
"inputComponents"
:group=
"
{ name: 'componentsGroup', pull: 'clone', put: false }"
:clone="cloneComponent"
draggable=".components-item"
:sort="false"
@end="onEnd"
>
<div
v-for=
"(element, index) in inputComponents"
:key=
"index"
class=
"components-item"
@
click=
"addComponent(element)"
>
<div
class=
"components-body"
>
<svg-icon
:icon-class=
"element.tagIcon"
/>
{{
element
.
label
}}
</div>
</div>
</draggable>
<div
class=
"components-title"
>
<svg-icon
icon-class=
"component"
/>
选择型组件
</div>
<draggable
class=
"components-draggable"
:list=
"selectComponents"
:group=
"
{ name: 'componentsGroup', pull: 'clone', put: false }"
:clone="cloneComponent"
draggable=".components-item"
:sort="false"
@end="onEnd"
>
<div
v-for=
"(element, index) in selectComponents"
:key=
"index"
class=
"components-item"
@
click=
"addComponent(element)"
>
<div
class=
"components-body"
>
<svg-icon
:icon-class=
"element.tagIcon"
/>
{{
element
.
label
}}
</div>
</div>
</draggable>
<div
class=
"components-title"
>
<svg-icon
icon-class=
"component"
/>
布局型组件
</div>
<draggable
class=
"components-draggable"
:list=
"layoutComponents"
:group=
"
{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent"
draggable=".components-item" :sort="false" @end="onEnd"
>
<div
v-for=
"(element, index) in layoutComponents"
:key=
"index"
class=
"components-item"
@
click=
"addComponent(element)"
>
<div
class=
"components-body"
>
<svg-icon
:icon-class=
"element.tagIcon"
/>
{{
element
.
label
}}
</div>
</div>
</draggable>
</div>
</el-scrollbar>
</div>
<div
class=
"center-board"
>
<div
class=
"action-bar"
>
<el-button
icon=
"el-icon-download"
type=
"text"
@
click=
"download"
>
导出vue文件
</el-button>
<el-button
class=
"copy-btn-main"
icon=
"el-icon-document-copy"
type=
"text"
@
click=
"copy"
>
复制代码
</el-button>
<el-button
class=
"delete-btn"
icon=
"el-icon-delete"
type=
"text"
@
click=
"empty"
>
清空
</el-button>
</div>
<el-scrollbar
class=
"center-scrollbar"
>
<el-row
class=
"center-board-row"
:gutter=
"formConf.gutter"
>
<el-form
:size=
"formConf.size"
:label-position=
"formConf.labelPosition"
:disabled=
"formConf.disabled"
:label-width=
"formConf.labelWidth + 'px'"
>
<draggable
class=
"drawing-board"
:list=
"drawingList"
:animation=
"340"
group=
"componentsGroup"
>
<draggable-item
v-for=
"(element, index) in drawingList"
:key=
"element.renderKey"
:drawing-list=
"drawingList"
:element=
"element"
:index=
"index"
:active-id=
"activeId"
:form-conf=
"formConf"
@
activeItem=
"activeFormItem"
@
copyItem=
"drawingItemCopy"
@
deleteItem=
"drawingItemDelete"
/>
</draggable>
<div
v-show=
"!drawingList.length"
class=
"empty-info"
>
从左侧拖入或点选组件进行表单设计
</div>
</el-form>
</el-row>
</el-scrollbar>
</div>
<right-panel
:active-data=
"activeData"
:form-conf=
"formConf"
:show-field=
"!!drawingList.length"
@
tag-change=
"tagChange"
/>
<code-type-dialog
:visible.sync=
"dialogVisible"
title=
"选择生成类型"
:show-file-name=
"showFileName"
@
confirm=
"generate"
/>
<input
id=
"copyNode"
type=
"hidden"
>
</div>
</
template
>
<
script
>
import
draggable
from
'
vuedraggable
'
import
beautifier
from
'
js-beautify
'
import
ClipboardJS
from
'
clipboard
'
import
render
from
'
@/utils/generator/render
'
import
RightPanel
from
'
./RightPanel
'
import
{
inputComponents
,
selectComponents
,
layoutComponents
,
formConf
}
from
'
@/utils/generator/config
'
import
{
beautifierConf
,
titleCase
}
from
'
@/utils/index
'
import
{
makeUpHtml
,
vueTemplate
,
vueScript
,
cssStyle
}
from
'
@/utils/generator/html
'
import
{
makeUpJs
}
from
'
@/utils/generator/js
'
import
{
makeUpCss
}
from
'
@/utils/generator/css
'
import
drawingDefa
l
ut
from
'
@/utils/generator/drawingDefa
l
ut
'
import
logo
from
'
@/assets/logo/logo.png
'
import
CodeTypeDialog
from
'
./CodeTypeDialog
'
import
DraggableItem
from
'
./DraggableItem
'
let
oldActiveId
let
tempActiveData
export
default
{
components
:
{
draggable
,
render
,
RightPanel
,
CodeTypeDialog
,
DraggableItem
},
data
()
{
return
{
logo
,
idGlobal
:
100
,
formConf
,
inputComponents
,
selectComponents
,
layoutComponents
,
labelWidth
:
100
,
drawingList
:
drawingDefa
l
ut
,
drawingData
:
{},
activeId
:
drawingDefa
l
ut
[
0
].
formId
,
drawerVisible
:
false
,
formData
:
{},
dialogVisible
:
false
,
generateConf
:
null
,
showFileName
:
false
,
activeData
:
drawingDefa
l
ut
[
0
]
}
},
created
()
{
// 防止 firefox 下 拖拽 会新打卡一个选项卡
document
.
body
.
ondrop
=
event
=>
{
event
.
preventDefault
()
event
.
stopPropagation
()
}
},
watch
:
{
// eslint-disable-next-line func-names
'
activeData.label
'
:
function
(
val
,
oldVal
)
{
if
(
this
.
activeData
.
placeholder
===
undefined
||
!
this
.
activeData
.
tag
||
oldActiveId
!==
this
.
activeId
)
{
return
}
this
.
activeData
.
placeholder
=
this
.
activeData
.
placeholder
.
replace
(
oldVal
,
''
)
+
val
},
activeId
:
{
handler
(
val
)
{
oldActiveId
=
val
},
immediate
:
true
}
},
mounted
()
{
const
clipboard
=
new
ClipboardJS
(
'
#copyNode
'
,
{
text
:
trigger
=>
{
const
codeStr
=
this
.
generateCode
()
this
.
$notify
({
title
:
'
成功
'
,
message
:
'
代码已复制到剪切板,可粘贴。
'
,
type
:
'
success
'
})
return
codeStr
}
})
clipboard
.
on
(
'
error
'
,
e
=>
{
this
.
$message
.
error
(
'
代码复制失败
'
)
})
},
methods
:
{
activeFormItem
(
element
)
{
this
.
activeData
=
element
this
.
activeId
=
element
.
formId
},
onEnd
(
obj
,
a
)
{
if
(
obj
.
from
!==
obj
.
to
)
{
this
.
activeData
=
tempActiveData
this
.
activeId
=
this
.
idGlobal
}
},
addComponent
(
item
)
{
const
clone
=
this
.
cloneComponent
(
item
)
this
.
drawingList
.
push
(
clone
)
this
.
activeFormItem
(
clone
)
},
cloneComponent
(
origin
)
{
const
clone
=
JSON
.
parse
(
JSON
.
stringify
(
origin
))
clone
.
formId
=
++
this
.
idGlobal
clone
.
span
=
formConf
.
span
clone
.
renderKey
=
+
new
Date
()
// 改变renderKey后可以实现强制更新组件
if
(
!
clone
.
layout
)
clone
.
layout
=
'
colFormItem
'
if
(
clone
.
layout
===
'
colFormItem
'
)
{
clone
.
vModel
=
`field
${
this
.
idGlobal
}
`
clone
.
placeholder
!==
undefined
&&
(
clone
.
placeholder
+=
clone
.
label
)
tempActiveData
=
clone
}
else
if
(
clone
.
layout
===
'
rowFormItem
'
)
{
delete
clone
.
label
clone
.
componentName
=
`row
${
this
.
idGlobal
}
`
clone
.
gutter
=
this
.
formConf
.
gutter
tempActiveData
=
clone
}
return
tempActiveData
},
AssembleFormData
()
{
this
.
formData
=
{
fields
:
JSON
.
parse
(
JSON
.
stringify
(
this
.
drawingList
)),
...
this
.
formConf
}
},
generate
(
data
)
{
const
func
=
this
[
`exec
${
titleCase
(
this
.
operationType
)}
`
]
this
.
generateConf
=
data
func
&&
func
(
data
)
},
execRun
(
data
)
{
this
.
AssembleFormData
()
this
.
drawerVisible
=
true
},
execDownload
(
data
)
{
const
codeStr
=
this
.
generateCode
()
const
blob
=
new
Blob
([
codeStr
],
{
type
:
'
text/plain;charset=utf-8
'
})
this
.
$download
.
saveAs
(
blob
,
data
.
fileName
)
},
execCopy
(
data
)
{
document
.
getElementById
(
'
copyNode
'
).
click
()
},
empty
()
{
this
.
$confirm
(
'
确定要清空所有组件吗?
'
,
'
提示
'
,
{
type
:
'
warning
'
}).
then
(
()
=>
{
this
.
drawingList
=
[]
}
)
},
drawingItemCopy
(
item
,
parent
)
{
let
clone
=
JSON
.
parse
(
JSON
.
stringify
(
item
))
clone
=
this
.
createIdAndKey
(
clone
)
parent
.
push
(
clone
)
this
.
activeFormItem
(
clone
)
},
createIdAndKey
(
item
)
{
item
.
formId
=
++
this
.
idGlobal
item
.
renderKey
=
+
new
Date
()
if
(
item
.
layout
===
'
colFormItem
'
)
{
item
.
vModel
=
`field
${
this
.
idGlobal
}
`
}
else
if
(
item
.
layout
===
'
rowFormItem
'
)
{
item
.
componentName
=
`row
${
this
.
idGlobal
}
`
}
if
(
Array
.
isArray
(
item
.
children
))
{
item
.
children
=
item
.
children
.
map
(
childItem
=>
this
.
createIdAndKey
(
childItem
))
}
return
item
},
drawingItemDelete
(
index
,
parent
)
{
parent
.
splice
(
index
,
1
)
this
.
$nextTick
(()
=>
{
const
len
=
this
.
drawingList
.
length
if
(
len
)
{
this
.
activeFormItem
(
this
.
drawingList
[
len
-
1
])
}
})
},
generateCode
()
{
const
{
type
}
=
this
.
generateConf
this
.
AssembleFormData
()
const
script
=
vueScript
(
makeUpJs
(
this
.
formData
,
type
))
const
html
=
vueTemplate
(
makeUpHtml
(
this
.
formData
,
type
))
const
css
=
cssStyle
(
makeUpCss
(
this
.
formData
))
return
beautifier
.
html
(
html
+
script
+
css
,
beautifierConf
.
html
)
},
download
()
{
this
.
dialogVisible
=
true
this
.
showFileName
=
true
this
.
operationType
=
'
download
'
},
run
()
{
this
.
dialogVisible
=
true
this
.
showFileName
=
false
this
.
operationType
=
'
run
'
},
copy
()
{
this
.
dialogVisible
=
true
this
.
showFileName
=
false
this
.
operationType
=
'
copy
'
},
tagChange
(
newTag
)
{
newTag
=
this
.
cloneComponent
(
newTag
)
newTag
.
vModel
=
this
.
activeData
.
vModel
newTag
.
formId
=
this
.
activeId
newTag
.
span
=
this
.
activeData
.
span
delete
this
.
activeData
.
tag
delete
this
.
activeData
.
tagIcon
delete
this
.
activeData
.
document
Object
.
keys
(
newTag
).
forEach
(
key
=>
{
if
(
this
.
activeData
[
key
]
!==
undefined
&&
typeof
this
.
activeData
[
key
]
===
typeof
newTag
[
key
])
{
newTag
[
key
]
=
this
.
activeData
[
key
]
}
})
this
.
activeData
=
newTag
this
.
updateDrawingList
(
newTag
,
this
.
drawingList
)
},
updateDrawingList
(
newTag
,
list
)
{
const
index
=
list
.
findIndex
(
item
=>
item
.
formId
===
this
.
activeId
)
if
(
index
>
-
1
)
{
list
.
splice
(
index
,
1
,
newTag
)
}
else
{
list
.
forEach
(
item
=>
{
if
(
Array
.
isArray
(
item
.
children
))
this
.
updateDrawingList
(
newTag
,
item
.
children
)
})
}
}
}
}
</
script
>
<
style
lang=
'scss'
>
body
,
html
{
margin
:
0
;
padding
:
0
;
background
:
#fff
;
-moz-osx-font-smoothing
:
grayscale
;
-webkit-font-smoothing
:
antialiased
;
text-rendering
:
optimizeLegibility
;
font-family
:
-
apple-system
,
BlinkMacSystemFont
,
Segoe
UI
,
Helvetica
,
Arial
,
sans-serif
,
Apple
Color
Emoji
,
Segoe
UI
Emoji
;
}
input
,
textarea
{
font-family
:
-
apple-system
,
BlinkMacSystemFont
,
Segoe
UI
,
Helvetica
,
Arial
,
sans-serif
,
Apple
Color
Emoji
,
Segoe
UI
Emoji
;
}
.editor-tabs
{
background
:
#121315
;
.el-tabs__header
{
margin
:
0
;
border-bottom-color
:
#121315
;
.el-tabs__nav
{
border-color
:
#121315
;
}
}
.el-tabs__item
{
height
:
32px
;
line-height
:
32px
;
color
:
#888a8e
;
border-left
:
1px
solid
#121315
!
important
;
background
:
#363636
;
margin-right
:
5px
;
user-select
:
none
;
}
.el-tabs__item.is-active
{
background
:
#1e1e1e
;
border-bottom-color
:
#1e1e1e
!
important
;
color
:
#fff
;
}
.el-icon-edit
{
color
:
#f1fa8c
;
}
.el-icon-document
{
color
:
#a95812
;
}
}
// home
.right-scrollbar
{
.el-scrollbar__view
{
padding
:
12px
18px
15px
15px
;
}
}
.left-scrollbar
.el-scrollbar__wrap
{
box-sizing
:
border-box
;
overflow-x
:
hidden
!
important
;
margin-bottom
:
0
!
important
;
}
.center-tabs
{
.el-tabs__header
{
margin-bottom
:
0
!
important
;
}
.el-tabs__item
{
width
:
50%
;
text-align
:
center
;
}
.el-tabs__nav
{
width
:
100%
;
}
}
.reg-item
{
padding
:
12px
6px
;
background
:
#f8f8f8
;
position
:
relative
;
border-radius
:
4px
;
.close-btn
{
position
:
absolute
;
right
:
-6px
;
top
:
-6px
;
display
:
block
;
width
:
16px
;
height
:
16px
;
line-height
:
16px
;
background
:
rgba
(
0
,
0
,
0
,
0
.2
);
border-radius
:
50%
;
color
:
#fff
;
text-align
:
center
;
z-index
:
1
;
cursor
:
pointer
;
font-size
:
12px
;
&
:hover
{
background
:
rgba
(
210
,
23
,
23
,
0
.5
)
}
}
&
+
.reg-item
{
margin-top
:
18px
;
}
}
.action-bar
{
&
.el-button
+
.el-button
{
margin-left
:
15px
;
}
&
i
{
font-size
:
20px
;
vertical-align
:
middle
;
position
:
relative
;
top
:
-1px
;
}
}
.custom-tree-node
{
width
:
100%
;
font-size
:
14px
;
.node-operation
{
float
:
right
;
}
i
[
class
*=
"el-icon"
]
+
i
[
class
*=
"el-icon"
]
{
margin-left
:
6px
;
}
.el-icon-plus
{
color
:
#409EFF
;
}
.el-icon-delete
{
color
:
#157a0c
;
}
}
.left-scrollbar
.el-scrollbar__view
{
overflow-x
:
hidden
;
}
.el-rate
{
display
:
inline-block
;
vertical-align
:
text-top
;
}
.el-upload__tip
{
line-height
:
1
.2
;
}
$selectedColor
:
#f6f7ff
;
$lighterBlue
:
#409EFF
;
.container
{
position
:
relative
;
width
:
100%
;
height
:
100%
;
}
.components-list
{
padding
:
8px
;
box-sizing
:
border-box
;
height
:
100%
;
.components-item
{
display
:
inline-block
;
width
:
48%
;
margin
:
1%
;
transition
:
transform
0ms
!
important
;
}
}
.components-draggable
{
padding-bottom
:
20px
;
}
.components-title
{
font-size
:
14px
;
color
:
#222
;
margin
:
6px
2px
;
.svg-icon
{
color
:
#666
;
font-size
:
18px
;
}
}
.components-body
{
padding
:
8px
10px
;
background
:
$selectedColor
;
font-size
:
12px
;
cursor
:
move
;
border
:
1px
dashed
$selectedColor
;
border-radius
:
3px
;
.svg-icon
{
color
:
#777
;
font-size
:
15px
;
}
&
:hover
{
border
:
1px
dashed
#787be8
;
color
:
#787be8
;
.svg-icon
{
color
:
#787be8
;
}
}
}
.left-board
{
width
:
260px
;
position
:
absolute
;
left
:
0
;
top
:
0
;
height
:
100vh
;
}
.left-scrollbar
{
height
:
calc
(
100vh
-
42px
);
overflow
:
hidden
;
}
.center-scrollbar
{
height
:
calc
(
100vh
-
42px
);
overflow
:
hidden
;
border-left
:
1px
solid
#f1e8e8
;
border-right
:
1px
solid
#f1e8e8
;
box-sizing
:
border-box
;
}
.center-board
{
height
:
100vh
;
width
:
auto
;
margin
:
0
350px
0
260px
;
box-sizing
:
border-box
;
}
.empty-info
{
position
:
absolute
;
top
:
46%
;
left
:
0
;
right
:
0
;
text-align
:
center
;
font-size
:
18px
;
color
:
#ccb1ea
;
letter-spacing
:
4px
;
}
.action-bar
{
position
:
relative
;
height
:
42px
;
text-align
:
right
;
padding
:
0
15px
;
box-sizing
:
border-box
;;
border
:
1px
solid
#f1e8e8
;
border-top
:
none
;
border-left
:
none
;
.delete-btn
{
color
:
#F56C6C
;
}
}
.logo-wrapper
{
position
:
relative
;
height
:
42px
;
background
:
#fff
;
border-bottom
:
1px
solid
#f1e8e8
;
box-sizing
:
border-box
;
}
.logo
{
position
:
absolute
;
left
:
12px
;
top
:
6px
;
line-height
:
30px
;
color
:
#00afff
;
font-weight
:
600
;
font-size
:
17px
;
white-space
:
nowrap
;
>
img
{
width
:
30px
;
height
:
30px
;
vertical-align
:
top
;
}
.github
{
display
:
inline-block
;
vertical-align
:
sub
;
margin-left
:
15px
;
>
img
{
height
:
22px
;
}
}
}
.center-board-row
{
padding
:
12px
12px
15px
12px
;
box-sizing
:
border-box
;
&
>
.el-form
{
// 69 = 12+15+42
height
:
calc
(
100vh
-
69px
);
}
}
.drawing-board
{
height
:
100%
;
position
:
relative
;
.components-body
{
padding
:
0
;
margin
:
0
;
font-size
:
0
;
}
.sortable-ghost
{
position
:
relative
;
display
:
block
;
overflow
:
hidden
;
&
::before
{
content
:
" "
;
position
:
absolute
;
left
:
0
;
right
:
0
;
top
:
0
;
height
:
3px
;
background
:
rgb
(
89
,
89
,
223
);
z-index
:
2
;
}
}
.components-item.sortable-ghost
{
width
:
100%
;
height
:
60px
;
background-color
:
$selectedColor
;
}
.active-from-item
{
&
>
.el-form-item
{
background
:
$selectedColor
;
border-radius
:
6px
;
}
&
>
.drawing-item-copy
,
&
>
.drawing-item-delete
{
display
:
initial
;
}
&
>
.component-name
{
color
:
$lighterBlue
;
}
}
.el-form-item
{
margin-bottom
:
15px
;
}
}
.drawing-item
{
position
:
relative
;
cursor
:
move
;
&
.unfocus-bordered
:not
(
.activeFromItem
)
>
div
:first-child
{
border
:
1px
dashed
#ccc
;
}
.el-form-item
{
padding
:
12px
10px
;
}
}
.drawing-row-item
{
position
:
relative
;
cursor
:
move
;
box-sizing
:
border-box
;
border
:
1px
dashed
#ccc
;
border-radius
:
3px
;
padding
:
0
2px
;
margin-bottom
:
15px
;
.drawing-row-item
{
margin-bottom
:
2px
;
}
.el-col
{
margin-top
:
22px
;
}
.el-form-item
{
margin-bottom
:
0
;
}
.drag-wrapper
{
min-height
:
80px
;
}
&
.active-from-item
{
border
:
1px
dashed
$lighterBlue
;
}
.component-name
{
position
:
absolute
;
top
:
0
;
left
:
0
;
font-size
:
12px
;
color
:
#bbb
;
display
:
inline-block
;
padding
:
0
6px
;
}
}
.drawing-item
,
.drawing-row-item
{
&
:hover
{
&
>
.el-form-item
{
background
:
$selectedColor
;
border-radius
:
6px
;
}
&
>
.drawing-item-copy
,
&
>
.drawing-item-delete
{
display
:
initial
;
}
}
&
>
.drawing-item-copy
,
&
>
.drawing-item-delete
{
display
:
none
;
position
:
absolute
;
top
:
-10px
;
width
:
22px
;
height
:
22px
;
line-height
:
22px
;
text-align
:
center
;
border-radius
:
50%
;
font-size
:
12px
;
border
:
1px
solid
;
cursor
:
pointer
;
z-index
:
1
;
}
&
>
.drawing-item-copy
{
right
:
56px
;
border-color
:
$lighterBlue
;
color
:
$lighterBlue
;
background
:
#fff
;
&
:hover
{
background
:
$lighterBlue
;
color
:
#fff
;
}
}
&
>
.drawing-item-delete
{
right
:
24px
;
border-color
:
#F56C6C
;
color
:
#F56C6C
;
background
:
#fff
;
&
:hover
{
background
:
#F56C6C
;
color
:
#fff
;
}
}
}
</
style
>
<
template
>
<div
class=
"container"
>
<div
class=
"left-board"
>
<div
class=
"logo-wrapper"
>
<div
class=
"logo"
>
<img
:src=
"logo"
alt=
"logo"
>
Form Generator
</div>
</div>
<el-scrollbar
class=
"left-scrollbar"
>
<div
class=
"components-list"
>
<div
class=
"components-title"
>
<svg-icon
icon-class=
"component"
/>
输入型组件
</div>
<draggable
class=
"components-draggable"
:list=
"inputComponents"
:group=
"
{ name: 'componentsGroup', pull: 'clone', put: false }"
:clone="cloneComponent"
draggable=".components-item"
:sort="false"
@end="onEnd"
>
<div
v-for=
"(element, index) in inputComponents"
:key=
"index"
class=
"components-item"
@
click=
"addComponent(element)"
>
<div
class=
"components-body"
>
<svg-icon
:icon-class=
"element.tagIcon"
/>
{{
element
.
label
}}
</div>
</div>
</draggable>
<div
class=
"components-title"
>
<svg-icon
icon-class=
"component"
/>
选择型组件
</div>
<draggable
class=
"components-draggable"
:list=
"selectComponents"
:group=
"
{ name: 'componentsGroup', pull: 'clone', put: false }"
:clone="cloneComponent"
draggable=".components-item"
:sort="false"
@end="onEnd"
>
<div
v-for=
"(element, index) in selectComponents"
:key=
"index"
class=
"components-item"
@
click=
"addComponent(element)"
>
<div
class=
"components-body"
>
<svg-icon
:icon-class=
"element.tagIcon"
/>
{{
element
.
label
}}
</div>
</div>
</draggable>
<div
class=
"components-title"
>
<svg-icon
icon-class=
"component"
/>
布局型组件
</div>
<draggable
class=
"components-draggable"
:list=
"layoutComponents"
:group=
"
{ name: 'componentsGroup', pull: 'clone', put: false }" :clone="cloneComponent"
draggable=".components-item" :sort="false" @end="onEnd"
>
<div
v-for=
"(element, index) in layoutComponents"
:key=
"index"
class=
"components-item"
@
click=
"addComponent(element)"
>
<div
class=
"components-body"
>
<svg-icon
:icon-class=
"element.tagIcon"
/>
{{
element
.
label
}}
</div>
</div>
</draggable>
</div>
</el-scrollbar>
</div>
<div
class=
"center-board"
>
<div
class=
"action-bar"
>
<el-button
icon=
"el-icon-download"
type=
"text"
@
click=
"download"
>
导出vue文件
</el-button>
<el-button
class=
"copy-btn-main"
icon=
"el-icon-document-copy"
type=
"text"
@
click=
"copy"
>
复制代码
</el-button>
<el-button
class=
"delete-btn"
icon=
"el-icon-delete"
type=
"text"
@
click=
"empty"
>
清空
</el-button>
</div>
<el-scrollbar
class=
"center-scrollbar"
>
<el-row
class=
"center-board-row"
:gutter=
"formConf.gutter"
>
<el-form
:size=
"formConf.size"
:label-position=
"formConf.labelPosition"
:disabled=
"formConf.disabled"
:label-width=
"formConf.labelWidth + 'px'"
>
<draggable
class=
"drawing-board"
:list=
"drawingList"
:animation=
"340"
group=
"componentsGroup"
>
<draggable-item
v-for=
"(element, index) in drawingList"
:key=
"element.renderKey"
:drawing-list=
"drawingList"
:element=
"element"
:index=
"index"
:active-id=
"activeId"
:form-conf=
"formConf"
@
activeItem=
"activeFormItem"
@
copyItem=
"drawingItemCopy"
@
deleteItem=
"drawingItemDelete"
/>
</draggable>
<div
v-show=
"!drawingList.length"
class=
"empty-info"
>
从左侧拖入或点选组件进行表单设计
</div>
</el-form>
</el-row>
</el-scrollbar>
</div>
<right-panel
:active-data=
"activeData"
:form-conf=
"formConf"
:show-field=
"!!drawingList.length"
@
tag-change=
"tagChange"
/>
<code-type-dialog
:visible.sync=
"dialogVisible"
title=
"选择生成类型"
:show-file-name=
"showFileName"
@
confirm=
"generate"
/>
<input
id=
"copyNode"
type=
"hidden"
>
</div>
</
template
>
<
script
>
import
draggable
from
'
vuedraggable
'
import
beautifier
from
'
js-beautify
'
import
ClipboardJS
from
'
clipboard
'
import
render
from
'
@/utils/generator/render
'
import
RightPanel
from
'
./RightPanel
'
import
{
inputComponents
,
selectComponents
,
layoutComponents
,
formConf
}
from
'
@/utils/generator/config
'
import
{
beautifierConf
,
titleCase
}
from
'
@/utils/index
'
import
{
makeUpHtml
,
vueTemplate
,
vueScript
,
cssStyle
}
from
'
@/utils/generator/html
'
import
{
makeUpJs
}
from
'
@/utils/generator/js
'
import
{
makeUpCss
}
from
'
@/utils/generator/css
'
import
drawingDefau
l
t
from
'
@/utils/generator/drawingDefau
l
t
'
import
logo
from
'
@/assets/logo/logo.png
'
import
CodeTypeDialog
from
'
./CodeTypeDialog
'
import
DraggableItem
from
'
./DraggableItem
'
let
oldActiveId
let
tempActiveData
export
default
{
components
:
{
draggable
,
render
,
RightPanel
,
CodeTypeDialog
,
DraggableItem
},
data
()
{
return
{
logo
,
idGlobal
:
100
,
formConf
,
inputComponents
,
selectComponents
,
layoutComponents
,
labelWidth
:
100
,
drawingList
:
drawingDefau
l
t
,
drawingData
:
{},
activeId
:
drawingDefau
l
t
[
0
].
formId
,
drawerVisible
:
false
,
formData
:
{},
dialogVisible
:
false
,
generateConf
:
null
,
showFileName
:
false
,
activeData
:
drawingDefau
l
t
[
0
]
}
},
created
()
{
// 防止 firefox 下 拖拽 会新打卡一个选项卡
document
.
body
.
ondrop
=
event
=>
{
event
.
preventDefault
()
event
.
stopPropagation
()
}
},
watch
:
{
// eslint-disable-next-line func-names
'
activeData.label
'
:
function
(
val
,
oldVal
)
{
if
(
this
.
activeData
.
placeholder
===
undefined
||
!
this
.
activeData
.
tag
||
oldActiveId
!==
this
.
activeId
)
{
return
}
this
.
activeData
.
placeholder
=
this
.
activeData
.
placeholder
.
replace
(
oldVal
,
''
)
+
val
},
activeId
:
{
handler
(
val
)
{
oldActiveId
=
val
},
immediate
:
true
}
},
mounted
()
{
const
clipboard
=
new
ClipboardJS
(
'
#copyNode
'
,
{
text
:
trigger
=>
{
const
codeStr
=
this
.
generateCode
()
this
.
$notify
({
title
:
'
成功
'
,
message
:
'
代码已复制到剪切板,可粘贴。
'
,
type
:
'
success
'
})
return
codeStr
}
})
clipboard
.
on
(
'
error
'
,
e
=>
{
this
.
$message
.
error
(
'
代码复制失败
'
)
})
},
methods
:
{
activeFormItem
(
element
)
{
this
.
activeData
=
element
this
.
activeId
=
element
.
formId
},
onEnd
(
obj
,
a
)
{
if
(
obj
.
from
!==
obj
.
to
)
{
this
.
activeData
=
tempActiveData
this
.
activeId
=
this
.
idGlobal
}
},
addComponent
(
item
)
{
const
clone
=
this
.
cloneComponent
(
item
)
this
.
drawingList
.
push
(
clone
)
this
.
activeFormItem
(
clone
)
},
cloneComponent
(
origin
)
{
const
clone
=
JSON
.
parse
(
JSON
.
stringify
(
origin
))
clone
.
formId
=
++
this
.
idGlobal
clone
.
span
=
formConf
.
span
clone
.
renderKey
=
+
new
Date
()
// 改变renderKey后可以实现强制更新组件
if
(
!
clone
.
layout
)
clone
.
layout
=
'
colFormItem
'
if
(
clone
.
layout
===
'
colFormItem
'
)
{
clone
.
vModel
=
`field
${
this
.
idGlobal
}
`
clone
.
placeholder
!==
undefined
&&
(
clone
.
placeholder
+=
clone
.
label
)
tempActiveData
=
clone
}
else
if
(
clone
.
layout
===
'
rowFormItem
'
)
{
delete
clone
.
label
clone
.
componentName
=
`row
${
this
.
idGlobal
}
`
clone
.
gutter
=
this
.
formConf
.
gutter
tempActiveData
=
clone
}
return
tempActiveData
},
AssembleFormData
()
{
this
.
formData
=
{
fields
:
JSON
.
parse
(
JSON
.
stringify
(
this
.
drawingList
)),
...
this
.
formConf
}
},
generate
(
data
)
{
const
func
=
this
[
`exec
${
titleCase
(
this
.
operationType
)}
`
]
this
.
generateConf
=
data
func
&&
func
(
data
)
},
execRun
(
data
)
{
this
.
AssembleFormData
()
this
.
drawerVisible
=
true
},
execDownload
(
data
)
{
const
codeStr
=
this
.
generateCode
()
const
blob
=
new
Blob
([
codeStr
],
{
type
:
'
text/plain;charset=utf-8
'
})
this
.
$download
.
saveAs
(
blob
,
data
.
fileName
)
},
execCopy
(
data
)
{
document
.
getElementById
(
'
copyNode
'
).
click
()
},
empty
()
{
this
.
$confirm
(
'
确定要清空所有组件吗?
'
,
'
提示
'
,
{
type
:
'
warning
'
}).
then
(
()
=>
{
this
.
drawingList
=
[]
}
)
},
drawingItemCopy
(
item
,
parent
)
{
let
clone
=
JSON
.
parse
(
JSON
.
stringify
(
item
))
clone
=
this
.
createIdAndKey
(
clone
)
parent
.
push
(
clone
)
this
.
activeFormItem
(
clone
)
},
createIdAndKey
(
item
)
{
item
.
formId
=
++
this
.
idGlobal
item
.
renderKey
=
+
new
Date
()
if
(
item
.
layout
===
'
colFormItem
'
)
{
item
.
vModel
=
`field
${
this
.
idGlobal
}
`
}
else
if
(
item
.
layout
===
'
rowFormItem
'
)
{
item
.
componentName
=
`row
${
this
.
idGlobal
}
`
}
if
(
Array
.
isArray
(
item
.
children
))
{
item
.
children
=
item
.
children
.
map
(
childItem
=>
this
.
createIdAndKey
(
childItem
))
}
return
item
},
drawingItemDelete
(
index
,
parent
)
{
parent
.
splice
(
index
,
1
)
this
.
$nextTick
(()
=>
{
const
len
=
this
.
drawingList
.
length
if
(
len
)
{
this
.
activeFormItem
(
this
.
drawingList
[
len
-
1
])
}
})
},
generateCode
()
{
const
{
type
}
=
this
.
generateConf
this
.
AssembleFormData
()
const
script
=
vueScript
(
makeUpJs
(
this
.
formData
,
type
))
const
html
=
vueTemplate
(
makeUpHtml
(
this
.
formData
,
type
))
const
css
=
cssStyle
(
makeUpCss
(
this
.
formData
))
return
beautifier
.
html
(
html
+
script
+
css
,
beautifierConf
.
html
)
},
download
()
{
this
.
dialogVisible
=
true
this
.
showFileName
=
true
this
.
operationType
=
'
download
'
},
run
()
{
this
.
dialogVisible
=
true
this
.
showFileName
=
false
this
.
operationType
=
'
run
'
},
copy
()
{
this
.
dialogVisible
=
true
this
.
showFileName
=
false
this
.
operationType
=
'
copy
'
},
tagChange
(
newTag
)
{
newTag
=
this
.
cloneComponent
(
newTag
)
newTag
.
vModel
=
this
.
activeData
.
vModel
newTag
.
formId
=
this
.
activeId
newTag
.
span
=
this
.
activeData
.
span
delete
this
.
activeData
.
tag
delete
this
.
activeData
.
tagIcon
delete
this
.
activeData
.
document
Object
.
keys
(
newTag
).
forEach
(
key
=>
{
if
(
this
.
activeData
[
key
]
!==
undefined
&&
typeof
this
.
activeData
[
key
]
===
typeof
newTag
[
key
])
{
newTag
[
key
]
=
this
.
activeData
[
key
]
}
})
this
.
activeData
=
newTag
this
.
updateDrawingList
(
newTag
,
this
.
drawingList
)
},
updateDrawingList
(
newTag
,
list
)
{
const
index
=
list
.
findIndex
(
item
=>
item
.
formId
===
this
.
activeId
)
if
(
index
>
-
1
)
{
list
.
splice
(
index
,
1
,
newTag
)
}
else
{
list
.
forEach
(
item
=>
{
if
(
Array
.
isArray
(
item
.
children
))
this
.
updateDrawingList
(
newTag
,
item
.
children
)
})
}
}
}
}
</
script
>
<
style
lang=
'scss'
>
body
,
html
{
margin
:
0
;
padding
:
0
;
background
:
#fff
;
-moz-osx-font-smoothing
:
grayscale
;
-webkit-font-smoothing
:
antialiased
;
text-rendering
:
optimizeLegibility
;
font-family
:
-
apple-system
,
BlinkMacSystemFont
,
Segoe
UI
,
Helvetica
,
Arial
,
sans-serif
,
Apple
Color
Emoji
,
Segoe
UI
Emoji
;
}
input
,
textarea
{
font-family
:
-
apple-system
,
BlinkMacSystemFont
,
Segoe
UI
,
Helvetica
,
Arial
,
sans-serif
,
Apple
Color
Emoji
,
Segoe
UI
Emoji
;
}
.editor-tabs
{
background
:
#121315
;
.el-tabs__header
{
margin
:
0
;
border-bottom-color
:
#121315
;
.el-tabs__nav
{
border-color
:
#121315
;
}
}
.el-tabs__item
{
height
:
32px
;
line-height
:
32px
;
color
:
#888a8e
;
border-left
:
1px
solid
#121315
!
important
;
background
:
#363636
;
margin-right
:
5px
;
user-select
:
none
;
}
.el-tabs__item.is-active
{
background
:
#1e1e1e
;
border-bottom-color
:
#1e1e1e
!
important
;
color
:
#fff
;
}
.el-icon-edit
{
color
:
#f1fa8c
;
}
.el-icon-document
{
color
:
#a95812
;
}
}
// home
.right-scrollbar
{
.el-scrollbar__view
{
padding
:
12px
18px
15px
15px
;
}
}
.left-scrollbar
.el-scrollbar__wrap
{
box-sizing
:
border-box
;
overflow-x
:
hidden
!
important
;
margin-bottom
:
0
!
important
;
}
.center-tabs
{
.el-tabs__header
{
margin-bottom
:
0
!
important
;
}
.el-tabs__item
{
width
:
50%
;
text-align
:
center
;
}
.el-tabs__nav
{
width
:
100%
;
}
}
.reg-item
{
padding
:
12px
6px
;
background
:
#f8f8f8
;
position
:
relative
;
border-radius
:
4px
;
.close-btn
{
position
:
absolute
;
right
:
-6px
;
top
:
-6px
;
display
:
block
;
width
:
16px
;
height
:
16px
;
line-height
:
16px
;
background
:
rgba
(
0
,
0
,
0
,
0
.2
);
border-radius
:
50%
;
color
:
#fff
;
text-align
:
center
;
z-index
:
1
;
cursor
:
pointer
;
font-size
:
12px
;
&
:hover
{
background
:
rgba
(
210
,
23
,
23
,
0
.5
)
}
}
&
+
.reg-item
{
margin-top
:
18px
;
}
}
.action-bar
{
&
.el-button
+
.el-button
{
margin-left
:
15px
;
}
&
i
{
font-size
:
20px
;
vertical-align
:
middle
;
position
:
relative
;
top
:
-1px
;
}
}
.custom-tree-node
{
width
:
100%
;
font-size
:
14px
;
.node-operation
{
float
:
right
;
}
i
[
class
*=
"el-icon"
]
+
i
[
class
*=
"el-icon"
]
{
margin-left
:
6px
;
}
.el-icon-plus
{
color
:
#409EFF
;
}
.el-icon-delete
{
color
:
#157a0c
;
}
}
.left-scrollbar
.el-scrollbar__view
{
overflow-x
:
hidden
;
}
.el-rate
{
display
:
inline-block
;
vertical-align
:
text-top
;
}
.el-upload__tip
{
line-height
:
1
.2
;
}
$selectedColor
:
#f6f7ff
;
$lighterBlue
:
#409EFF
;
.container
{
position
:
relative
;
width
:
100%
;
height
:
100%
;
}
.components-list
{
padding
:
8px
;
box-sizing
:
border-box
;
height
:
100%
;
.components-item
{
display
:
inline-block
;
width
:
48%
;
margin
:
1%
;
transition
:
transform
0ms
!
important
;
}
}
.components-draggable
{
padding-bottom
:
20px
;
}
.components-title
{
font-size
:
14px
;
color
:
#222
;
margin
:
6px
2px
;
.svg-icon
{
color
:
#666
;
font-size
:
18px
;
}
}
.components-body
{
padding
:
8px
10px
;
background
:
$selectedColor
;
font-size
:
12px
;
cursor
:
move
;
border
:
1px
dashed
$selectedColor
;
border-radius
:
3px
;
.svg-icon
{
color
:
#777
;
font-size
:
15px
;
}
&
:hover
{
border
:
1px
dashed
#787be8
;
color
:
#787be8
;
.svg-icon
{
color
:
#787be8
;
}
}
}
.left-board
{
width
:
260px
;
position
:
absolute
;
left
:
0
;
top
:
0
;
height
:
100vh
;
}
.left-scrollbar
{
height
:
calc
(
100vh
-
42px
);
overflow
:
hidden
;
}
.center-scrollbar
{
height
:
calc
(
100vh
-
42px
);
overflow
:
hidden
;
border-left
:
1px
solid
#f1e8e8
;
border-right
:
1px
solid
#f1e8e8
;
box-sizing
:
border-box
;
}
.center-board
{
height
:
100vh
;
width
:
auto
;
margin
:
0
350px
0
260px
;
box-sizing
:
border-box
;
}
.empty-info
{
position
:
absolute
;
top
:
46%
;
left
:
0
;
right
:
0
;
text-align
:
center
;
font-size
:
18px
;
color
:
#ccb1ea
;
letter-spacing
:
4px
;
}
.action-bar
{
position
:
relative
;
height
:
42px
;
text-align
:
right
;
padding
:
0
15px
;
box-sizing
:
border-box
;;
border
:
1px
solid
#f1e8e8
;
border-top
:
none
;
border-left
:
none
;
.delete-btn
{
color
:
#F56C6C
;
}
}
.logo-wrapper
{
position
:
relative
;
height
:
42px
;
background
:
#fff
;
border-bottom
:
1px
solid
#f1e8e8
;
box-sizing
:
border-box
;
}
.logo
{
position
:
absolute
;
left
:
12px
;
top
:
6px
;
line-height
:
30px
;
color
:
#00afff
;
font-weight
:
600
;
font-size
:
17px
;
white-space
:
nowrap
;
>
img
{
width
:
30px
;
height
:
30px
;
vertical-align
:
top
;
}
.github
{
display
:
inline-block
;
vertical-align
:
sub
;
margin-left
:
15px
;
>
img
{
height
:
22px
;
}
}
}
.center-board-row
{
padding
:
12px
12px
15px
12px
;
box-sizing
:
border-box
;
&
>
.el-form
{
// 69 = 12+15+42
height
:
calc
(
100vh
-
69px
);
}
}
.drawing-board
{
height
:
100%
;
position
:
relative
;
.components-body
{
padding
:
0
;
margin
:
0
;
font-size
:
0
;
}
.sortable-ghost
{
position
:
relative
;
display
:
block
;
overflow
:
hidden
;
&
::before
{
content
:
" "
;
position
:
absolute
;
left
:
0
;
right
:
0
;
top
:
0
;
height
:
3px
;
background
:
rgb
(
89
,
89
,
223
);
z-index
:
2
;
}
}
.components-item.sortable-ghost
{
width
:
100%
;
height
:
60px
;
background-color
:
$selectedColor
;
}
.active-from-item
{
&
>
.el-form-item
{
background
:
$selectedColor
;
border-radius
:
6px
;
}
&
>
.drawing-item-copy
,
&
>
.drawing-item-delete
{
display
:
initial
;
}
&
>
.component-name
{
color
:
$lighterBlue
;
}
}
.el-form-item
{
margin-bottom
:
15px
;
}
}
.drawing-item
{
position
:
relative
;
cursor
:
move
;
&
.unfocus-bordered
:not
(
.activeFromItem
)
>
div
:first-child
{
border
:
1px
dashed
#ccc
;
}
.el-form-item
{
padding
:
12px
10px
;
}
}
.drawing-row-item
{
position
:
relative
;
cursor
:
move
;
box-sizing
:
border-box
;
border
:
1px
dashed
#ccc
;
border-radius
:
3px
;
padding
:
0
2px
;
margin-bottom
:
15px
;
.drawing-row-item
{
margin-bottom
:
2px
;
}
.el-col
{
margin-top
:
22px
;
}
.el-form-item
{
margin-bottom
:
0
;
}
.drag-wrapper
{
min-height
:
80px
;
}
&
.active-from-item
{
border
:
1px
dashed
$lighterBlue
;
}
.component-name
{
position
:
absolute
;
top
:
0
;
left
:
0
;
font-size
:
12px
;
color
:
#bbb
;
display
:
inline-block
;
padding
:
0
6px
;
}
}
.drawing-item
,
.drawing-row-item
{
&
:hover
{
&
>
.el-form-item
{
background
:
$selectedColor
;
border-radius
:
6px
;
}
&
>
.drawing-item-copy
,
&
>
.drawing-item-delete
{
display
:
initial
;
}
}
&
>
.drawing-item-copy
,
&
>
.drawing-item-delete
{
display
:
none
;
position
:
absolute
;
top
:
-10px
;
width
:
22px
;
height
:
22px
;
line-height
:
22px
;
text-align
:
center
;
border-radius
:
50%
;
font-size
:
12px
;
border
:
1px
solid
;
cursor
:
pointer
;
z-index
:
1
;
}
&
>
.drawing-item-copy
{
right
:
56px
;
border-color
:
$lighterBlue
;
color
:
$lighterBlue
;
background
:
#fff
;
&
:hover
{
background
:
$lighterBlue
;
color
:
#fff
;
}
}
&
>
.drawing-item-delete
{
right
:
24px
;
border-color
:
#F56C6C
;
color
:
#F56C6C
;
background
:
#fff
;
&
:hover
{
background
:
#F56C6C
;
color
:
#fff
;
}
}
}
</
style
>
Prev
1
2
Next
Write
Preview
Supports
Markdown
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