This commit is contained in:
zhang zhuo 2025-12-12 10:17:38 +08:00
parent d91c90a685
commit 1121cb1664
7 changed files with 65 additions and 71 deletions

View File

@ -49,7 +49,7 @@ const value = computed({
} }
}) })
function remove(element, index) { function remove(index) {
value.value.splice(index, 1) value.value.splice(index, 1)
} }

View File

@ -20,10 +20,10 @@
<component :is="'pi-icon-json'"/> <component :is="'pi-icon-json'"/>
</el-icon> </el-icon>
</section> </section>
<el-scrollbar style="height: calc(100% - 20px);" :class="{mobile: data.config.isMobile}"> <el-scrollbar style="height: calc(100% - 30px);" :class="{mobile: data.config.isMobile}">
<el-form class="h100 pad10" :label-position="data.config.labelPosition" <el-form class="h100 pad10" :label-position="data.config.labelPosition"
:label-width="data.config.labelWidth" :size="data.config.size" :disabled="data.config.disabled"> :label-width="data.config.labelWidth" :size="data.config.size" :disabled="data.config.disabled">
<drag :data="data" name="page"/> <drag :data="data" name="page" :field="field"/>
</el-form> </el-form>
</el-scrollbar> </el-scrollbar>
</section> </section>
@ -39,7 +39,8 @@ import FormBuild from "./formBuild"
import api from "@/api/index.js"; import api from "@/api/index.js";
const props = defineProps({ const props = defineProps({
data: {type: FormBuild, default: []} data: {type: FormBuild, default: []},
field: {type: Object, default: {}}
}) })
const {t} = useI18n() const {t} = useI18n()
const {proxy} = getCurrentInstance() const {proxy} = getCurrentInstance()
@ -58,7 +59,10 @@ function empty() {
ElMessageBox.confirm("确定要清空所有表单数据吗", t('system.warning'), { ElMessageBox.confirm("确定要清空所有表单数据吗", t('system.warning'), {
confirmButtonText: t('system.ok'), cancelButtonText: t('system.cancel'), type: 'warning' confirmButtonText: t('system.ok'), cancelButtonText: t('system.cancel'), type: 'warning'
}).then(() => { }).then(() => {
tools.data.remove("FORM-FIELDS")
tools.data.remove("FORM-CONFIG")
props.data.restData() props.data.restData()
proxy.$message.success("清空成功")
}).catch(() => { }).catch(() => {
}) })
} }

View File

@ -57,6 +57,7 @@ export const fieldEditors = {
{key: 'props.placeholder', label: '占位提示', type: 'input'}, {key: 'props.placeholder', label: '占位提示', type: 'input'},
{key: 'props.multiple', label: '是否多选', type: 'switch'}, {key: 'props.multiple', label: '是否多选', type: 'switch'},
{key: 'props.filterable', label: '能否筛选', type: 'switch'}, {key: 'props.filterable', label: '能否筛选', type: 'switch'},
{key: 'props.clearable', label: '能否清空', type: 'switch'},
{key: 'props.disabled', label: '是否禁用', type: 'switch'}, {key: 'props.disabled', label: '是否禁用', type: 'switch'},
], ],
cascader: [ cascader: [
@ -175,7 +176,6 @@ export const fieldEditors = {
], ],
time: [ time: [
{key: 'props.editable', label: '可输入', type: 'switch'}, {key: 'props.editable', label: '可输入', type: 'switch'},
{key: 'props.rangeSeparator', label: '分隔符', type: 'input'},
{key: 'props.clearable', label: '能否清空', type: 'switch'}, {key: 'props.clearable', label: '能否清空', type: 'switch'},
{key: 'props.readonly', label: '是否只读', type: 'switch'}, {key: 'props.readonly', label: '是否只读', type: 'switch'},
{key: 'props.disabled', label: '是否禁用', type: 'switch'}, {key: 'props.disabled', label: '是否禁用', type: 'switch'},

View File

@ -4,15 +4,14 @@
ghostClass="ghostClass" :class="{empty: data.fields.length === 0, layout: isRow}"> ghostClass="ghostClass" :class="{empty: data.fields.length === 0, layout: isRow}">
<template #item="{ element, index }"> <template #item="{ element, index }">
<el-col :span="element.span || 24" class="col"> <el-col :span="element.span || 24" class="col">
<el-row v-if="element.name === 'layout'" class="item row" :class="{'active': field.id===element.id}" <el-row v-if="element.name === 'layout'" class="item row" :class="{'active': field.element.id===element.id}"
@click.stop="clickComp(element)" v-bind="element.props"> @click.stop="clickComp(element)" v-bind="element.props">
<span class="name">{{ element.field_name }}</span> <span class="name">{{ element.field_name }}</span>
<drag :fields="element.children" :config="data.config" :data <drag :data="getChildrenData(element.children)" :field="field" :name="element.field_name" :is-row="true"/>
:name="element.field_name" :is-row="true"/>
</el-row> </el-row>
<item v-else class="item" :class="{'active': field.id===element.id}" :field="element" <item v-else class="item" :class="{'active': field.element.id===element.id}" :field="element"
@click.stop="clickComp(element)"/> @click.stop="clickComp(element)"/>
<div v-if="field.id===element.id" class="tools-box"> <div v-if="field.element.id===element.id" class="tools-box">
<el-icon size="22" class="icon-box remove" @click.stop="delComp(index)"> <el-icon size="22" class="icon-box remove" @click.stop="delComp(index)">
<component :is="'pi-icon-shan-chu'"/> <component :is="'pi-icon-shan-chu'"/>
</el-icon> </el-icon>
@ -27,7 +26,7 @@
<script setup> <script setup>
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
import {computed, nextTick} from "vue" import {nextTick, reactive} from "vue"
import item from "./item.vue" import item from "./item.vue"
import drag from "./drag.vue" import drag from "./drag.vue"
import tools from "@/utils/tools" import tools from "@/utils/tools"
@ -35,21 +34,19 @@ import FormBuild from "./formBuild"
const props = defineProps({ const props = defineProps({
data: {type: FormBuild, default: {}}, data: {type: FormBuild, default: {}},
field: {type: Object, default: {}},
curKey: {type: String, default: ''}, curKey: {type: String, default: ''},
isRow: {type: Boolean, default: false}, isRow: {type: Boolean, default: false},
name: {type: String, default: 'page'} name: {type: String, default: 'page'}
}) })
const group = {name: props.name, pull: true, put: true} const group = {name: props.name, pull: true, put: true}
const field = computed(() => {
return props.data.activeField || {}
})
// //
function sortComp(e) { function sortComp(e) {
let element = props.data.fields[e.newIndex] let element = props.data.fields[e.newIndex]
if (element) { if (element) {
props.data.setActiveField(element) props.field.element = element
} }
} }
@ -59,15 +56,15 @@ function addComp(e) {
if (props.isRow) { if (props.isRow) {
element.span = 12 element.span = 12
} }
props.data.setActiveField(element) props.field.element = element
} }
// //
function clickComp(element) { function clickComp(element) {
if (field.value.id === element.id) { if (props.field.element.id === element.id) {
return; return;
} }
props.data.setActiveField(element) props.field.element = element
} }
// //
@ -86,8 +83,7 @@ function delComp(index) {
i = index i = index
} }
} }
let element = props.data.fields[i] || {} props.field.element = props.data.fields[i] || {}
props.data.setActiveField(element)
}) })
} }
@ -101,8 +97,7 @@ function copyComp(index) {
props.data.fields.push(tmp) props.data.fields.push(tmp)
// //
tools.array.zIndexTo(props.data.fields, props.data.fields.length - 1, index + 1) tools.array.zIndexTo(props.data.fields, props.data.fields.length - 1, index + 1)
let element = props.data.fields[index + 1] props.field.element = props.data.fields[index + 1]
props.data.setActiveField(element)
}) })
} }
@ -111,6 +106,12 @@ function increaseNum(str) {
return props.data.config.counter.toString(); return props.data.config.counter.toString();
}); });
} }
function getChildrenData(_data) {
const _form = reactive(new FormBuild())
_form.initData(props.data.config, _data)
return _form
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -15,8 +15,6 @@ export default class FormBuild {
config: Config config: Config
// 字段信息 // 字段信息
fields: Array<any> fields: Array<any>
// 激活字段
activeField: Object
constructor() { constructor() {
this.config = { this.config = {
@ -31,7 +29,6 @@ export default class FormBuild {
counter: 0 counter: 0
} }
this.fields = [] this.fields = []
this.activeField = null
} }
restData() { restData() {
@ -53,8 +50,4 @@ export default class FormBuild {
this.config = config this.config = config
this.fields = fields this.fields = fields
} }
setActiveField(field: Object) {
this.activeField = field
}
} }

View File

@ -1,8 +1,8 @@
<template> <template>
<el-container class="pi-panel"> <el-container class="pi-panel">
<left-panel :data="formData"></left-panel> <left-panel :data="formData"></left-panel>
<center-panel ref="centerRef" :data="formData"></center-panel> <center-panel ref="centerRef" :data="formData" :field="field"></center-panel>
<right-panel :data="formData" :fieldd="field"></right-panel> <right-panel :data="formData" :field="field"></right-panel>
</el-container> </el-container>
</template> </template>
@ -19,9 +19,8 @@ defineOptions({
}) })
const centerRef = ref(null) const centerRef = ref(null)
let formData = reactive(new FormBuild()) let formData = reactive(new FormBuild())
let field = ref({}) let field = reactive({element: {}})
onMounted(() => { onMounted(() => {
const fields = tools.data.get("FORM-FIELDS") const fields = tools.data.get("FORM-FIELDS")
@ -30,10 +29,6 @@ onMounted(() => {
formData.initData(config, fields) formData.initData(config, fields)
} }
}) })
function activeField(v) {
field.value = v
}
</script> </script>
<style scoped> <style scoped>

View File

@ -3,80 +3,80 @@
<el-scrollbar> <el-scrollbar>
<el-tabs v-model="activeName" stretch> <el-tabs v-model="activeName" stretch>
<el-tab-pane label="组件属性" name="field"> <el-tab-pane label="组件属性" name="field">
<el-form :model="field" label-width="90px"> <el-form :model="form" label-width="90px">
<el-form-item v-if="field.name !== 'button' && field.name !== 'layout'" label="字段名" <el-form-item v-if="form.name !== 'button' && form.name !== 'layout'" label="字段名"
prop="field_name"> prop="field_name">
<el-input v-model="field.field_name"></el-input> <el-input v-model="form.field_name"></el-input>
</el-form-item> </el-form-item>
<el-form-item v-if="field.name !== 'layout'" label="标题" prop="title"> <el-form-item v-if="form.name !== 'layout'" label="标题" prop="title">
<el-input v-model="field.title"></el-input> <el-input v-model="form.title"></el-input>
</el-form-item> </el-form-item>
<el-form-item v-if="field.name !== 'button' && field.name !== 'layout'" label="默认值" <el-form-item v-if="form.name !== 'button' && form.name !== 'layout'" label="默认值"
prop="value"> prop="value">
<el-input v-model="field.value"></el-input> <el-input v-model="form.value"></el-input>
</el-form-item> </el-form-item>
<el-form-item v-if="field.span !== undefined" label="栅格列数" prop="span"> <el-form-item v-if="form.span !== undefined" label="栅格列数" prop="span">
<el-slider v-model="field.span" :min="1" :max="24"/> <el-slider v-model="form.span" :min="1" :max="24"/>
</el-form-item> </el-form-item>
<template v-for="item in fieldEditors[field.name] || []" :key="item.key"> <template v-for="item in fieldEditors[form.name] || []" :key="item.key">
<el-form-item :label="item.label"> <el-form-item :label="item.label">
<el-input <el-input
v-if="item.type === 'input'" v-if="item.type === 'input'"
:model-value="getByPath(field, item.key)" :model-value="getByPath(form, item.key)"
@input="val => setByPath(field, item.key, val)" @input="val => setByPath(form, item.key, val)"
clearable clearable
/> />
<el-input <el-input
v-else-if="item.type === 'number'" v-else-if="item.type === 'number'"
type="number" type="number"
:model-value="getByPath(field, item.key)" :model-value="getByPath(form, item.key)"
@input="val => setByPath(field, item.key, Number(val))" @input="val => setByPath(form, item.key, Number(val))"
/> />
<el-switch <el-switch
v-else-if="item.type === 'switch'" v-else-if="item.type === 'switch'"
:model-value="getByPath(field, item.key)" :model-value="getByPath(form, item.key)"
@change="val => setByPath(field, item.key, val)" @change="val => setByPath(form, item.key, val)"
/> />
<el-color-picker v-else-if="item.type === 'color'" show-alpha <el-color-picker v-else-if="item.type === 'color'" show-alpha
:model-value="getByPath(field, item.key)" :model-value="getByPath(form, item.key)"
@change="val => setByPath(field, item.key, val)"/> @change="val => setByPath(form, item.key, val)"/>
<el-radio-group v-else-if="item.type === 'radio'" <el-radio-group v-else-if="item.type === 'radio'"
:model-value="getByPath(field, item.key)" :model-value="getByPath(form, item.key)"
@change="val => setByPath(field, item.key, val)"> @change="val => setByPath(form, item.key, val)">
<el-radio-button v-for="op in item.options" :key="op.value" <el-radio-button v-for="op in item.options" :key="op.value"
:value="op.value">{{ op.label }} :value="op.value">{{ op.label }}
</el-radio-button> </el-radio-button>
</el-radio-group> </el-radio-group>
<el-select v-else-if="item.type === 'select'" :model-value="getByPath(field, item.key)" <el-select v-else-if="item.type === 'select'" :model-value="getByPath(form, item.key)"
@change="val => setByPath(field, item.key, val)"> @change="val => setByPath(form, item.key, val)">
<el-option v-for="op in item.options" :key="op.value" :value="op.value" <el-option v-for="op in item.options" :key="op.value" :value="op.value"
:label="op.label"> :label="op.label">
</el-option> </el-option>
</el-select> </el-select>
<pi-icon <pi-icon
v-else-if="item.type === 'icon'" v-else-if="item.type === 'icon'"
:model-value="getByPath(field, item.key)" :model-value="getByPath(form, item.key)"
@update:modelValue="val => setByPath(field, item.key, val)" @update:modelValue="val => setByPath(form, item.key, val)"
/> />
</el-form-item> </el-form-item>
</template> </template>
<el-form-item label="是否必填" v-if="field.required !== undefined"> <el-form-item label="是否必填" v-if="form.required !== undefined">
<el-switch v-model="field.required"/> <el-switch v-model="form.required"/>
</el-form-item> </el-form-item>
<template v-if="field.options !== undefined"> <template v-if="form.options !== undefined">
<el-divider>选项</el-divider> <el-divider>选项</el-divider>
<pi-draggable v-model="field.options" item-key="label" :template="{label:'', value:''}"> <pi-draggable v-model="form.options" item-key="label" :template="{label:'', value:''}">
<template #default="scope"> <template #default="scope">
<div style="display:flex;"> <div style="display:flex;gap: 5px;">
<el-input v-model="scope.element.label" placeholder="label"/> <el-input v-model="scope.element.label" placeholder="label"/>
<el-input v-model="scope.element.value" placeholder="value"/> <el-input v-model="scope.element.value" placeholder="value"/>
</div> </div>
</template> </template>
</pi-draggable> </pi-draggable>
</template> </template>
<template v-if="field.name !== 'button' && field.name !== 'layout'"> <template v-if="form.name !== 'button' && form.name !== 'layout'">
<el-divider>正则校验</el-divider> <el-divider>正则校验</el-divider>
<pi-draggable v-model="field.rules" item-key="regex" :template="{regex:'',message:''}"> <pi-draggable v-model="form.rules" item-key="regex" :template="{regex:'',message:''}">
<template #default="scope"> <template #default="scope">
<el-form-item label="表达式"> <el-form-item label="表达式">
<el-input v-model="scope.element.regex"/> <el-input v-model="scope.element.regex"/>
@ -136,11 +136,12 @@ import FormBuild from "./formBuild";
const emit = defineEmits(['update:field']) const emit = defineEmits(['update:field'])
const props = defineProps({ const props = defineProps({
data: {type: FormBuild, default: {}} data: {type: FormBuild, default: {}},
field: {type: Object, default: {}}
}) })
let activeName = ref("field") let activeName = ref("field")
const field = computed(() => { let form = computed(() => {
return props.data.activeField || {} return props.field.element
}) })
</script> </script>