表单构建
This commit is contained in:
parent
8f85550572
commit
f06cfbfda6
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<svg t="1764050396290" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="32274" width="256" height="256"><path d="M512 34.1C247.5 34.1 34.1 247.5 34.1 512S247.4 989.9 512 989.9 989.9 776.6 989.9 512 776.5 34.1 512 34.1zM346.5 397.6v216.8h-46.1c-10.2 0-17.1-8.5-17.1-17.1V298.7c0-10.2 8.5-17.1 17.1-17.1h296.9c10.2 0 17.1 8.5 17.1 17.1v46.1H397.7c-29 1.7-51.2 23.9-51.2 52.9z m395.9 327.7c0 10.2-8.5 17.1-17.1 17.1H428.4c-10.2 0-17.1-8.5-17.1-17.1V428.4c0-10.2 8.5-17.1 17.1-17.1h297c10.2 0 17.1 8.5 17.1 17.1v297z" p-id="32275"></path></svg>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<svg t="1764049809865" class="icon" viewBox="0 0 1066 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5898" width="256" height="256"><path d="M533.333333 183.466667a328.533333 328.533333 0 1 0 0 657.066666 328.533333 328.533333 0 0 0 0-657.066666zM136.533333 512a396.8 396.8 0 1 1 793.6 0 396.8 396.8 0 0 1-793.6 0z" p-id="5899"></path><path d="M629.888 656.810667L388.522667 415.445333l48.298666-48.256 241.322667 241.365334-48.256 48.256z" p-id="5900"></path><path d="M388.522667 608.554667l241.322666-241.365334 48.298667 48.256-241.365333 241.365334-48.256-48.256z" p-id="5901"></path></svg>
|
||||
</template>
|
||||
|
|
@ -161,7 +161,59 @@ const tools = {
|
|||
} else {
|
||||
return '未知'
|
||||
}
|
||||
},
|
||||
// 数组0 - index
|
||||
array: {
|
||||
// 交换
|
||||
swap(arr: Array<any>, index1: number, index2: number) {
|
||||
arr[index1] = arr.splice(index2, 1, arr[index1])[0];
|
||||
return arr;
|
||||
},
|
||||
zIndexUp(arr, index) {
|
||||
if (index !== 0) {
|
||||
this.swap(arr, index, index - 1);
|
||||
}
|
||||
},
|
||||
zIndexDown(arr, index) {
|
||||
if (index + 1 < arr.length) {
|
||||
this.swap(arr, index, index + 1);
|
||||
}
|
||||
},
|
||||
zIndexTop(arr, index) {
|
||||
if (index > 0) {
|
||||
const moveNum = index - 0;
|
||||
for (let i = 0; i < moveNum; i++) {
|
||||
this.swap(arr, index, index - 1);
|
||||
index--;
|
||||
}
|
||||
}
|
||||
},
|
||||
zIndexBottom(arr, index) {
|
||||
if (index + 1 < arr.length) {
|
||||
const moveNum = arr.length - 1 - index;
|
||||
for (let i = 0; i < moveNum; i++) {
|
||||
this.swap(arr, index, index + 1);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
},
|
||||
// index1 >>> index2
|
||||
zIndexTo(arr, index1, index2) {
|
||||
if (index1 === index2) return;
|
||||
if (index1 < index2) {
|
||||
const moveNum = index2 - 1 - index1;
|
||||
for (let i = 0; i < moveNum; i++) {
|
||||
this.swap(arr, index1, index1 + 1);
|
||||
index1++;
|
||||
}
|
||||
} else {
|
||||
const moveNum = index1 - index2;
|
||||
for (let i = 0; i < moveNum; i++) {
|
||||
this.swap(arr, index1, index1 - 1);
|
||||
index1--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default tools
|
||||
|
|
|
|||
|
|
@ -1,32 +1,30 @@
|
|||
<template>
|
||||
<section class="panel">
|
||||
<el-main>
|
||||
<draggable v-model="data" :scroll="true" animation="200" item-key="id" class="go-page-body"
|
||||
:group="group"
|
||||
@add="addComp" @sort="sortComp" ghostClass="ghostClass">
|
||||
<el-main style="height: 100%;">
|
||||
<draggable :list="fields" @update:list="fields = $event" :scroll="true" animation="200" item-key="id"
|
||||
class="body"
|
||||
:group="group" @add="addComp" @sort="sortComp" ghostClass="ghostClass">
|
||||
<template #item="{ element, index }">
|
||||
<div class="comp-box">
|
||||
<div class="comp-item" :class="{'active': curIndex==index}" @click="clickComp(element,index)">
|
||||
<component :is="allComps[element.name]" v-model="value.components[index]"></component>
|
||||
</div>
|
||||
<div v-if="curIndex==index" class="tools-box">
|
||||
<el-icon size="18" class="icon-box" @click="delComp(index)">
|
||||
<component :is="'sc-icon-Delete'"/>
|
||||
<form class="box">
|
||||
<el-form-item class="item" :class="{'active': curIndex===index}"
|
||||
@click="clickComp(element,index)" :label="fields[index].title">
|
||||
<el-input v-if="fields[index].name==='text'" v-model="fields[index].value"></el-input>
|
||||
<el-input v-if="fields[index].name==='textarea'" type="textarea"
|
||||
v-model="fields[index].value"></el-input>
|
||||
<el-input v-if="fields[index].name==='password'" type="password"
|
||||
v-model="fields[index].value" show-password></el-input>
|
||||
<el-input-number v-if="fields[index].name==='number'"
|
||||
v-model="fields[index].value"></el-input-number>
|
||||
</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="18" class="icon-box mt15" @click="copyComp(index)">
|
||||
<component :is="'sc-icon-copy'"/>
|
||||
</el-icon>
|
||||
<el-icon size="18" class="icon-box mt15" :class="{disabled: index == 0}"
|
||||
@click="moveUpComp(index)">
|
||||
<component :is="'el-icon-ArrowUpBold'"/>
|
||||
</el-icon>
|
||||
<el-icon size="18" class="icon-box mt15"
|
||||
:class="{disabled: index == value.components.length-1}"
|
||||
@click="moveDownComp(index)">
|
||||
<component :is="'el-icon-ArrowDownBold'"/>
|
||||
<el-icon size="20" class="icon-box copy mt5" @click="copyComp(index)">
|
||||
<component :is="'pi-icon-fu-zhi'"/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
</draggable>
|
||||
</el-main>
|
||||
|
|
@ -34,11 +32,15 @@
|
|||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import draggable from 'vuedraggable'
|
||||
import {nextTick, ref} from "vue";
|
||||
import tools from "@/utils/tools"
|
||||
|
||||
const emit = defineEmits(["change"])
|
||||
const props = defineProps({
|
||||
fields: {type: Array, default: []},
|
||||
config: {type: Object, default: {}}
|
||||
})
|
||||
|
||||
let data = ref({})
|
||||
const group = {name: 'page', pull: false, put: true}
|
||||
let curIndex = ref(0)
|
||||
let curComp = ref({})
|
||||
|
|
@ -46,32 +48,77 @@ let curComp = ref({})
|
|||
// 排序
|
||||
function sortComp(e) {
|
||||
curIndex.value = e.newIndex
|
||||
curComp.value = data.value.components[e.newIndex]
|
||||
curComp.value = props.fields[e.newIndex]
|
||||
}
|
||||
|
||||
// 添加项
|
||||
// 添加项时
|
||||
function addComp(e) {
|
||||
let element = data.value.components[e.newIndex]
|
||||
// 删除提示
|
||||
let element = props.fields[e.newIndex]
|
||||
curIndex.value = e.newIndex
|
||||
curComp.value = element
|
||||
emit('change', curComp.value, curIndex.value, data.value)
|
||||
}
|
||||
|
||||
// 选中项
|
||||
function clickComp(element, index) {
|
||||
if (curIndex.value === index) {
|
||||
return;
|
||||
}
|
||||
curIndex.value = index
|
||||
curComp.value = element
|
||||
}
|
||||
|
||||
// 删除字段
|
||||
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]
|
||||
})
|
||||
}
|
||||
|
||||
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]
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.panel {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:deep(.ghostClass) {
|
||||
.comp {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tips {
|
||||
padding: 10px;
|
||||
border: 1px dashed var(--el-color-primary);
|
||||
|
|
@ -79,31 +126,45 @@ function addComp(e) {
|
|||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.active {
|
||||
border: 2px solid #1890ff;
|
||||
box-shadow: var(--el-box-shadow-light);
|
||||
border: 1px dashed #787be8 !important;
|
||||
color: #787be8;
|
||||
}
|
||||
.comp-box {
|
||||
|
||||
.box {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
.comp-item {
|
||||
|
||||
.item {
|
||||
padding: 12px 10px;
|
||||
background: #f6f7ff;
|
||||
border: 1px dashed #f6f7ff;
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tools-box {
|
||||
position: absolute;
|
||||
right: -42px;
|
||||
right: -25px;
|
||||
top: 0;
|
||||
background: var(--el-color-primary);
|
||||
display: inline-grid;
|
||||
padding: 15px 8px;
|
||||
padding: 5px 8px;
|
||||
border-radius: 4px;
|
||||
|
||||
.icon-box {
|
||||
cursor: pointer;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.mt15 {
|
||||
margin-top: 15px;
|
||||
.icon-box.remove {
|
||||
color: var(--el-color-danger);
|
||||
}
|
||||
.icon-box.copy {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
.mt5 {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
cursor: no-drop;
|
||||
color: #c3c3c3;
|
||||
|
|
|
|||
|
|
@ -3,18 +3,22 @@
|
|||
<el-main class="main">
|
||||
<left-panel></left-panel>
|
||||
<el-divider direction="vertical" style="height: 100%"/>
|
||||
<center-panel></center-panel>
|
||||
<center-panel :fields="fields" :config="config"></center-panel>
|
||||
<el-divider direction="vertical" style="height: 100%"/>
|
||||
<right-panel></right-panel>
|
||||
<right-panel :fields="fields" :config="config"></right-panel>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from "vue"
|
||||
import leftPanel from "./left"
|
||||
import centerPanel from "./center"
|
||||
import rightPanel from "./right"
|
||||
|
||||
let fields = ref([])
|
||||
let config = ref({})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@
|
|||
|
||||
<script setup>
|
||||
import draggable from 'vuedraggable'
|
||||
import {ref} from "vue"
|
||||
|
||||
const group = {name: 'base', pull: 'clone', put: false}
|
||||
const inputComps = [{
|
||||
|
|
@ -218,7 +219,7 @@ const systemComps = [{
|
|||
icon: "pi-icon-asset-choice",
|
||||
name: "asset",
|
||||
props: {}
|
||||
},{
|
||||
}, {
|
||||
id: 's2',
|
||||
title: "图标选择",
|
||||
icon: "pi-icon-icon-choice",
|
||||
|
|
@ -226,10 +227,13 @@ const systemComps = [{
|
|||
props: {}
|
||||
}]
|
||||
|
||||
let num = ref(100)
|
||||
|
||||
function cloneField(e) {
|
||||
const field = JSON.parse(JSON.stringify(e));
|
||||
field.id = field.id + this.num;
|
||||
this.num++
|
||||
field.id = field.id + num.value;
|
||||
field.index = num.value
|
||||
num.value++
|
||||
return field
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,42 @@
|
|||
<template>
|
||||
<section class="panel">
|
||||
13213
|
||||
<el-tabs v-model="activeName" stretch>
|
||||
<el-tab-pane label="组件属性" name="field">
|
||||
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="表单属性" name="form">
|
||||
<el-form>
|
||||
<el-form-item label="表单名称" prop="ref">
|
||||
<el-input v-model="config.ref"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="表单模型" prop="model" required>
|
||||
<el-input v-model="config.model"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="校验模型" prop="rules">
|
||||
<el-input v-model="config.rules"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="校验模型" prop="rules">
|
||||
<el-input v-model="config.rules"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
|
||||
let activeName = ref("field")
|
||||
const props = defineProps({
|
||||
fields: {type: Array, default: []},
|
||||
config: {type: Object, default: {}}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.panel {width: 350px}
|
||||
.panel {
|
||||
width: 350px
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue