<template> <div class="form-dynamic"> <template v-if="isShowData"> <div v-if="type === 'form'"> <el-button title="添加" type="primary" icon="el-icon-plus" v-if="addBtn" size="mini" style="margin-bottom: 20px" circle @click="handleAddColumn()" ></el-button> <!-- @mouseenter="$set(row, 'delBtn', true)" @mouseleave="$set(row, 'delBtn', false)" --> <DynamicScroller :items="items" v-if="items && items.length" :min-item-size="54" class="scroll-view" key-field="$_keyField" > <template v-slot="{ item, index, active }"> <DynamicScrollerItem :item="item" :active="active" :data-index="index" :size-dependencies="column" > <div class="form-dynamic-content" v-if="value[index]"> <div v-if="optionsIndex" class="index">#{{ index + 1 }}</div> <div v-if="delBtn && value[index] && value[index].showDelBtn" class="delBtn" > <el-button title="删除" type="danger" icon="el-icon-delete" size="mini" circle @click="handleDelColumn(index)" ></el-button> </div> <template v-for="(c, cIndex) in column"> <div v-if="c.type === 'dynamic'" :key="cIndex" style="width: 100%" class="dynamic-form-container" > <el-form-item :prop="c.prop" :label-width="'0px'" style="display: none" > </el-form-item> <div class="dynamic-form-container_lable"> {{ c.label }} </div> <form-dynamic :options="c.children" :dynamic-data="c.dynamicData" v-model="value[index][c.prop]" :is-show-important="isShowImportant" ></form-dynamic> </div> <el-col v-else-if="c.type === 'title'" :xs="c.xs || 24" :sm="c.sm || 24" :md="c.md || 24" :lg="c.span || 12" :key="columnIndex" v-show="c.display" > <div class="form_title" :style="c.styles"> <span>{{ c.value }}</span> </div> </el-col> <el-col :xs="c.xs || 24" :sm="c.sm || 24" :md="c.md || 24" :lg="c.lg || c.span || 12" :xl="c.span || 12" :key="cIndex" v-else v-show=" c.display && !value[index]['$_hidden' + c.prop] && isShowIm(c.importantField) " > <form-item-self :form="value[index]" type="form" :item="c" :columns="column" ></form-item-self> </el-col> </template> </div> </DynamicScrollerItem> </template> </DynamicScroller> </div> <vxe-table v-else :data="value" :align="headerAlign" resizable border highlight-hover-row max-height="300" :sort-config="{ trigger: 'cell' }" :scroll-y="{ gt: 10 }" class="el-table-self" empty-text="暂无数据" > <!-- :scroll-y="{ gt: 0}" gt为 0 则虚拟滚动总是启用 -1关闭 --> <vxe-column label="add" width="60" v-if="addBtn || delBtn" fixed="left"> <template #header> <el-button type="primary" icon="el-icon-plus" title="添加" v-if="addBtn" size="mini" circle @click="handleAddColumn()" ></el-button> </template> <template #default="{ rowIndex }"> <el-button v-if="delBtn && value[rowIndex] && value[rowIndex].showDelBtn" type="danger" icon="el-icon-delete" size="mini" title="删除" circle @click="handleDelColumn(rowIndex)" ></el-button> <div v-else>{{ rowIndex + 1 }}</div> </template> </vxe-column> <template v-for="(c, cIndex) in column"> <vxe-column :key="cIndex" :title="c.label" :align="align" :min-width="c.width" :class-name="c.required ? 'is-required' : ''" v-if="isShowIm(c.importantField)" :show-overflow="false" > <template #default="{ rowIndex }"> <form-item-self v-show="c.display && !value[rowIndex]['$_hidden' + c.prop]" :form="value[rowIndex]" :columns="column" type="form" :item="c" style="margin: 0" hidden-label ></form-item-self> </template> </vxe-column> </template> </vxe-table> </template> <div v-else>无</div> </div> </template> <script> /** * @description 自定义子表单 */ import FormItemSelf from "./FormItemSelf.vue" import { isObject } from "@/utils/validate" export default { name: "FormDynamic", inject: { vwForm: { default: {}, }, }, props: { options: Object, // 配置数据 dynamicData: Array, //初始数据 value: {}, isShowImportant: { type: Boolean, default: false, }, }, components: { FormItemSelf }, data() { const { addBtn, align, delBtn, headerAlign, column, index, type, } = this.$props.options const initFieldMap = {} column && column.forEach((item) => { if (!item.prop) return false if ( item.type === "daterange" || item.type === "dynamic" || item.type === "checkbox" || item.type === "cascader" || (item.type === "select" && item.multiple) ) { initFieldMap[item.prop] = [] } else if ( item.type === "dental-tab" || item.type === "dental-tab-tj" ) { initFieldMap[item.prop] = {} } else { initFieldMap[item.prop] = item.value || "" } }) return { type, addBtn, align, delBtn, headerAlign, column, optionsIndex: index, initFieldMap, } }, computed: { isShowIm() { return function (val) { return !this.isShowImportant ? true : val } }, isShowData() { const detail = this.vwForm.detail if (!detail) return true return !this.isEmpty(this.value, true) }, items() { return ( this.value && this.value.map((item, index) => { return { ...item, $_keyField: index, } }) ) }, }, methods: { isEmpty(value, flag) { let empty = !value if (value === 0) empty = false else if (Array.isArray(value)) { empty = true if (value.length > 0) { // 有数据 取第一项数据判断是否为空对象 const firstValue = value[0] if (isObject(firstValue)) { const isNotEmpty = Object.keys(firstValue).some((key) => { if (flag) { // 只判断this.column中的数据 if ( this.column.find( (_) => _.type !== "dynamic" && _.prop === key ) ) { return !this.isEmpty(firstValue[key]) } return false } return !this.isEmpty(firstValue[key]) }) empty = !isNotEmpty } else { empty = false } } } return empty }, handleAddColumn() { if (!this.value) { this.setValue([]) } this.$nextTick(() => { this.value.push({ ...this.deepClone(this.initFieldMap), showDelBtn: true, // 手动添加的可删除 }) }) }, handleDelColumn($index) { this.value.splice($index, 1) }, setValue(value) { this.$emit("input", value) }, // 子表单初始化数据 setDynamicData() { if (!this.value || !Array.isArray(this.value)) { this.setValue([]) } if (this.dynamicData && this.dynamicData.length > 0) { this.$nextTick(() => { this.$watch( "value", (value) => { if (!value || value.length === 0) { const data = this.deepClone(this.dynamicData) data.forEach((item) => { const initFieldMap = this.deepClone(this.initFieldMap) for (let k in initFieldMap) { // if (!item[k] && initFieldMap[k]) { // item[k] = initFieldMap[k] // } item[k] = item[k] || initFieldMap[k] } }) this.setValue(data) } // else { // value.forEach((item, index) => { // if (!isObject(item)) { // this.$set(value, index, {}) // } // }) // } }, { immediate: true } ) }) } }, }, created() { this.setDynamicData() }, } </script> <style lang="scss" scoped> ::v-deep .el-table__header .is-required { .cell::before { content: "*"; color: #f56c6c; margin-right: 4px; } } .scroll-view { max-height: 500px; overflow: auto; } .form-dynamic-content { padding: 10px; &:hover { background-color: #ecf8ff; outline: 1px dashed #ccc; } position: relative; display: flex; padding-left: 82px; flex-wrap: wrap; margin-bottom: 20px; .index { font-size: 18px; font-weight: bold; position: absolute; left: 40px; } .delBtn { position: absolute; left: 0; width: 28px; height: 28px; text-align: center; } } .el-table-self { ::v-deep .el-form-item { background: transparent !important; padding: 0 !important; } ::v-deep .vxe-cell { overflow: visible !important; text-overflow: clip !important; white-space: normal !important; .el-radio-group, .el-checkbox-group { text-align: left; } } } </style>