表单构建

This commit is contained in:
zhang zhuo 2025-11-25 14:22:11 +08:00
parent 8f85550572
commit f06cfbfda6
7 changed files with 205 additions and 49 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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;

View File

@ -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>

View File

@ -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
}

View File

@ -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>