This commit is contained in:
parent
76e925b214
commit
d3ba90b84c
|
|
@ -4,60 +4,7 @@
|
|||
<el-main class="height-100">
|
||||
<el-form class="height-100" :label-position="config.labelPosition" :label-width="config.labelWidth"
|
||||
:size="config.size" :disabled="config.disabled">
|
||||
<draggable :list="fields" :scroll="true" animation="200" item-key="id" class="height-100"
|
||||
:group="group" @update:list="fields = $event" @add="addComp" @sort="sortComp"
|
||||
ghostClass="ghostClass">
|
||||
<template #item="{ element, index }">
|
||||
<div class="box">
|
||||
<el-form-item class="item" :class="{'active': curIndex===index}"
|
||||
@click="clickComp(element,index)" :label="fields[index].title"
|
||||
:required="fields[index].required">
|
||||
<component :is="getComponent(fields[index].name)" v-model="fields[index].value"
|
||||
v-bind="fields[index].props" :style="{width: fields[index].width}">
|
||||
<template v-if="fields[index].name === 'select'" #default>
|
||||
<el-option v-for="(op, idx) in fields[index].options"
|
||||
:key="idx" :label="op.label" :value="op.value"/>
|
||||
</template>
|
||||
<template v-else-if="fields[index].name === 'radio'" #default>
|
||||
<el-radio-button v-if="fields[index].style === 'button'"
|
||||
v-for="(op, idx) in fields[index].options"
|
||||
:key="idx" :label="op.label" :value="op.value"/>
|
||||
<el-radio v-else v-for="(op, id) in fields[index].options"
|
||||
:key="id" :label="op.label" :value="op.value"/>
|
||||
</template>
|
||||
<template v-else-if="fields[index].name === 'checkbox'" #default>
|
||||
<el-checkbox-button v-if="fields[index].style === 'button'"
|
||||
v-for="(op, idx) in fields[index].options"
|
||||
:key="idx" :label="op.label" :value="op.value"/>
|
||||
<el-checkbox v-else v-for="(op, id) in fields[index].options"
|
||||
:key="id" :label="op.label" :value="op.value"/>
|
||||
</template>
|
||||
<template v-if="fields[index].name === 'upload' && (fields[index].props.listType === 'text' || fields[index].props.listType === 'picture')" #trigger>
|
||||
<el-button type="primary" icon="pi-icon-upload-file">
|
||||
{{ fields[index].btnText }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template v-if="fields[index].name === 'upload' && fields[index].props.listType === 'picture-card'" #trigger>
|
||||
<el-icon><component :is="'el-icon-plus'"/></el-icon>
|
||||
</template>
|
||||
<template v-if="fields[index].name === 'upload' && fields[index].tip" #tip>
|
||||
<div class="el-upload__tip">
|
||||
{{ fields[index].tip }}
|
||||
</div>
|
||||
</template>
|
||||
</component>
|
||||
</el-form-item>
|
||||
<div v-if="curIndex===index" class="tools-box">
|
||||
<el-icon size="22" class="icon-box remove" @click="delComp(index)">
|
||||
<component :is="'pi-icon-shan-chu'"/>
|
||||
</el-icon>
|
||||
<el-icon size="20" class="icon-box copy ml5" @click="copyComp(index)">
|
||||
<component :is="'pi-icon-fu-zhi'"/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
<drag :fields="fields" @change="changeHandle" name="page"/>
|
||||
</el-form>
|
||||
</el-main>
|
||||
</el-scrollbar>
|
||||
|
|
@ -65,13 +12,7 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import draggable from 'vuedraggable'
|
||||
import {nextTick, ref} from "vue";
|
||||
import tools from "@/utils/tools"
|
||||
import {getComponent} from "./config"
|
||||
import piIcon from "@/components/piIcon"
|
||||
import PiAsset from "@/components/piAsset"
|
||||
|
||||
import drag from "./drag.vue"
|
||||
defineExpose({clickAddComp})
|
||||
|
||||
const props = defineProps({
|
||||
|
|
@ -79,76 +20,15 @@ const props = defineProps({
|
|||
config: {type: Object, default: {}}
|
||||
})
|
||||
const emit = defineEmits(['change'])
|
||||
const group = {name: 'page', pull: false, put: true}
|
||||
let curIndex = ref(0)
|
||||
let curComp = ref({})
|
||||
|
||||
// 排序
|
||||
function sortComp(e) {
|
||||
curIndex.value = e.newIndex
|
||||
curComp.value = props.fields[e.newIndex]
|
||||
emit('change', curComp.value)
|
||||
}
|
||||
|
||||
// 添加项时
|
||||
function addComp(e) {
|
||||
let element = props.fields[e.newIndex]
|
||||
curIndex.value = e.newIndex
|
||||
curComp.value = element
|
||||
emit('change', curComp.value)
|
||||
}
|
||||
|
||||
// 添加项时
|
||||
function clickAddComp(e) {
|
||||
props.fields.push(e)
|
||||
curIndex.value = props.fields.length-1
|
||||
curComp.value = e
|
||||
emit('change', curComp.value)
|
||||
emit('change', e)
|
||||
}
|
||||
|
||||
// 选中项
|
||||
function clickComp(element, index) {
|
||||
if (curIndex.value === index) {
|
||||
return;
|
||||
}
|
||||
curIndex.value = index
|
||||
curComp.value = element
|
||||
emit('change', curComp.value)
|
||||
}
|
||||
|
||||
// 删除字段
|
||||
function delComp(index) {
|
||||
nextTick(() => {
|
||||
// 原数组长度
|
||||
const len = props.fields.length
|
||||
props.fields.splice(index, 1);
|
||||
let i = -1;
|
||||
if (len > 1) {
|
||||
if (index === 0) {
|
||||
i = 0;
|
||||
} else if (index === len - 1) {
|
||||
i = index - 1
|
||||
} else {
|
||||
i = index
|
||||
}
|
||||
}
|
||||
curIndex.value = i
|
||||
curComp.value = props.fields[i]
|
||||
emit('change', curComp.value || {})
|
||||
})
|
||||
}
|
||||
|
||||
function copyComp(index) {
|
||||
nextTick(() => {
|
||||
// 复制当前元素到尾部
|
||||
const tmp = JSON.parse(JSON.stringify(props.fields[index]))
|
||||
props.fields.push(tmp)
|
||||
// 将复制的元素移动到当前元素下边
|
||||
tools.array.zIndexTo(props.fields, props.fields.length - 1, index + 1)
|
||||
curIndex.value = index + 1
|
||||
curComp.value = props.fields[index + 1]
|
||||
emit('change', curComp.value)
|
||||
})
|
||||
function changeHandle(e) {
|
||||
emit('change', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -161,18 +41,6 @@ function copyComp(index) {
|
|||
display: none;
|
||||
}
|
||||
|
||||
:deep(.ghostClass) {
|
||||
.comp {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tips {
|
||||
padding: 10px;
|
||||
border: 1px dashed var(--el-color-primary);
|
||||
text-align: center;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.height-100 {
|
||||
height: 100%;
|
||||
|
|
@ -182,50 +50,4 @@ function copyComp(index) {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.active {
|
||||
border: 1px dashed #787be8 !important;
|
||||
color: #787be8;
|
||||
}
|
||||
|
||||
.box {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
.item {
|
||||
padding: 12px 10px;
|
||||
border: 1px;
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tools-box {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: -16px;
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
border-radius: 4px;
|
||||
|
||||
.icon-box {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-box.remove {
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
|
||||
.icon-box.copy {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.ml5 {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
cursor: no-drop;
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -186,6 +186,25 @@ export const fieldEditors = {
|
|||
{key: 'props.clearable', label: '能否清空', type: 'switch'},
|
||||
{key: 'props.readonly', label: '是否只读', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
button: [
|
||||
{key: 'btnText', label: '按钮文字', type: 'input'},
|
||||
{
|
||||
key: 'props.type', label: '按钮类型', type: 'select', options: [
|
||||
{label: 'primary', value: 'primary'},
|
||||
{label: 'success', value: 'success'},
|
||||
{label: 'warning', value: 'warning'},
|
||||
{label: 'danger', value: 'danger'},
|
||||
{label: 'info', value: 'info'},
|
||||
{label: 'default', value: ''},
|
||||
]
|
||||
},
|
||||
{key: 'props.icon', label: '图标', type: 'icon'},
|
||||
{key: 'props.plain', label: '朴素按钮', type: 'switch'},
|
||||
{key: 'props.text', label: '文字按钮', type: 'switch'},
|
||||
{key: 'props.round', label: '圆角按钮', type: 'switch'},
|
||||
{key: 'props.circle', label: '圆形按钮', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
]
|
||||
}
|
||||
// 'year' | 'years' |'month' | 'months' | 'date' | 'dates' | 'datetime' | 'week' | 'datetimerange' | 'daterange' | 'monthrange' | 'yearrange'
|
||||
|
|
@ -208,8 +227,7 @@ export const getComponent = (type) => {
|
|||
daterange: 'el-date-picker',
|
||||
time: 'el-time-picker',
|
||||
timerange: 'el-time-picker',
|
||||
icon: 'pi-icon',
|
||||
asset: 'pi-asset'
|
||||
button: 'el-button'
|
||||
}
|
||||
return map[type] || 'div'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,187 @@
|
|||
<template>
|
||||
<draggable :list="fields" :scroll="true" animation="200" item-key="id" class="height-100"
|
||||
:group="group" @update:list="fields = $event" @add="addComp" @sort="sortComp"
|
||||
ghostClass="ghostClass">
|
||||
<template #item="{ element, index }">
|
||||
<div class="box">
|
||||
<el-row v-if="element.name === 'layout'" class="item row" :class="{'active': curIndex===index}"
|
||||
@click="clickComp(element, index)">
|
||||
<span class="name">{{ element.field }}</span>
|
||||
<drag :fields="element.children" @change="changeHandle" :name="element.field"/>
|
||||
</el-row>
|
||||
<item v-else class="item" :class="{'active': curIndex===index}" :field="element"
|
||||
@click="clickComp(element,index)"/>
|
||||
<div v-if="curIndex===index" class="tools-box">
|
||||
<el-icon size="22" class="icon-box remove" @click="delComp(index)">
|
||||
<component :is="'pi-icon-shan-chu'"/>
|
||||
</el-icon>
|
||||
<el-icon size="20" class="icon-box copy ml5" @click="copyComp(index)">
|
||||
<component :is="'pi-icon-fu-zhi'"/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import draggable from 'vuedraggable'
|
||||
import {nextTick, ref} from "vue"
|
||||
import tools from "@/utils/tools"
|
||||
import item from "./item.vue"
|
||||
import drag from "./drag.vue"
|
||||
|
||||
|
||||
console.log(drag)
|
||||
|
||||
const props = defineProps({
|
||||
fields: {type: Array, default: []},
|
||||
name: {type: String, default: 'page'}
|
||||
})
|
||||
const emit = defineEmits(['change'])
|
||||
const group = {name: props.name, pull: false, put: true}
|
||||
let curIndex = ref(0)
|
||||
let curComp = ref({})
|
||||
|
||||
// 排序
|
||||
function sortComp(e) {
|
||||
curIndex.value = e.newIndex
|
||||
curComp.value = props.fields[e.newIndex]
|
||||
emit('change', curComp.value)
|
||||
}
|
||||
|
||||
// 添加项时
|
||||
function addComp(e) {
|
||||
let element = props.fields[e.newIndex]
|
||||
curIndex.value = e.newIndex
|
||||
curComp.value = element
|
||||
emit('change', curComp.value)
|
||||
}
|
||||
|
||||
// 选中项
|
||||
function clickComp(element, index) {
|
||||
if (curIndex.value === index) {
|
||||
return;
|
||||
}
|
||||
curIndex.value = index
|
||||
curComp.value = element
|
||||
emit('change', curComp.value)
|
||||
}
|
||||
|
||||
// 删除字段
|
||||
function delComp(index) {
|
||||
nextTick(() => {
|
||||
// 原数组长度
|
||||
const len = props.fields.length
|
||||
props.fields.splice(index, 1);
|
||||
let i = -1;
|
||||
if (len > 1) {
|
||||
if (index === 0) {
|
||||
i = 0;
|
||||
} else if (index === len - 1) {
|
||||
i = index - 1
|
||||
} else {
|
||||
i = index
|
||||
}
|
||||
}
|
||||
curIndex.value = i
|
||||
curComp.value = props.fields[i]
|
||||
emit('change', curComp.value || {})
|
||||
})
|
||||
}
|
||||
|
||||
function copyComp(index) {
|
||||
nextTick(() => {
|
||||
// 复制当前元素到尾部
|
||||
const tmp = JSON.parse(JSON.stringify(props.fields[index]))
|
||||
props.fields.push(tmp)
|
||||
// 将复制的元素移动到当前元素下边
|
||||
tools.array.zIndexTo(props.fields, props.fields.length - 1, index + 1)
|
||||
curIndex.value = index + 1
|
||||
curComp.value = props.fields[index + 1]
|
||||
emit('change', curComp.value)
|
||||
})
|
||||
}
|
||||
|
||||
function changeHandle(e) {
|
||||
emit('change', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.ghostClass) {
|
||||
.comp {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tips {
|
||||
padding: 10px;
|
||||
border: 1px dashed var(--el-color-primary);
|
||||
text-align: center;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
border: 1px dashed #787be8 !important;
|
||||
color: #787be8;
|
||||
}
|
||||
|
||||
.box {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
.item {
|
||||
padding: 12px 10px;
|
||||
border: 1px;
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.row {
|
||||
min-height: 80px;
|
||||
border: 1px dashed #c1c1c1;
|
||||
margin-bottom: 18px;
|
||||
|
||||
.name {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
font-size: 12px;
|
||||
color: #bbb;
|
||||
display: inline-block;
|
||||
padding: 0 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.tools-box {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: -16px;
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
border-radius: 4px;
|
||||
|
||||
.icon-box {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon-box.remove {
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
|
||||
.icon-box.copy {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.ml5 {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
cursor: no-drop;
|
||||
color: #c3c3c3;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
<template>
|
||||
<el-form-item :label="field.title" :required="field.required">
|
||||
<component :is="getComponent(field.name)" v-model="field.value" v-bind="field.props"
|
||||
:style="{width: field.width}">
|
||||
<template v-if="field.name === 'select'" #default>
|
||||
<el-option v-for="(op, idx) in field.options" :key="idx" :label="op.label" :value="op.value"/>
|
||||
</template>
|
||||
<template v-else-if="field.name === 'radio'" #default>
|
||||
<el-radio-button v-if="field.style === 'button'" v-for="(op, idx) in field.options" :key="idx"
|
||||
:label="op.label" :value="op.value"/>
|
||||
<el-radio v-else v-for="(op, id) in field.options" :key="id" :label="op.label" :value="op.value"/>
|
||||
</template>
|
||||
<template v-else-if="field.name === 'checkbox'" #default>
|
||||
<el-checkbox-button v-if="field.style === 'button'" v-for="(op, idx) in field.options" :key="idx"
|
||||
:label="op.label" :value="op.value"/>
|
||||
<el-checkbox v-else v-for="(op, id) in field.options" :key="id" :label="op.label" :value="op.value"/>
|
||||
</template>
|
||||
<template
|
||||
v-if="field.name === 'upload' && (field.props.listType === 'text' || field.props.listType === 'picture')"
|
||||
#trigger>
|
||||
<el-button type="primary" icon="pi-icon-upload-file">
|
||||
{{ field.btnText }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template
|
||||
v-if="field.name === 'upload' && field.props.listType === 'picture-card'" #trigger>
|
||||
<el-icon>
|
||||
<component :is="'el-icon-plus'"/>
|
||||
</el-icon>
|
||||
</template>
|
||||
<template v-if="field.name === 'upload' && field.tip" #tip>
|
||||
<div class="el-upload__tip">
|
||||
{{ field.tip }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="field.name === 'button' && field.btnText"
|
||||
#default>
|
||||
{{ field.btnText }}
|
||||
</template>
|
||||
</component>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {getComponent} from "./config"
|
||||
|
||||
const props = defineProps({
|
||||
field: {type: Object, default: {}}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
@ -266,19 +266,23 @@ const choiceComps = [{
|
|||
btnText: "点击上传"
|
||||
}]
|
||||
const layoutComps = [{
|
||||
id: 'l1',
|
||||
id: 'row',
|
||||
title: "行容器",
|
||||
icon: "pi-icon-row-layout",
|
||||
name: "layout",
|
||||
props: {},
|
||||
rules: []
|
||||
props: {
|
||||
gutter: '15'
|
||||
},
|
||||
children: []
|
||||
}, {
|
||||
id: 'l2',
|
||||
title: "按钮",
|
||||
icon: "pi-icon-button",
|
||||
name: "button",
|
||||
props: {},
|
||||
rules: []
|
||||
props: {
|
||||
type: 'primary'
|
||||
},
|
||||
btnText: "按钮文字"
|
||||
}];
|
||||
let num = ref(100)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@
|
|||
<el-tabs v-model="activeName" stretch>
|
||||
<el-tab-pane label="组件属性" name="field">
|
||||
<el-form v-model="fForm" label-width="90px">
|
||||
<el-form-item label="字段名" prop="field">
|
||||
<el-form-item v-if="fForm.name !== 'button'" label="字段名" prop="field">
|
||||
<el-input v-model="fForm.field"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input v-model="fForm.title"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="默认值" prop="value">
|
||||
<el-form-item v-if="fForm.name !== 'button'" label="默认值" prop="value">
|
||||
<el-input v-model="fForm.value"></el-input>
|
||||
</el-form-item>
|
||||
<template v-for="item in fieldEditors[fForm.name] || []" :key="item.key">
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
v-if="item.type === 'input'"
|
||||
:model-value="getByPath(fForm, item.key)"
|
||||
@input="val => setByPath(fForm, item.key, val)"
|
||||
clearable
|
||||
/>
|
||||
<el-input
|
||||
v-else-if="item.type === 'number'"
|
||||
|
|
|
|||
Loading…
Reference in New Issue