表单构建

This commit is contained in:
zhang zhuo 2025-11-26 18:12:56 +08:00
parent f06cfbfda6
commit 013ebbbf13
8 changed files with 524 additions and 202 deletions

View File

@ -0,0 +1,3 @@
<template>
<svg t="1764150592466" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5627" width="256" height="256"><path d="M446.5664 0H332.8v113.7664h113.7664V0z m0 227.5584H332.8v113.7664h113.7664V227.584zM332.8 455.1168h113.7664v113.7664H332.8v-113.7664z m113.7664 227.5584H332.8v113.7664h113.7664v-113.7664zM332.8 910.2336h113.7664V1024H332.8v-113.7664zM674.1248 0H560.384v113.7664h113.7664V0zM560.384 227.5584h113.7664v113.7664H560.384V227.584z m113.7664 227.5584H560.384v113.7664h113.7664v-113.7664zM560.384 682.6752h113.7664v113.7664H560.384v-113.7664z m113.7664 227.5584H560.384V1024h113.7664v-113.7664z" p-id="5628"></path></svg>
</template>

View File

@ -0,0 +1,97 @@
<template>
<section>
<draggable v-model="value" :item-key="itemKey" animation="200" :group="group" handle=".item-rank">
<template #item="{ element, index }">
<div class="draggable-item">
<div class="item-content">
<el-icon size="50" class="item-rank">
<component :is="'pi-icon-move-area'"/>
</el-icon>
<div class="item-value">
<slot v-bind="{ element, index }"></slot>
</div>
</div>
<el-icon v-if="showDel" size="28" class="item-close" @click="remove(index)">
<component :is="'el-icon-CircleCloseFilled'"/>
</el-icon>
</div>
</template>
</draggable>
<div v-if="showAdd" class="add">
<el-link :underline="false" type="primary" icon="el-icon-circle-plus" @click="add">添加规则</el-link>
</div>
</section>
</template>
<script setup>
import draggable from 'vuedraggable'
import {computed} from "vue";
defineOptions({
name: "piDraggable"
})
const emit = defineEmits(['update:modelValue'])
const props = defineProps({
modelValue: {type: Array},
group: {type: Object},
itemKey: {type: String, default: "index"},
showAdd: {type: Boolean, default: true},
showDel: {type: Boolean, default: true},
template: {type: Object, default: {}}
})
const value = computed({
get() {
return props.modelValue
},
set(value) {
emit('update:modelValue', value)
}
})
function remove(element, index) {
value.value.splice(index, 1)
}
function add() {
value.value.push(JSON.parse(JSON.stringify(props.template)))
}
</script>
<style lang="scss" scoped>
.draggable-item {
margin: 0 10px 15px 0;
position: relative;
.item-content {
display: flex;
align-items: center;
padding: 10px;
border: 1px solid #eeeeee;
border-radius: 8px;
}
.item-rank {
cursor: move;
color: #9E9E9E;
width: 50px;
}
.item-value {
flex: 1;
}
.item-close {
position: absolute;
cursor: pointer;
color: #9E9E9E;
top: -14px;
right: -12px;
}
}
.add {
padding: 0 10px 10px 20px;
}
</style>

View File

@ -1,9 +1,14 @@
<template> <template>
<div class="pi-icon-select"> <div class="pi-icon-select">
<div class="pi-icon-select__wrapper" :class="{'hasValue':value}" @click="open"> <div class="pi-icon-select__wrapper" :class="{'hasValue':value}" style="width: 100%;">
<el-input :prefix-icon="value||'el-icon-plus'" v-model="value" :disabled="disabled" readonly></el-input> <el-input v-model="value" :disabled="disabled" readonly>
<template #append>
<el-icon size="22" @click="open">
<component :is="value||'el-icon-plus'"/>
</el-icon>
</template>
</el-input>
</div> </div>
<el-text style="margin-left: 5px;">{{value}}</el-text>
<el-dialog title="图标选择器" v-model="dialogVisible" :width="760" destroy-on-close append-to-body> <el-dialog title="图标选择器" v-model="dialogVisible" :width="760" destroy-on-close append-to-body>
<div class="pi-icon-select__dialog"> <div class="pi-icon-select__dialog">
<el-form :rules="{}"> <el-form :rules="{}">
@ -21,7 +26,7 @@
<div class="pi-icon-select__list"> <div class="pi-icon-select__list">
<el-scrollbar> <el-scrollbar>
<ul @click="selectIcon"> <ul @click="selectIcon">
<el-empty v-if="item.icons.length==0" :image-size="100" <el-empty v-if="item.icons.length===0" :image-size="100"
description="未查询到相关图标"/> description="未查询到相关图标"/>
<li v-for="icon in item.icons" :key="icon"> <li v-for="icon in item.icons" :key="icon">
<span :data-icon="icon"></span> <span :data-icon="icon"></span>
@ -115,37 +120,19 @@ function search(text) {
display: inline-flex; display: inline-flex;
} }
.pi-icon-select__wrapper {
cursor: pointer;
display: inline-flex;
}
.pi-icon-select__wrapper:deep(.el-input__wrapper).is-focus {
box-shadow: 0 0 0 1px var(--el-input-hover-border-color) inset;
}
.pi-icon-select__wrapper:deep(.el-input__inner) {
flex-grow: 0;
width: 0;
}
.pi-icon-select__wrapper:deep(.el-input__icon) {
margin: 0;
font-size: 16px;
}
.pi-icon-select__wrapper.hasValue:deep(.el-input__icon) { .pi-icon-select__wrapper.hasValue:deep(.el-input__icon) {
color: var(--el-text-color-regular); color: var(--el-text-color-regular);
} }
.pi-icon-select__wrapper:deep(.el-input-group__append, .el-input-group__prepend) {
padding: 0 10px;
}
.pi-icon-select__list { .pi-icon-select__list {
height: 270px; height: 270px;
overflow: auto; overflow: auto;
} }
.pi-icon-select__list ul {
}
.pi-icon-select__list li { .pi-icon-select__list li {
display: inline-block; display: inline-block;
width: 80px; width: 80px;

View File

@ -8,11 +8,11 @@
<h2>{{ form.title || "新增菜单" }}</h2> <h2>{{ form.title || "新增菜单" }}</h2>
<el-form :model="form" :rules="rules" ref="dialogForm" label-width="80px" label-position="left"> <el-form :model="form" :rules="rules" ref="dialogForm" label-width="80px" label-position="left">
<el-form-item label="显示名称" prop="title"> <el-form-item label="显示名称" prop="title">
<el-input v-model="form.title" clearable placeholder="菜单显示名字"></el-input> <el-input v-model="form.title" clearable placeholder="菜单显示名字" style="width: 300px;"></el-input>
</el-form-item> </el-form-item>
<el-form-item label="上级菜单" prop="pid"> <el-form-item label="上级菜单" prop="pid">
<el-cascader ref="parentId" v-model="form.pid" :options="menuOptions" :props="menuProps" <el-cascader ref="parentId" v-model="form.pid" :options="menuOptions" :props="menuProps"
:show-all-levels="false" placeholder="顶级菜单" clearable></el-cascader> :show-all-levels="false" placeholder="顶级菜单" clearable style="width: 300px;"></el-cascader>
</el-form-item> </el-form-item>
<el-form-item label="类型" prop="type"> <el-form-item label="类型" prop="type">
<el-radio-group v-model="form.type"> <el-radio-group v-model="form.type">
@ -23,30 +23,30 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item v-if="form.type == 0 || form.type == 3" label="菜单图标" prop="icon"> <el-form-item v-if="form.type == 0 || form.type == 3" label="菜单图标" prop="icon">
<pi-icon v-model="form.icon" clearable></pi-icon> <pi-icon v-model="form.icon" style="width: 300px;"></pi-icon>
</el-form-item> </el-form-item>
<el-form-item v-if="form.type == 0" label="页面名称" prop="name"> <el-form-item v-if="form.type == 0" label="页面名称" prop="name">
<el-input v-model="form.name" clearable></el-input> <el-input v-model="form.name" clearable style="width: 300px;"></el-input>
<div class="el-form-item-msg"> <div class="el-form-item-msg">
系统唯一且与内置组件名一致否则导致缓存失效如类型为Iframe的菜单别名将代替源地址显示在地址栏 系统唯一且与内置组件名一致否则导致缓存失效如类型为Iframe的菜单别名将代替源地址显示在地址栏
</div> </div>
</el-form-item> </el-form-item>
<el-form-item v-if="form.type == 0" label="路由地址" prop="path"> <el-form-item v-if="form.type == 0" label="路由地址" prop="path">
<el-input v-model="form.path" clearable></el-input> <el-input v-model="form.path" clearable style="width: 300px;"></el-input>
<div class="el-form-item-msg">首位不需要填写 "/" 自动匹配页面路径</div> <div class="el-form-item-msg">首位不需要填写 "/" 自动匹配页面路径</div>
</el-form-item> </el-form-item>
<el-form-item v-if="form.type == 0" label="是否隐藏" prop="hidden"> <el-form-item v-if="form.type == 0" label="是否隐藏" prop="hidden">
<el-checkbox v-model="form.hidden" :true-value="1" :false-value="0">隐藏菜单</el-checkbox> <el-checkbox v-model="form.hidden" :true-value="1" :false-value="0">隐藏菜单</el-checkbox>
</el-form-item> </el-form-item>
<el-form-item v-if="form.type == 3" label="外部链接" prop="path"> <el-form-item v-if="form.type == 3" label="外部链接" prop="path">
<el-input v-model="form.path" clearable></el-input> <el-input v-model="form.path" clearable style="width: 300px;"></el-input>
</el-form-item> </el-form-item>
<el-form-item v-if="form.type == 0 || form.type == 3" label="页面顺序" prop="rank"> <el-form-item v-if="form.type == 0 || form.type == 3" label="页面顺序" prop="rank">
<el-input v-model="form.rank" clearable></el-input> <el-input v-model="form.rank" clearable style="width: 300px;"></el-input>
<div class="el-form-item-msg">数值越大越靠前</div> <div class="el-form-item-msg">数值越大越靠前</div>
</el-form-item> </el-form-item>
<el-form-item v-if="form.type == 2" label="请求方式" prop="method"> <el-form-item v-if="form.type == 2" label="请求方式" prop="method">
<el-select v-model="form.method"> <el-select v-model="form.method" style="width: 300px;">
<el-option <el-option
v-for="item in methodOptions" v-for="item in methodOptions"
:key="item.value" :key="item.value"
@ -56,7 +56,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item v-if="form.type == 1 || form.type == 2" label="权限标识" prop="flag"> <el-form-item v-if="form.type == 1 || form.type == 2" label="权限标识" prop="flag">
<el-input v-model="form.flag" clearable></el-input> <el-input v-model="form.flag" clearable style="width: 300px;"></el-input>
<div class="el-form-item-msg">权限分隔符":"分割</div> <div class="el-form-item-msg">权限分隔符":"分割</div>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>

View File

@ -1,33 +1,41 @@
<template> <template>
<section class="panel"> <section class="panel">
<el-main style="height: 100%;"> <el-scrollbar class="height-100">
<draggable :list="fields" @update:list="fields = $event" :scroll="true" animation="200" item-key="id" <el-main class="height-100">
class="body" <el-form class="height-100" :label-position="config.labelPosition" :label-width="config.labelWidth"
:group="group" @add="addComp" @sort="sortComp" ghostClass="ghostClass"> :size="config.size" :disabled="config.disabled">
<template #item="{ element, index }"> <draggable :list="fields" :scroll="true" animation="200" item-key="id" class="height-100"
<form class="box"> :group="group" @update:list="fields = $event" @add="addComp" @sort="sortComp"
<el-form-item class="item" :class="{'active': curIndex===index}" ghostClass="ghostClass">
@click="clickComp(element,index)" :label="fields[index].title"> <template #item="{ element, index }">
<el-input v-if="fields[index].name==='text'" v-model="fields[index].value"></el-input> <div class="box">
<el-input v-if="fields[index].name==='textarea'" type="textarea" <el-form-item class="item" :class="{'active': curIndex===index}"
v-model="fields[index].value"></el-input> @click="clickComp(element,index)" :label="fields[index].title"
<el-input v-if="fields[index].name==='password'" type="password" :style="{width: fields[index].width}" :required="fields[index].required">
v-model="fields[index].value" show-password></el-input> <el-input v-if="fields[index].name==='text'" v-model="fields[index].value"
<el-input-number v-if="fields[index].name==='number'" v-bind="fields[index].props"></el-input>
v-model="fields[index].value"></el-input-number> <el-input v-if="fields[index].name==='textarea'" type="textarea"
</el-form-item> v-model="fields[index].value" v-bind="fields[index].props"></el-input>
<div v-if="curIndex===index" class="tools-box"> <el-input v-if="fields[index].name==='password'" type="password"
<el-icon size="22" class="icon-box remove" @click="delComp(index)"> v-model="fields[index].value" v-bind="fields[index].props"
<component :is="'pi-icon-shan-chu'"/> show-password></el-input>
</el-icon> <el-input-number v-if="fields[index].name==='number'" v-model="fields[index].value"
<el-icon size="20" class="icon-box copy mt5" @click="copyComp(index)"> v-bind="fields[index].props"></el-input-number>
<component :is="'pi-icon-fu-zhi'"/> </el-form-item>
</el-icon> <div v-if="curIndex===index" class="tools-box">
</div> <el-icon size="22" class="icon-box remove" @click="delComp(index)">
</form> <component :is="'pi-icon-shan-chu'"/>
</template> </el-icon>
</draggable> <el-icon size="20" class="icon-box copy mt5" @click="copyComp(index)">
</el-main> <component :is="'pi-icon-fu-zhi'"/>
</el-icon>
</div>
</div>
</template>
</draggable>
</el-form>
</el-main>
</el-scrollbar>
</section> </section>
</template> </template>
@ -40,7 +48,7 @@ const props = defineProps({
fields: {type: Array, default: []}, fields: {type: Array, default: []},
config: {type: Object, default: {}} config: {type: Object, default: {}}
}) })
const emit = defineEmits(['change'])
const group = {name: 'page', pull: false, put: true} const group = {name: 'page', pull: false, put: true}
let curIndex = ref(0) let curIndex = ref(0)
let curComp = ref({}) let curComp = ref({})
@ -49,6 +57,7 @@ let curComp = ref({})
function sortComp(e) { function sortComp(e) {
curIndex.value = e.newIndex curIndex.value = e.newIndex
curComp.value = props.fields[e.newIndex] curComp.value = props.fields[e.newIndex]
emit('change', curComp.value)
} }
// //
@ -56,6 +65,7 @@ function addComp(e) {
let element = props.fields[e.newIndex] let element = props.fields[e.newIndex]
curIndex.value = e.newIndex curIndex.value = e.newIndex
curComp.value = element curComp.value = element
emit('change', curComp.value)
} }
// //
@ -65,6 +75,7 @@ function clickComp(element, index) {
} }
curIndex.value = index curIndex.value = index
curComp.value = element curComp.value = element
emit('change', curComp.value)
} }
// //
@ -85,6 +96,7 @@ function delComp(index) {
} }
curIndex.value = i curIndex.value = i
curComp.value = props.fields[i] curComp.value = props.fields[i]
emit('change', curComp.value || {})
}) })
} }
@ -97,6 +109,7 @@ function copyComp(index) {
tools.array.zIndexTo(props.fields, props.fields.length - 1, index + 1) tools.array.zIndexTo(props.fields, props.fields.length - 1, index + 1)
curIndex.value = index + 1 curIndex.value = index + 1
curComp.value = props.fields[index + 1] curComp.value = props.fields[index + 1]
emit('change', curComp.value)
}) })
} }
</script> </script>
@ -110,10 +123,6 @@ function copyComp(index) {
display: none; display: none;
} }
.body {
height: 100%;
}
:deep(.ghostClass) { :deep(.ghostClass) {
.comp { .comp {
display: none; display: none;
@ -127,6 +136,14 @@ function copyComp(index) {
} }
} }
.height-100 {
height: 100%;
}
:deep(.el-scrollbar__view) {
height: 100%;
}
.active { .active {
border: 1px dashed #787be8 !important; border: 1px dashed #787be8 !important;
color: #787be8; color: #787be8;
@ -146,21 +163,24 @@ function copyComp(index) {
.tools-box { .tools-box {
position: absolute; position: absolute;
right: -25px; right: -20px;
top: 0; top: 0;
display: inline-grid; display: inline-grid;
padding: 5px 8px; padding: 5px;
border-radius: 4px; border-radius: 4px;
.icon-box { .icon-box {
cursor: pointer; cursor: pointer;
} }
.icon-box.remove { .icon-box.remove {
color: var(--el-color-danger); color: var(--el-color-danger);
} }
.icon-box.copy { .icon-box.copy {
color: var(--el-color-primary); color: var(--el-color-primary);
} }
.mt5 { .mt5 {
margin-top: 5px; margin-top: 5px;
} }

View File

@ -3,9 +3,9 @@
<el-main class="main"> <el-main class="main">
<left-panel></left-panel> <left-panel></left-panel>
<el-divider direction="vertical" style="height: 100%"/> <el-divider direction="vertical" style="height: 100%"/>
<center-panel :fields="fields" :config="config"></center-panel> <center-panel :fields="fields" :config="config" @change="setField"></center-panel>
<el-divider direction="vertical" style="height: 100%"/> <el-divider direction="vertical" style="height: 100%"/>
<right-panel :fields="fields" :config="config"></right-panel> <right-panel :config="config" :curField="curField"></right-panel>
</el-main> </el-main>
</el-container> </el-container>
</template> </template>
@ -17,7 +17,21 @@ import centerPanel from "./center"
import rightPanel from "./right" import rightPanel from "./right"
let fields = ref([]) let fields = ref([])
let config = ref({}) let config = ref({
ref: 'formRef',
model: 'form',
rules: 'rules',
size: 'default',
labelPosition: 'right',
labelWidth: 100,
disabled: false
})
let curField = ref({})
function setField(v) {
curField.value = v
curField.value.field = v.id
}
</script> </script>

View File

@ -1,93 +1,95 @@
<template> <template>
<section class="panel"> <section class="panel">
<div class="title"> <el-scrollbar>
<el-text> <div class="title">
<el-icon> <el-text>
<component :is="'pi-icon-zu-jian'"/> <el-icon>
</el-icon> <component :is="'pi-icon-zu-jian'"/>
输入组件 </el-icon>
</el-text> 输入组件
</div> </el-text>
<draggable v-model="inputComps" animation="200" item-key="id" class="go-base" :group="group" :sort="false" </div>
:clone="cloneField"> <draggable v-model="inputComps" animation="200" item-key="id" class="go-base" :group="group" :sort="false"
<template #item="{ element }"> :clone="cloneField">
<div class="item"> <template #item="{ element }">
<div class="tips">松开鼠标组件将添加到此处</div> <div class="item">
<div class="comp"> <div class="tips">松开鼠标组件将添加到此处</div>
<el-icon size="20"> <div class="comp">
<component :is="element.icon"/> <el-icon size="20">
</el-icon> <component :is="element.icon"/>
{{ element.title }} </el-icon>
{{ element.title }}
</div>
</div> </div>
</div> </template>
</template> </draggable>
</draggable> <div class="title">
<div class="title"> <el-text>
<el-text> <el-icon>
<el-icon> <component :is="'pi-icon-zu-jian'"/>
<component :is="'pi-icon-zu-jian'"/> </el-icon>
</el-icon> 选择组件
选择组件 </el-text>
</el-text> </div>
</div> <draggable v-model="choiceComps" animation="200" item-key="id" class="go-base" :group="group" :sort="false"
<draggable v-model="choiceComps" animation="200" item-key="id" class="go-base" :group="group" :sort="false" :clone="cloneField">
:clone="cloneField"> <template #item="{ element }">
<template #item="{ element }"> <div class="item">
<div class="item"> <div class="tips">松开鼠标组件将添加到此处</div>
<div class="tips">松开鼠标组件将添加到此处</div> <div class="comp">
<div class="comp"> <el-icon size="20">
<el-icon size="20"> <component :is="element.icon"/>
<component :is="element.icon"/> </el-icon>
</el-icon> {{ element.title }}
{{ element.title }} </div>
</div> </div>
</div> </template>
</template> </draggable>
</draggable> <div class="title">
<div class="title"> <el-text>
<el-text> <el-icon>
<el-icon> <component :is="'pi-icon-zu-jian'"/>
<component :is="'pi-icon-zu-jian'"/> </el-icon>
</el-icon> 布局组件
布局组件 </el-text>
</el-text> </div>
</div> <draggable v-model="layoutComps" animation="200" item-key="id" class="go-base" :group="group" :sort="false"
<draggable v-model="layoutComps" animation="200" item-key="id" class="go-base" :group="group" :sort="false" :clone="cloneField">
:clone="cloneField"> <template #item="{ element }">
<template #item="{ element }"> <div class="item">
<div class="item"> <div class="tips">松开鼠标组件将添加到此处</div>
<div class="tips">松开鼠标组件将添加到此处</div> <div class="comp">
<div class="comp"> <el-icon size="20">
<el-icon size="20"> <component :is="element.icon"/>
<component :is="element.icon"/> </el-icon>
</el-icon> {{ element.title }}
{{ element.title }} </div>
</div> </div>
</div> </template>
</template> </draggable>
</draggable> <div class="title">
<div class="title"> <el-text>
<el-text> <el-icon>
<el-icon> <component :is="'pi-icon-zu-jian'"/>
<component :is="'pi-icon-zu-jian'"/> </el-icon>
</el-icon> 内置组件
内置组件 </el-text>
</el-text> </div>
</div> <draggable v-model="systemComps" animation="200" item-key="id" class="go-base" :group="group" :sort="false"
<draggable v-model="systemComps" animation="200" item-key="id" class="go-base" :group="group" :sort="false" :clone="cloneField">
:clone="cloneField"> <template #item="{ element }">
<template #item="{ element }"> <div class="item">
<div class="item"> <div class="tips">松开鼠标组件将添加到此处</div>
<div class="tips">松开鼠标组件将添加到此处</div> <div class="comp">
<div class="comp"> <el-icon size="20">
<el-icon size="20"> <component :is="element.icon"/>
<component :is="element.icon"/> </el-icon>
</el-icon> {{ element.title }}
{{ element.title }} </div>
</div> </div>
</div> </template>
</template> </draggable>
</draggable> </el-scrollbar>
</section> </section>
</template> </template>
@ -101,130 +103,196 @@ const inputComps = [{
title: "单行文本", title: "单行文本",
icon: "pi-icon-line-input", icon: "pi-icon-line-input",
name: "text", name: "text",
props: {} props: {
placeholder: null,
labelWidth: null,
prefixIcon: null,
suffixIcon: null,
minlength: null,
maxlength: null,
clearable: false,
readonly: false,
disabled: false,
required: false,
showWordLimit: false
},
rules: [{regex: '', message: '213123'}],
width: "100%",
required: false
}, { }, {
id: "i2", id: "i2",
title: "多行文本", title: "多行文本",
icon: "pi-icon-multi-input", icon: "pi-icon-multi-input",
name: "textarea", name: "textarea",
props: {} props: {
placeholder: null,
labelWidth: null,
minlength: null,
maxlength: null,
readonly: false,
disabled: false,
required: false,
showWordLimit: false,
rows: 3
},
rules: [],
width: "100%",
required: false
}, { }, {
id: "i3", id: "i3",
title: "密码", title: "密码",
icon: "pi-icon-lock", icon: "pi-icon-lock",
name: "password", name: "password",
props: {} props: {
placeholder: null,
labelWidth: null,
prefixIcon: null,
suffixIcon: null,
minlength: null,
maxlength: null,
clearable: false,
readonly: false,
disabled: false,
required: false,
showWordLimit: false
},
rules: [],
width: "100%",
required: false
}, { }, {
id: "i4", id: "i4",
title: "计数器", title: "计数器",
icon: "pi-icon-number-input", icon: "pi-icon-number-input",
name: "number", name: "number",
props: {} props: {
placeholder: null
},
rules: [],
width: "100%",
required: false,
value: 1
}] }]
const choiceComps = [{ const choiceComps = [{
id: "c1", id: "c1",
title: "下拉组件", title: "下拉组件",
icon: "pi-icon-select", icon: "pi-icon-select",
name: "select", name: "select",
props: {} props: {},
rules: []
}, { }, {
id: "c2", id: "c2",
title: "级联组件", title: "级联组件",
icon: "pi-icon-cascader", icon: "pi-icon-cascader",
name: "cascader", name: "cascader",
props: {} props: {},
rules: []
}, { }, {
id: "c3", id: "c3",
title: "单选组件", title: "单选组件",
icon: "pi-icon-radio", icon: "pi-icon-radio",
name: "radio", name: "radio",
props: {} props: {},
rules: []
}, { }, {
id: "c4", id: "c4",
title: "多选组件", title: "多选组件",
icon: "pi-icon-checkbox", icon: "pi-icon-checkbox",
name: "checkbox", name: "checkbox",
props: {} props: {},
rules: []
}, { }, {
id: "c5", id: "c5",
title: "开关", title: "开关",
icon: "pi-icon-switch", icon: "pi-icon-switch",
name: "switch", name: "switch",
props: {} props: {},
rules: []
}, { }, {
id: "c6", id: "c6",
title: "滑块", title: "滑块",
icon: "pi-icon-slider", icon: "pi-icon-slider",
name: "slider", name: "slider",
props: {} props: {},
rules: []
}, { }, {
id: "c7", id: "c7",
title: "时间选择", title: "时间选择",
icon: "pi-icon-time-picker", icon: "pi-icon-time-picker",
name: "time", name: "time",
props: {} props: {},
rules: []
}, { }, {
id: "c8", id: "c8",
title: "时间范围", title: "时间范围",
icon: "pi-icon-time-range", icon: "pi-icon-time-range",
name: "timerange", name: "timerange",
props: {} props: {},
rules: []
}, { }, {
id: "c9", id: "c9",
title: "日期选择", title: "日期选择",
icon: "pi-icon-date-picker", icon: "pi-icon-date-picker",
name: "date", name: "date",
props: {} props: {},
rules: []
}, { }, {
id: "c10", id: "c10",
title: "日期范围", title: "日期范围",
icon: "pi-icon-date-range", icon: "pi-icon-date-range",
name: "daterange", name: "daterange",
props: {} props: {},
rules: []
}, { }, {
id: "c11", id: "c11",
title: "评分", title: "评分",
icon: "pi-icon-rate", icon: "pi-icon-rate",
name: "rate", name: "rate",
props: {} props: {},
rules: []
}, { }, {
id: "c12", id: "c12",
title: "颜色选择", title: "颜色选择",
icon: "pi-icon-color-picker", icon: "pi-icon-color-picker",
name: "color", name: "color",
props: {} props: {},
rules: []
}, { }, {
id: 'c13', id: 'c13',
title: "上传", title: "上传",
icon: "pi-icon-upload-file", icon: "pi-icon-upload-file",
name: "upload", name: "upload",
props: {} props: {},
rules: []
}] }]
const layoutComps = [{ const layoutComps = [{
id: 'l1', id: 'l1',
title: "行容器", title: "行容器",
icon: "pi-icon-row-layout", icon: "pi-icon-row-layout",
name: "layout", name: "layout",
props: {} props: {},
rules: []
}, { }, {
id: 'l2', id: 'l2',
title: "按钮", title: "按钮",
icon: "pi-icon-button", icon: "pi-icon-button",
name: "button", name: "button",
props: {} props: {},
rules: []
}]; }];
const systemComps = [{ const systemComps = [{
id: 's1', id: 's1',
title: "资源选择", title: "资源选择",
icon: "pi-icon-asset-choice", icon: "pi-icon-asset-choice",
name: "asset", name: "asset",
props: {} props: {},
rules: []
}, { }, {
id: 's2', id: 's2',
title: "图标选择", title: "图标选择",
icon: "pi-icon-icon-choice", icon: "pi-icon-icon-choice",
name: "icon", name: "icon",
props: {} props: {},
rules: []
}] }]
let num = ref(100) let num = ref(100)
@ -252,6 +320,18 @@ function cloneField(e) {
} }
} }
:deep(.el-scrollbar__bar) {
display: none !important;
}
:deep(.el-scrollbar__wrap) {
scrollbar-width: none;
}
:deep(.el-scrollbar__wrap::-webkit-scrollbar) {
display: none !important;
}
.go-base { .go-base {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View File

@ -1,42 +1,163 @@
<template> <template>
<section class="panel"> <section class="panel">
<el-tabs v-model="activeName" stretch> <el-scrollbar>
<el-tab-pane label="组件属性" name="field"> <el-tabs v-model="activeName" stretch>
<el-tab-pane label="组件属性" name="field">
</el-tab-pane> <el-form v-model="fForm" label-width="90px">
<el-tab-pane label="表单属性" name="form"> <el-form-item label="字段名" prop="field">
<el-form> <el-input v-model="fForm.field"></el-input>
<el-form-item label="表单名称" prop="ref"> </el-form-item>
<el-input v-model="config.ref"></el-input> <el-form-item label="标题" prop="title">
</el-form-item> <el-input v-model="fForm.title"></el-input>
<el-form-item label="表单模型" prop="model" required> </el-form-item>
<el-input v-model="config.model"></el-input> <el-form-item v-if="fForm.props?.placeholder !== undefined" label="占位提示"
</el-form-item> prop="props.placeholder">
<el-form-item label="校验模型" prop="rules"> <el-input v-model="fForm.props.placeholder"></el-input>
<el-input v-model="config.rules"></el-input> </el-form-item>
</el-form-item> <el-form-item v-if="fForm.props?.labelWidth !== undefined" label="标签宽度"
<el-form-item label="校验模型" prop="rules"> prop="props.labelWidth">
<el-input v-model="config.rules"></el-input> <el-input v-model="fForm.props.labelWidth"></el-input>
</el-form-item> </el-form-item>
</el-form> <el-form-item v-if="fForm.width !== undefined" label="组件宽度" prop="width">
</el-tab-pane> <el-input v-model="fForm.width"></el-input>
</el-tabs> </el-form-item>
<el-form-item label="默认值" prop="value">
<el-input v-model="fForm.value"></el-input>
</el-form-item>
<el-form-item v-if="fForm.props?.prefixIcon !== undefined" label="前图标"
prop="props.prefixIcon">
<pi-icon v-model="fForm.props.prefixIcon" style="width: 100%"></pi-icon>
</el-form-item>
<el-form-item v-if="fForm.props?.suffixIcon !== undefined" label="后图标"
prop="props.suffixIcon">
<pi-icon v-model="fForm.props.suffixIcon" style="width: 100%"></pi-icon>
</el-form-item>
<el-form-item v-if="fForm.props?.minlength !== undefined" label="最少输入"
prop="props.minlength">
<el-input v-model.number="fForm.props.minlength" type="number"></el-input>
</el-form-item>
<el-form-item v-if="fForm.props?.maxlength !== undefined" label="最多输入"
prop="props.maxlength">
<el-input v-model.number="fForm.props.maxlength" type="number"></el-input>
</el-form-item>
<el-form-item v-if="fForm.props?.rows !== undefined" label="默认行数" prop="props.rows">
<el-input v-model.number="fForm.props.rows" type="number"></el-input>
</el-form-item>
<el-form-item v-if="fForm.props?.showWordLimit !== undefined" label="显示字数"
prop="props.showWordLimit">
<el-switch v-model="fForm.props.showWordLimit"/>
</el-form-item>
<el-form-item v-if="fForm.props?.clearable !== undefined" label="能否清空"
prop="props.clearable">
<el-switch v-model="fForm.props.clearable"/>
</el-form-item>
<el-form-item v-if="fForm.props?.readonly !== undefined" label="是否只读" prop="props.readonly">
<el-switch v-model="fForm.props.readonly"/>
</el-form-item>
<el-form-item v-if="fForm.props?.disabled !== undefined" label="是否禁用" prop="props.disabled">
<el-switch v-model="fForm.props.disabled"/>
</el-form-item>
<el-form-item v-if="fForm.required !== undefined" label="是否必填" prop="required">
<el-switch v-model="fForm.required"/>
</el-form-item>
<el-divider>
<template #default>
正则校验
</template>
</el-divider>
<pi-draggable v-model="fForm.rules" item-key="regex">
<template #default="scope">
<el-form-item label="表达式" prop="regex">
<el-input v-model="scope.element.regex"></el-input>
</el-form-item>
<el-form-item label="错误提示" prop="message" style="margin-bottom: 0;">
<el-input v-model="scope.element.message"></el-input>
</el-form-item>
</template>
</pi-draggable>
</el-form>
</el-tab-pane>
<el-tab-pane label="表单属性" name="form">
<el-form v-model="cForm" label-width="90px">
<el-form-item label="表单名称" prop="ref">
<el-input v-model="cForm.ref"></el-input>
</el-form-item>
<el-form-item label="表单模型" prop="model">
<el-input v-model="cForm.model"></el-input>
</el-form-item>
<el-form-item label="校验模型" prop="rules">
<el-input v-model="cForm.rules"></el-input>
</el-form-item>
<el-form-item label="表单尺寸" prop="size">
<el-radio-group v-model="cForm.size">
<el-radio-button value="large">较大</el-radio-button>
<el-radio-button value="default">默认</el-radio-button>
<el-radio-button value="small">较小</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="标签对齐" prop="labelPosition">
<el-radio-group v-model="cForm.labelPosition">
<el-radio-button value="left">左对齐</el-radio-button>
<el-radio-button value="right">右对齐</el-radio-button>
<el-radio-button value="top">顶部对齐</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="标签宽度" prop="labelWidth">
<el-input-number v-model="cForm.labelWidth"/>
</el-form-item>
<el-form-item label="禁用表单" prop="disabled">
<el-switch v-model="cForm.disabled"/>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</el-scrollbar>
</section> </section>
</template> </template>
<script setup> <script setup>
import {ref} from "vue"; import {computed, ref} from "vue";
import piIcon from "@/components/piIcon"
import piDraggable from "@/components/piDraggable"
let activeName = ref("field") let activeName = ref("field")
const props = defineProps({ const props = defineProps({
fields: {type: Array, default: []}, curField: {type: Object, default: {}},
config: {type: Object, default: {}} config: {type: Object, default: {}}
}) })
const emit = defineEmits(['update:modelValue'])
let cForm = computed({
get() {
return props.config
},
set(value) {
this.$emit('update:modelValue', value)
}
})
let fForm = computed({
get() {
return props.curField
},
set(value) {
this.$emit('update:modelValue', value)
}
})
</script> </script>
<style scoped> <style lang="scss" scoped>
.panel { .panel {
width: 350px width: 350px
} }
:deep(.el-scrollbar__bar) {
display: none !important;
}
:deep(.el-scrollbar__wrap) {
scrollbar-width: none;
}
:deep(.el-scrollbar__wrap::-webkit-scrollbar) {
display: none !important;
}
</style> </style>