表单构建
This commit is contained in:
parent
013ebbbf13
commit
76e925b214
|
|
@ -93,5 +93,6 @@ function add() {
|
|||
|
||||
.add {
|
||||
padding: 0 10px 10px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -11,14 +11,12 @@ import errorHandler from "@/utils/errorHandler";
|
|||
|
||||
import piDialog from "@/components/piDialog"
|
||||
import piTable from "@/components/piTable"
|
||||
import piUpload from "@/components/piUpload"
|
||||
|
||||
export default {
|
||||
install(app: App) {
|
||||
// 注册全局组件
|
||||
app.component('piDialog', piDialog)
|
||||
app.component('piTable', piTable)
|
||||
app.component('piUpload', piUpload)
|
||||
|
||||
//注册全局指令
|
||||
app.directive('auth', auth)
|
||||
|
|
|
|||
|
|
@ -11,22 +11,47 @@
|
|||
<div class="box">
|
||||
<el-form-item class="item" :class="{'active': curIndex===index}"
|
||||
@click="clickComp(element,index)" :label="fields[index].title"
|
||||
:style="{width: fields[index].width}" :required="fields[index].required">
|
||||
<el-input v-if="fields[index].name==='text'" v-model="fields[index].value"
|
||||
v-bind="fields[index].props"></el-input>
|
||||
<el-input v-if="fields[index].name==='textarea'" type="textarea"
|
||||
v-model="fields[index].value" v-bind="fields[index].props"></el-input>
|
||||
<el-input v-if="fields[index].name==='password'" type="password"
|
||||
v-model="fields[index].value" v-bind="fields[index].props"
|
||||
show-password></el-input>
|
||||
<el-input-number v-if="fields[index].name==='number'" v-model="fields[index].value"
|
||||
v-bind="fields[index].props"></el-input-number>
|
||||
: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 mt5" @click="copyComp(index)">
|
||||
<el-icon size="20" class="icon-box copy ml5" @click="copyComp(index)">
|
||||
<component :is="'pi-icon-fu-zhi'"/>
|
||||
</el-icon>
|
||||
</div>
|
||||
|
|
@ -43,6 +68,11 @@
|
|||
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"
|
||||
|
||||
defineExpose({clickAddComp})
|
||||
|
||||
const props = defineProps({
|
||||
fields: {type: Array, default: []},
|
||||
|
|
@ -68,6 +98,14 @@ function addComp(e) {
|
|||
emit('change', curComp.value)
|
||||
}
|
||||
|
||||
// 添加项时
|
||||
function clickAddComp(e) {
|
||||
props.fields.push(e)
|
||||
curIndex.value = props.fields.length-1
|
||||
curComp.value = e
|
||||
emit('change', curComp.value)
|
||||
}
|
||||
|
||||
// 选中项
|
||||
function clickComp(element, index) {
|
||||
if (curIndex.value === index) {
|
||||
|
|
@ -155,17 +193,16 @@ function copyComp(index) {
|
|||
|
||||
.item {
|
||||
padding: 12px 10px;
|
||||
background: #f6f7ff;
|
||||
border: 1px dashed #f6f7ff;
|
||||
border: 1px;
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tools-box {
|
||||
position: absolute;
|
||||
right: -20px;
|
||||
top: 0;
|
||||
display: inline-grid;
|
||||
right: 10px;
|
||||
top: -16px;
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
border-radius: 4px;
|
||||
|
||||
|
|
@ -181,8 +218,8 @@ function copyComp(index) {
|
|||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.mt5 {
|
||||
margin-top: 5px;
|
||||
.ml5 {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,226 @@
|
|||
/**
|
||||
* 根据 fForm.name 决定要显示哪些设置项
|
||||
*/
|
||||
export const fieldEditors = {
|
||||
text: [
|
||||
{key: 'width', label: '组件宽度', type: 'input'},
|
||||
{key: 'props.placeholder', label: '占位提示', type: 'input'},
|
||||
{key: 'props.prefixIcon', label: '前图标', type: 'icon'},
|
||||
{key: 'props.suffixIcon', label: '后图标', type: 'icon'},
|
||||
{key: 'props.minlength', label: '最少输入', type: 'number'},
|
||||
{key: 'props.maxlength', label: '最多输入', type: 'number'},
|
||||
{key: 'props.showWordLimit', label: '显示字数', type: 'switch'},
|
||||
{key: 'props.clearable', label: '能否清空', type: 'switch'},
|
||||
{key: 'props.readonly', label: '是否只读', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
textarea: [
|
||||
{key: 'width', label: '组件宽度', type: 'input'},
|
||||
{key: 'props.placeholder', label: '占位提示', type: 'input'},
|
||||
{key: 'props.rows', label: '默认行数', type: 'number'},
|
||||
{key: 'props.minlength', label: '最少输入', type: 'number'},
|
||||
{key: 'props.maxlength', label: '最多输入', type: 'number'},
|
||||
{key: 'props.showWordLimit', label: '显示字数', type: 'switch'},
|
||||
{key: 'props.clearable', label: '能否清空', type: 'switch'},
|
||||
{key: 'props.readonly', label: '是否只读', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
password: [
|
||||
{key: 'width', label: '组件宽度', type: 'input'},
|
||||
{key: 'props.placeholder', label: '占位提示', type: 'input'},
|
||||
{key: 'props.prefixIcon', label: '前图标', type: 'icon'},
|
||||
{key: 'props.suffixIcon', label: '后图标', type: 'icon'},
|
||||
{key: 'props.minlength', label: '最少输入', type: 'number'},
|
||||
{key: 'props.maxlength', label: '最多输入', type: 'number'},
|
||||
{key: 'props.showWordLimit', label: '显示字数', type: 'switch'},
|
||||
{key: 'props.clearable', label: '能否清空', type: 'switch'},
|
||||
{key: 'props.readonly', label: '是否只读', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
number: [
|
||||
{key: 'width', label: '组件宽度', type: 'input'},
|
||||
{key: 'props.placeholder', label: '占位提示', type: 'input'},
|
||||
{key: 'props.precision', label: '精度', type: 'number'},
|
||||
{key: 'props.step', label: '步长', type: 'number'},
|
||||
{key: 'props.stepStrictly', label: '严格步数', type: 'switch'},
|
||||
{
|
||||
key: 'props.controlsPosition', label: '按钮位置', type: 'radio', options: [
|
||||
{label: '默认', value: ''},
|
||||
{label: '右侧', value: 'right'},
|
||||
]
|
||||
},
|
||||
{key: 'props.readonly', label: '是否只读', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
select: [
|
||||
{key: 'width', label: '组件宽度', type: 'input'},
|
||||
{key: 'props.placeholder', label: '占位提示', type: 'input'},
|
||||
{key: 'props.multiple', label: '是否多选', type: 'switch'},
|
||||
{key: 'props.filterable', label: '能否筛选', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
cascader: [
|
||||
{key: 'width', label: '组件宽度', type: 'input'},
|
||||
{key: 'props.placeholder', label: '占位提示', type: 'input'},
|
||||
{key: 'props.separator', label: '分隔符', type: 'input'},
|
||||
{key: 'props.showAllLevels', label: '展示全路径', type: 'switch'},
|
||||
{key: 'props.clearable', label: '能否清空', type: 'switch'},
|
||||
{key: 'props.props.multiple', label: '是否多选', type: 'switch'},
|
||||
{key: 'props.filterable', label: '能否筛选', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
radio: [
|
||||
{
|
||||
key: 'style', label: '选项样式', type: 'radio', options: [
|
||||
{label: '默认', value: ''},
|
||||
{label: '按钮', value: 'button'},
|
||||
]
|
||||
},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
checkbox: [
|
||||
{key: 'props.min', label: '最少应选', type: 'number'},
|
||||
{key: 'props.max', label: '最多可选', type: 'number'},
|
||||
{
|
||||
key: 'style', label: '选项样式', type: 'radio', options: [
|
||||
{label: '默认', value: ''},
|
||||
{label: '按钮', value: 'button'},
|
||||
]
|
||||
},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
switch: [
|
||||
{key: 'props.activeText', label: '打开提示', type: 'input'},
|
||||
{key: 'props.inactiveText', label: '关闭提示', type: 'input'},
|
||||
{key: 'props.activeValue', label: '打开值', type: 'input'},
|
||||
{key: 'props.inactiveValue', label: '关闭值', type: 'input'},
|
||||
{key: 'props.activeActionIcon', label: '打开图标', type: 'icon'},
|
||||
{key: 'props.inactiveActionIcon', label: '关闭图标', type: 'icon'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
slider: [
|
||||
{key: 'width', label: '组件宽度', type: 'input'},
|
||||
{key: 'props.min', label: '最大值', type: 'number'},
|
||||
{key: 'props.max', label: '最小值', type: 'number'},
|
||||
{key: 'props.step', label: '步长', type: 'number'},
|
||||
{key: 'props.showStops', label: '显示间隔点', type: 'switch'},
|
||||
{key: 'props.range', label: '范围选择', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
rate: [
|
||||
{key: 'props.max', label: '最大值', type: 'number'},
|
||||
{key: 'props.allowHalf', label: '是否半选', type: 'switch'},
|
||||
{key: 'props.showScore', label: '显示分数', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
color: [
|
||||
{
|
||||
key: 'props.colorFormat', label: '颜色格式', type: 'select', options: [
|
||||
{label: 'hsl', value: 'hsl'},
|
||||
{label: 'hsv', value: 'hsv'},
|
||||
{label: 'hex(when show-alpha is false)', value: 'hex'},
|
||||
{label: 'rgb(when show-alpha is true)', value: 'rgb'},
|
||||
]
|
||||
},
|
||||
{key: 'props.showAlpha', label: '透明度', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
upload: [
|
||||
{key: 'props.name', label: '文件字段名', type: 'input'},
|
||||
{key: 'btnText', label: '按钮文字', type: 'input'},
|
||||
{key: 'props.accept', label: '文件类型', type: 'input'},
|
||||
{
|
||||
key: 'props.listType', label: '列表类型', type: 'radio', options: [
|
||||
{label: 'text', value: 'text'},
|
||||
{label: 'picture', value: 'picture'},
|
||||
{label: 'picture-card', value: 'picture-card'}
|
||||
]
|
||||
},
|
||||
{key: 'tip', label: '提示信息', type: 'input'},
|
||||
{key: 'props.multiple', label: '是否多选', type: 'switch'},
|
||||
{key: 'props.autoUpload', label: '自动上传', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
date: [
|
||||
{key: 'width', label: '组件宽度', type: 'input'},
|
||||
{
|
||||
key: 'props.type', label: '时间类型', type: 'select', options: [
|
||||
{label: '日', value: 'date'},
|
||||
{label: '周', value: 'week'},
|
||||
{label: '月', value: 'month'},
|
||||
{label: '年', value: 'year'},
|
||||
{label: '日期时间', value: 'datetime'},
|
||||
]
|
||||
},
|
||||
{key: 'props.format', label: '显示格式', type: 'input'},
|
||||
{key: 'props.valueFormat', label: '日期格式', type: 'input'},
|
||||
{key: 'props.clearable', label: '能否清空', type: 'switch'},
|
||||
{key: 'props.readonly', label: '是否只读', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
daterange: [
|
||||
{
|
||||
key: 'props.type', label: '时间类型', type: 'select', options: [
|
||||
{label: '日期范围', value: 'daterange'},
|
||||
{label: '月范围', value: 'monthrange'},
|
||||
{label: '日期时间范围', value: 'datetimerange'},
|
||||
]
|
||||
},
|
||||
{key: 'props.rangeSeparator', label: '分隔符', type: 'input'},
|
||||
{key: 'props.format', label: '显示格式', type: 'input'},
|
||||
{key: 'props.valueFormat', label: '日期格式', type: 'input'},
|
||||
{key: 'props.clearable', label: '能否清空', type: 'switch'},
|
||||
{key: 'props.readonly', label: '是否只读', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
time: [
|
||||
{key: 'props.editable', label: '可输入', type: 'switch'},
|
||||
{key: 'props.rangeSeparator', label: '分隔符', type: 'input'},
|
||||
{key: 'props.clearable', label: '能否清空', type: 'switch'},
|
||||
{key: 'props.readonly', label: '是否只读', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
],
|
||||
timerange: [
|
||||
{key: 'props.editable', label: '可输入', type: 'switch'},
|
||||
{key: 'props.rangeSeparator', label: '分隔符', type: 'input'},
|
||||
{key: 'props.clearable', label: '能否清空', type: 'switch'},
|
||||
{key: 'props.readonly', label: '是否只读', type: 'switch'},
|
||||
{key: 'props.disabled', label: '是否禁用', type: 'switch'},
|
||||
]
|
||||
}
|
||||
// 'year' | 'years' |'month' | 'months' | 'date' | 'dates' | 'datetime' | 'week' | 'datetimerange' | 'daterange' | 'monthrange' | 'yearrange'
|
||||
export const getComponent = (type) => {
|
||||
const map = {
|
||||
text: 'el-input',
|
||||
textarea: 'el-input',
|
||||
password: 'el-input',
|
||||
number: 'el-input-number',
|
||||
select: 'el-select',
|
||||
cascader: 'el-cascader',
|
||||
radio: 'el-radio-group',
|
||||
checkbox: 'el-checkbox-group',
|
||||
switch: 'el-switch',
|
||||
slider: 'el-slider',
|
||||
rate: 'el-rate',
|
||||
color: 'el-color-picker',
|
||||
upload: 'el-upload',
|
||||
date: 'el-date-picker',
|
||||
daterange: 'el-date-picker',
|
||||
time: 'el-time-picker',
|
||||
timerange: 'el-time-picker',
|
||||
icon: 'pi-icon',
|
||||
asset: 'pi-asset'
|
||||
}
|
||||
return map[type] || 'div'
|
||||
}
|
||||
|
||||
export function getByPath(obj, path) {
|
||||
return path.split('.').reduce((o, k) => o?.[k], obj)
|
||||
}
|
||||
|
||||
export function setByPath(obj, path, value) {
|
||||
const keys = path.split('.')
|
||||
let o = obj
|
||||
keys.slice(0, -1).forEach(k => o = o[k])
|
||||
o[keys[keys.length - 1]] = value
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
<template>
|
||||
<el-container class="pi-panel">
|
||||
<el-main class="main">
|
||||
<left-panel></left-panel>
|
||||
<left-panel @addField="addField"></left-panel>
|
||||
<el-divider direction="vertical" style="height: 100%"/>
|
||||
<center-panel :fields="fields" :config="config" @change="setField"></center-panel>
|
||||
<center-panel ref="centerRef" :fields="fields" :config="config" @change="setField"></center-panel>
|
||||
<el-divider direction="vertical" style="height: 100%"/>
|
||||
<right-panel :config="config" :curField="curField"></right-panel>
|
||||
</el-main>
|
||||
|
|
@ -27,12 +27,17 @@ let config = ref({
|
|||
disabled: false
|
||||
})
|
||||
let curField = ref({})
|
||||
const centerRef = ref(null)
|
||||
|
||||
function setField(v) {
|
||||
curField.value = v
|
||||
curField.value.field = v.id
|
||||
}
|
||||
|
||||
function addField(e) {
|
||||
centerRef.value.clickAddComp(e)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<draggable v-model="inputComps" animation="200" item-key="id" class="go-base" :group="group" :sort="false"
|
||||
:clone="cloneField">
|
||||
<template #item="{ element }">
|
||||
<div class="item">
|
||||
<div class="item" @click="handleClick(element)">
|
||||
<div class="tips">松开鼠标组件将添加到此处</div>
|
||||
<div class="comp">
|
||||
<el-icon size="20">
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
<draggable v-model="choiceComps" animation="200" item-key="id" class="go-base" :group="group" :sort="false"
|
||||
:clone="cloneField">
|
||||
<template #item="{ element }">
|
||||
<div class="item">
|
||||
<div class="item" @click="handleClick(element)">
|
||||
<div class="tips">松开鼠标组件将添加到此处</div>
|
||||
<div class="comp">
|
||||
<el-icon size="20">
|
||||
|
|
@ -56,29 +56,7 @@
|
|||
<draggable v-model="layoutComps" animation="200" item-key="id" class="go-base" :group="group" :sort="false"
|
||||
:clone="cloneField">
|
||||
<template #item="{ element }">
|
||||
<div class="item">
|
||||
<div class="tips">松开鼠标组件将添加到此处</div>
|
||||
<div class="comp">
|
||||
<el-icon size="20">
|
||||
<component :is="element.icon"/>
|
||||
</el-icon>
|
||||
{{ element.title }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
<div class="title">
|
||||
<el-text>
|
||||
<el-icon>
|
||||
<component :is="'pi-icon-zu-jian'"/>
|
||||
</el-icon>
|
||||
内置组件
|
||||
</el-text>
|
||||
</div>
|
||||
<draggable v-model="systemComps" animation="200" item-key="id" class="go-base" :group="group" :sort="false"
|
||||
:clone="cloneField">
|
||||
<template #item="{ element }">
|
||||
<div class="item">
|
||||
<div class="item" @click="handleClick(element)">
|
||||
<div class="tips">松开鼠标组件将添加到此处</div>
|
||||
<div class="comp">
|
||||
<el-icon size="20">
|
||||
|
|
@ -97,6 +75,8 @@
|
|||
import draggable from 'vuedraggable'
|
||||
import {ref} from "vue"
|
||||
|
||||
const emit = defineEmits(['addField'])
|
||||
|
||||
const group = {name: 'base', pull: 'clone', put: false}
|
||||
const inputComps = [{
|
||||
id: "i1",
|
||||
|
|
@ -104,19 +84,9 @@ const inputComps = [{
|
|||
icon: "pi-icon-line-input",
|
||||
name: "text",
|
||||
props: {
|
||||
placeholder: null,
|
||||
labelWidth: null,
|
||||
prefixIcon: null,
|
||||
suffixIcon: null,
|
||||
minlength: null,
|
||||
maxlength: null,
|
||||
clearable: false,
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
required: false,
|
||||
showWordLimit: false
|
||||
type: 'text'
|
||||
},
|
||||
rules: [{regex: '', message: '213123'}],
|
||||
rules: [{regex: '', message: ''}],
|
||||
width: "100%",
|
||||
required: false
|
||||
}, {
|
||||
|
|
@ -125,15 +95,7 @@ const inputComps = [{
|
|||
icon: "pi-icon-multi-input",
|
||||
name: "textarea",
|
||||
props: {
|
||||
placeholder: null,
|
||||
labelWidth: null,
|
||||
minlength: null,
|
||||
maxlength: null,
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
required: false,
|
||||
showWordLimit: false,
|
||||
rows: 3
|
||||
type: 'textarea'
|
||||
},
|
||||
rules: [],
|
||||
width: "100%",
|
||||
|
|
@ -144,17 +106,8 @@ const inputComps = [{
|
|||
icon: "pi-icon-lock",
|
||||
name: "password",
|
||||
props: {
|
||||
placeholder: null,
|
||||
labelWidth: null,
|
||||
prefixIcon: null,
|
||||
suffixIcon: null,
|
||||
minlength: null,
|
||||
maxlength: null,
|
||||
clearable: false,
|
||||
readonly: false,
|
||||
disabled: false,
|
||||
required: false,
|
||||
showWordLimit: false
|
||||
type: 'password',
|
||||
showPassword: true
|
||||
},
|
||||
rules: [],
|
||||
width: "100%",
|
||||
|
|
@ -165,7 +118,7 @@ const inputComps = [{
|
|||
icon: "pi-icon-number-input",
|
||||
name: "number",
|
||||
props: {
|
||||
placeholder: null
|
||||
controlsPosition: ''
|
||||
},
|
||||
rules: [],
|
||||
width: "100%",
|
||||
|
|
@ -178,91 +131,139 @@ const choiceComps = [{
|
|||
icon: "pi-icon-select",
|
||||
name: "select",
|
||||
props: {},
|
||||
rules: []
|
||||
rules: [],
|
||||
options: [],
|
||||
width: "100%",
|
||||
required: false,
|
||||
}, {
|
||||
id: "c2",
|
||||
title: "级联组件",
|
||||
icon: "pi-icon-cascader",
|
||||
name: "cascader",
|
||||
props: {},
|
||||
rules: []
|
||||
props: {
|
||||
props: {
|
||||
label: 'label',
|
||||
value: 'value',
|
||||
multiple: true,
|
||||
},
|
||||
options: [{label: '一号楼', value: 'r1', children: [{label: '二单元', value: 'c2'}]}]
|
||||
},
|
||||
rules: [],
|
||||
width: "100%",
|
||||
required: false,
|
||||
}, {
|
||||
id: "c3",
|
||||
title: "单选组件",
|
||||
icon: "pi-icon-radio",
|
||||
name: "radio",
|
||||
props: {},
|
||||
rules: []
|
||||
rules: [],
|
||||
options: [],
|
||||
width: "100%",
|
||||
style: '',
|
||||
required: false,
|
||||
}, {
|
||||
id: "c4",
|
||||
title: "多选组件",
|
||||
icon: "pi-icon-checkbox",
|
||||
name: "checkbox",
|
||||
props: {},
|
||||
rules: []
|
||||
rules: [],
|
||||
options: [],
|
||||
style: '',
|
||||
required: false,
|
||||
}, {
|
||||
id: "c5",
|
||||
title: "开关",
|
||||
icon: "pi-icon-switch",
|
||||
name: "switch",
|
||||
props: {},
|
||||
rules: []
|
||||
rules: [],
|
||||
required: false,
|
||||
}, {
|
||||
id: "c6",
|
||||
title: "滑块",
|
||||
icon: "pi-icon-slider",
|
||||
name: "slider",
|
||||
props: {},
|
||||
rules: []
|
||||
rules: [],
|
||||
width: "100%",
|
||||
required: false,
|
||||
}, {
|
||||
id: "c7",
|
||||
title: "时间选择",
|
||||
icon: "pi-icon-time-picker",
|
||||
name: "time",
|
||||
props: {},
|
||||
props: {
|
||||
format: 'HH:mm:ss',
|
||||
valueFormat: 'HH:mm:ss'
|
||||
},
|
||||
rules: []
|
||||
}, {
|
||||
id: "c8",
|
||||
title: "时间范围",
|
||||
icon: "pi-icon-time-range",
|
||||
name: "timerange",
|
||||
props: {},
|
||||
props: {
|
||||
isRange: true,
|
||||
format: 'HH:mm:ss',
|
||||
valueFormat: 'HH:mm:ss'
|
||||
},
|
||||
rules: []
|
||||
}, {
|
||||
id: "c9",
|
||||
title: "日期选择",
|
||||
icon: "pi-icon-date-picker",
|
||||
name: "date",
|
||||
props: {},
|
||||
rules: []
|
||||
props: {
|
||||
type: "date",
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD'
|
||||
},
|
||||
rules: [],
|
||||
width: "100%",
|
||||
required: false,
|
||||
}, {
|
||||
id: "c10",
|
||||
title: "日期范围",
|
||||
icon: "pi-icon-date-range",
|
||||
name: "daterange",
|
||||
props: {},
|
||||
rules: []
|
||||
props: {
|
||||
type: "daterange",
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD'
|
||||
},
|
||||
rules: [],
|
||||
width: "100%",
|
||||
required: false,
|
||||
}, {
|
||||
id: "c11",
|
||||
title: "评分",
|
||||
icon: "pi-icon-rate",
|
||||
name: "rate",
|
||||
props: {},
|
||||
rules: []
|
||||
rules: [],
|
||||
required: false,
|
||||
}, {
|
||||
id: "c12",
|
||||
title: "颜色选择",
|
||||
icon: "pi-icon-color-picker",
|
||||
name: "color",
|
||||
props: {},
|
||||
rules: []
|
||||
rules: [],
|
||||
required: false,
|
||||
}, {
|
||||
id: 'c13',
|
||||
title: "上传",
|
||||
icon: "pi-icon-upload-file",
|
||||
name: "upload",
|
||||
props: {},
|
||||
rules: []
|
||||
props: {
|
||||
listType: 'text',
|
||||
name: 'file'
|
||||
},
|
||||
rules: [],
|
||||
required: false,
|
||||
btnText: "点击上传"
|
||||
}]
|
||||
const layoutComps = [{
|
||||
id: 'l1',
|
||||
|
|
@ -279,22 +280,6 @@ const layoutComps = [{
|
|||
props: {},
|
||||
rules: []
|
||||
}];
|
||||
const systemComps = [{
|
||||
id: 's1',
|
||||
title: "资源选择",
|
||||
icon: "pi-icon-asset-choice",
|
||||
name: "asset",
|
||||
props: {},
|
||||
rules: []
|
||||
}, {
|
||||
id: 's2',
|
||||
title: "图标选择",
|
||||
icon: "pi-icon-icon-choice",
|
||||
name: "icon",
|
||||
props: {},
|
||||
rules: []
|
||||
}]
|
||||
|
||||
let num = ref(100)
|
||||
|
||||
function cloneField(e) {
|
||||
|
|
@ -305,6 +290,10 @@ function cloneField(e) {
|
|||
return field
|
||||
}
|
||||
|
||||
function handleClick(e) {
|
||||
emit('addField', cloneField(e))
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -10,68 +10,72 @@
|
|||
<el-form-item label="标题" prop="title">
|
||||
<el-input v-model="fForm.title"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="fForm.props?.placeholder !== undefined" label="占位提示"
|
||||
prop="props.placeholder">
|
||||
<el-input v-model="fForm.props.placeholder"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="fForm.props?.labelWidth !== undefined" label="标签宽度"
|
||||
prop="props.labelWidth">
|
||||
<el-input v-model="fForm.props.labelWidth"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="fForm.width !== undefined" label="组件宽度" prop="width">
|
||||
<el-input v-model="fForm.width"></el-input>
|
||||
</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">
|
||||
<template v-for="item in fieldEditors[fForm.name] || []" :key="item.key">
|
||||
<el-form-item :label="item.label">
|
||||
<el-input
|
||||
v-if="item.type === 'input'"
|
||||
:model-value="getByPath(fForm, item.key)"
|
||||
@input="val => setByPath(fForm, item.key, val)"
|
||||
/>
|
||||
<el-input
|
||||
v-else-if="item.type === 'number'"
|
||||
type="number"
|
||||
:model-value="getByPath(fForm, item.key)"
|
||||
@input="val => setByPath(fForm, item.key, Number(val))"
|
||||
/>
|
||||
<el-switch
|
||||
v-else-if="item.type === 'switch'"
|
||||
:model-value="getByPath(fForm, item.key)"
|
||||
@change="val => setByPath(fForm, item.key, val)"
|
||||
/>
|
||||
<el-color-picker v-else-if="item.type === 'color'" show-alpha
|
||||
:model-value="getByPath(fForm, item.key)"
|
||||
@change="val => setByPath(fForm, item.key, val)"/>
|
||||
<el-radio-group v-else-if="item.type === 'radio'"
|
||||
:model-value="getByPath(fForm, item.key)"
|
||||
@change="val => setByPath(fForm, item.key, val)">
|
||||
<el-radio-button v-for="op in item.options" :key="op.value"
|
||||
:value="op.value">{{ op.label }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-select v-else-if="item.type === 'select'" :model-value="getByPath(fForm, item.key)"
|
||||
@change="val => setByPath(fForm, item.key, val)">
|
||||
<el-option v-for="op in item.options" :key="op.value" :value="op.value"
|
||||
:label="op.label">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<pi-icon
|
||||
v-else-if="item.type === 'icon'"
|
||||
:model-value="getByPath(fForm, item.key)"
|
||||
@update:modelValue="val => setByPath(fForm, item.key, val)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<el-form-item label="是否必填" v-if="fForm.required !== undefined">
|
||||
<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 v-if="fForm.options !== undefined">
|
||||
<el-divider>选项</el-divider>
|
||||
<pi-draggable v-model="fForm.options" item-key="label" :template="{label:'', value:''}">
|
||||
<template #default="scope">
|
||||
<div style="display:flex;">
|
||||
<el-input v-model="scope.element.label" placeholder="label"/>
|
||||
<el-input v-model="scope.element.value" placeholder="value"/>
|
||||
</div>
|
||||
</template>
|
||||
</pi-draggable>
|
||||
</template>
|
||||
<el-divider>正则校验</el-divider>
|
||||
<pi-draggable v-model="fForm.rules" item-key="regex" :template="{regex:'',message:''}">
|
||||
<template #default="scope">
|
||||
<el-form-item label="表达式" prop="regex">
|
||||
<el-input v-model="scope.element.regex"></el-input>
|
||||
<el-form-item label="表达式">
|
||||
<el-input v-model="scope.element.regex"/>
|
||||
</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 label="错误提示">
|
||||
<el-input v-model="scope.element.message"/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</pi-draggable>
|
||||
|
|
@ -119,14 +123,14 @@
|
|||
import {computed, ref} from "vue";
|
||||
import piIcon from "@/components/piIcon"
|
||||
import piDraggable from "@/components/piDraggable"
|
||||
import {fieldEditors, getByPath, setByPath} from "./config"
|
||||
|
||||
let activeName = ref("field")
|
||||
const props = defineProps({
|
||||
curField: {type: Object, default: {}},
|
||||
config: {type: Object, default: {}}
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
let activeName = ref("field")
|
||||
let cForm = computed({
|
||||
get() {
|
||||
return props.config
|
||||
|
|
@ -135,7 +139,6 @@ let cForm = computed({
|
|||
this.$emit('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
let fForm = computed({
|
||||
get() {
|
||||
return props.curField
|
||||
|
|
@ -144,19 +147,21 @@ let fForm = computed({
|
|||
this.$emit('update:modelValue', value)
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.panel {
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue