<template>
<el-form
ref="elform"
:model="formData"
:inline="inline"
:label-width="formLabelWidth"
:size="size"
v-bind="$attrs"
:rules="rules"
v-on="$listeners"
@submit.native.prevent
>
<!-- {{ notebookAndModuleInfo }} -->
<!-- {{ formWidth }} -->
<!-- {{ autoLayout }}
{{ isAutoComputedColumns }}
{{ Object.keys(formView).length }}
{{ columns }}
{{ itemcolumns }} -->
<!-- {{ checkboxMultiple }} -->
<!-- {{ formView }} -->
<!-- {{ formData }} -->
<!-- {{ rules }} -->
<!-- {{ proxyMethods }} -->
<!-- {{ tableRowIndex }} -->
<!-- {{ groupCodeOptions }} -->
<draggable
:group="dragGroup"
animation="300"
:scroll="true"
@end="dragEnd"
ghostClass="ghost"
:disabled="!draggable"
:class="[layoutClass, hiddenLabelClass, labelPosition ? `el-form--label-${labelPosition}` : '', dragFormClass]"
:style="styleObj"
>
<!-- :label="item.itemlabel" -->
<template v-for="(item, key) in formView">
<el-form-item
v-if="
item.showInEditForm ||
(typeof item.hidden === 'function'
? !item.hidden({
item,
formView,
formData,
editTableRowIndex,
tableRowIndex
})
: !item.hidden &&
!hiddenKeys.includes(item.prop || key) &&
!hiddenTypes.includes(item.itemtype) &&
item?.itemAttr != 'hidden')
"
v-show="(item.showInEditForm || item.show) ?? true"
:key="item.key || key"
:prop="item.prop || key"
:show-message="item.showMessage"
:item-key="key"
:label-width="item.labelWidth"
:label="$t(item.itemlabel) || item.itemlabel"
:readonly="item.readonly"
:class="[
{
selectedFormItem: selectedKey === key,
hiddenLabelClass: item.hiddenLabel,
itemIsDisabled:
typeof item.itemDisabled === 'function'
? item.itemDisabled({
item,
formView,
formData,
editTableRowIndex,
tableRowIndex
})
: item.itemDisabled,
contentOverHidden: ['Table'].includes(item.itemtype) ? true : false
},
item.itemClassName
]"
@click.native.stop="
handleClickFormItem({
key,
item,
formView,
formData,
editTableRowIndex,
tableRowIndex
})
"
:style="[setStyle(item, key), item.itemStyle]"
>
<!-- TODO注意key和prop,要考虑具体绑定的值和验证的值在一些场景是否会区分 -->
<!--TODO需要插件语法支持,默认为 :clearable="item.clearable ?? true" -->
<!-- <template slot="label" v-if="!item.hiddenLabel">
{{ item.itemlabel }}
</template> -->
<template #label v-if="item.customFormLabel">
<div>
<span>{{ item.itemlabel }}</span>
<el-tooltip :content="item.formLabelContent" placement="top">
<i class="el-icon-question" style="color: #e6a23c; margin-left: 2px" v-if="item.formLabelContent"></i>
</el-tooltip>
</div>
</template>
<span slot="label" v-if="item.clickable">
<span :class="item.clickable ? 'clickable' : ''">{{ $t(item.i18nkey || item.itemlabel) || ' ' }}</span>
</span>
<!-- ReadonlyInput类型仅仅用来展示,需要配合bindkey一起使用 -->
<el-input
v-if="item.itemtype === 'ReadonlyInput'"
v-model="formData[item.bindkey]"
v-bind="item"
v-on="item"
:placeholder="$t(item.placeholder || `${$t('请输入')} ${$t(item.label || item.itemlabel || '')}`)"
:title="formData[item.bindkey] ?? ''"
>
</el-input>
<el-input
v-else-if="item.itemtype === 'Input'"
:maxlength="item.verify && !item?.needMaxLength ? false : item.maxlength || 255"
:show-word-limit="item.showWordLimit === false || !item.maxlength ? false : true"
ref="formInput"
:id="item?.ref || 'formInput'"
v-model="formData[key]"
v-bind="item"
v-on="item"
v-verify="item.verify"
:clearable="item.clearable ?? true"
:readonly="item?.readonly || item?.isReadonlyInputView"
@clear="handleClear"
@click.native="clickHandler(key, item)"
@keyup.enter.native="keyupEnterHandler(key, item)"
:placeholder="
$t(
item.placeholder ||
`${showPlaceholderPrefix ? $t('请输入') : ''} ${$t(item.label || item.itemlabel || '')}`
)
"
:title="item.itemlabel && !item.itemlabel.includes('密码') && formData[key] ? formData[key] : ''"
:class="item.showWordLimit === false || !item.maxlength ? '' : 'showWordLimitInput'"
>
<component :is="item.prefix" slot="prefix" v-if="item.prefix"></component>
<component :is="item.suffix" slot="suffix" v-if="item.suffix"></component>
<component
:is="item.prepend"
slot="prepend"
v-if="item.prepend"
v-bind="item.prependAttrs"
v-on="item.prependListeners"
></component>
<component :is="item.append" slot="append" v-if="item.append"></component>
<template v-if="item.child">
{{ typeof item.child === 'function' ? item.child({ key, formData, itemConfig: item }) : item.child }}
</template>
<template slot="append" v-if="item.appendTemplate">
{{
typeof item.appendTemplate === 'function'
? item.appendTemplate({ key, formData, itemConfig: item })
: item.appendTemplate
}}
</template>
</el-input>
<el-input
v-else-if="item.itemtype === 'Password'"
v-model="formData[key]"
type="password"
autocomplete="new-password"
:maxlength="item.verify ? false : item.maxlength || 255"
:show-word-limit="item.showWordLimit === false || !item.maxlength ? false : true"
:placeholder="$t(item.placeholder || `${$t('请输入')} ${$t(item.label || item.itemlabel || '')}`)"
>
<!-- <i style="cursor: pointer; line-height: 30px" slot="suffix" :class="iconClass" @click="showPwd(key)"></i>-->
</el-input>
<toggle-password v-else-if="item.itemtype === 'TogglePassword'" v-model="formData[key]" v-bind="item">
</toggle-password>
<el-button
v-else-if="item.itemtype === 'Button'"
v-bind="item"
:menuCode="item.menuCode"
:disabled="
typeof item.disabled === 'function'
? item.disabled({
item,
formView,
formData,
editTableRowIndex,
tableRowIndex
})
: item.disabled
"
@click="item.click({ key, tableRowIndex, tableColIndex, formData })"
>
{{
typeof item.buttonText === 'function'
? item.buttonText({ key, tableRowIndex, tableColIndex, formData })
: $t(item.buttonText)
}}
</el-button>
<template v-else-if="item.itemtype === 'NativeFile'">
<!-- <input type="file" @change="item.change && item.change($event)" class="nativeFile" /> -->
<input
ref="nativeFile"
type="file"
id="nativeFile"
style="display: none"
@change="nativeFileChange(item, $event)"
/>
<input ref="nativeFileName" id="nativeFileName" readonly />
<input
type="button"
:value="$t('选择文件')"
style="border-radius: 2px; padding: 0 4px; margin-left: 6px"
@click="nativeFileClick"
/>
</template>
<template v-else-if="item.itemtype === 'Autocomplete'">
<el-autocomplete
v-bind="item"
v-on="item"
v-model="formData[key]"
:title="formData[key]"
@select="changeAutoComplete()"
>
<template slot-scope="{ item }">
<span>{{ item.label || item.value }}</span>
</template>
</el-autocomplete>
</template>
<template v-else-if="item.itemtype === 'CustomAutocomplete'">
<customAutocomplete
:formData="formData"
:itemkey="key"
:formItem="item"
v-model="formData[key]"
:title="formData[key]"
>
</customAutocomplete>
</template>
<el-input
v-else-if="item.itemtype === 'SelectInput'"
v-model="formData[key]"
v-bind="item"
v-on="item"
v-verify="item.verify"
clearable
@clear="handleClear"
:placeholder="$t(item.placeholder || `${$t('请输入')} ${$t(item.label || item.itemlabel || '')}`)"
:title="formData[key]"
>
<!-- ReadonlyInput类型仅仅用来展示,需要配合bindkey一起使用 -->
<template v-if="item.selectConfig.itemtype === 'ReadonlyInput'">
<span :slot="item.selectConfig.slot || 'append'">{{ formData[item.selectConfig.bindkey] }}</span>
</template>
<template v-else>
<el-select
v-if="item.selectConfig"
v-model="formData[item.selectConfig.prop]"
:slot="item.selectConfig.slot || 'append'"
:style="[{ minWidth: '80px' }, item.selectConfig.itemStyle]"
:placeholder="$t('请选择')"
v-bind="item.selectConfig"
@clear="handleClear(item, key)"
@change="handleChangeSelectInput(item.selectConfig, item.selectConfig.prop)"
>
<el-option
v-for="(selitem, index) in item.selectConfig.groupCode
? groupCodeOptions[item.selectConfig.groupCode]
: item.selectConfig.options"
:key="index"
:label="selitem.label"
:value="selitem[item.selectConfig.bindValueField || 'value' || 'label']"
:disabled="selitem.disabled"
@click.native="handleSelect(selitem, key, item)"
></el-option>
</el-select>
</template>
</el-input>
<template v-else-if="item.itemtype === 'Select'">
<div
v-if="item.multiple"
v-selectloadmore="item.infiniteScrollMethods ? item.infiniteScrollMethods : ''"
style="width: 100%; display: grid"
>
<el-select
v-model="checkboxMultiple[key]"
v-bind="item"
v-on="item"
:collapse-tags="item.collapseTags ? true : false"
ref="SelectMultiple"
:placeholder="$t(item.placeholder || `${$t('请选择')} ${$t(item.label || item.itemlabel || '')}`)"
:clearable="item.clearable ?? true"
:filterable="item.filterable ?? true"
:title="renderTitle('select', { key, item })"
@clear="handleClear(item, key)"
>
<li
class="el-select-dropdown__item"
:class="{
selected: selmultiAll(key, 2),
optionBtnType: item.optionBtnType
}"
style="user-select: none"
v-if="item.multiple"
@click="selmultiAll(key, 1, item, item.groupCode ? groupCodeOptions[item.groupCode] : item.options)"
>
<span>{{ selmultiAll(key, 2) ? $t('全不选') : $t('全选') }}</span>
</li>
<template v-if="item.slotTitle">
<li v-html="item.slotTitle" class="el-select-dropdown__item" style="user-select: none"></li>
</template>
<el-option
v-for="(selitem, index) in item.groupCode ? groupCodeOptions[item.groupCode] : item.options"
:key="selitem.value + index"
:label="selitem.label"
:value="selitem[item.bindValueField || 'value' || 'label']"
:disabled="selitem.disabled ?? (selitem.optionDisabled && selitem.optionDisabled(selitem))"
@click.native="
selectOptionsRow(
key,
selitem,
item,
item.groupCode ? groupCodeOptions[item.groupCode] : item.options
)
"
>
<div v-if="item.slotHtml" v-html="item.slotHtml(selitem)"></div>
</el-option>
</el-select>
</div>
<div
v-else
v-selectloadmore="item.infiniteScrollMethods ? item.infiniteScrollMethods : ''"
style="width: 100%; display: grid"
>
<el-select
@focus="handleFocus(item, key)"
:ref="'selectSingle' + key"
v-model="formData[key]"
v-bind="item"
v-on="item"
:placeholder="$t(item.placeholder || `${$t('请选择')} ${$t(item.label || item.itemlabel || '')}`)"
:clearable="item.clearable ?? true"
:filterable="item.filterable ?? true"
:title="renderTitle('select', { key, item })"
@clear="handleClear(item, key)"
:bindKey="key"
@filter-change="handleFilterChange"
@visible-change="item.shouldInput ? selectFocus(key) : item.visibleChange"
>
<template v-if="item.slotTitle">
<li v-html="item.slotTitle" class="el-select-dropdown__item" style="user-select: none"></li>
</template>
<template v-if="item.customOption">
<li
:style="item.customOption.style || ''"
@click="item.customOption.click && item.customOption.click($refs['selectSingle' + key][0])"
>
<span> {{ item.customOption.label }} </span>
</li>
<el-option
v-if="
typeof item.options === 'function'
? item.options(key, formData, formView) && item.options(key, formData, formView).length === 0
: item.options && item.options.length === 0
"
style="height: 0"
:value="''"
@click.native="selectOptionsRow(key, selitem, item)"
></el-option>
</template>
<el-option
v-for="(selitem, index) in typeof item.options === 'function'
? item.options(key, formData, formView)
: item.groupCode
? groupCodeOptions[item.groupCode]
: item.options"
:key="key + index"
:optionData="selitem"
:filterVal="selectFilterObj[key]"
:customFilterMethod="item.customFilterMethod"
:label="selitem[item.bindLabelField || 'label' || 'value']"
:value="selitem[item.bindValueField || 'value' || 'label']"
:disabled="selitem.disabled"
@click.native="selectOptionsRow(key, selitem, item)"
>
<span v-if="selitem.html" v-html="selitem.html"></span>
<div v-if="item.slotHtml" v-html="item.slotHtml(selitem)"></div>
</el-option>
</el-select>
</div>
</template>
<!-- todo支持多选和下拉多选返回的值的类型,数组或者逗号隔开的字符串 -->
<template v-else-if="item.itemtype === 'Checkbox'">
<span
v-if="item?.class === 'custom_check_box' || item?.class === 'custom_label'"
:style="{ 'font-size': '12px', color: formData[key] ? 'red' : '' }"
>{{ formData[key] ? $t('是') : $t('否') }}</span
>
<el-checkbox
v-else
v-model="formData[key]"
v-bind="item"
v-on="item"
:title="formData[key]"
:disabled="
typeof item.disabled === 'function'
? item.disabled({
item,
formView,
formData,
editTableRowIndex,
tableRowIndex
})
: item.disabled
"
>
{{ item.label }}
</el-checkbox>
</template>
<el-checkbox-group
v-else-if="item.itemtype === 'CheckboxGroup'"
v-model="checkboxMultiple[key]"
v-bind="item"
v-on="item"
:title="formData[key]"
>
<template v-if="checkboxMultiple[key]">
<el-checkbox
v-for="(checkitem, checkboxindex) in item.options"
:label="checkitem.value"
:disabled="checkitem.disabled"
:key="checkboxindex"
>
{{ checkitem.label }}
</el-checkbox>
</template>
</el-checkbox-group>
<el-radio-group
v-else-if="item.itemtype === 'Radio'"
v-model="formData[key]"
v-bind="item"
v-on="item"
:title="formData[key]"
>
<el-radio
v-for="(radioItem, index) in getSelectOptions(item, item.params)"
:label="radioItem.value"
:key="index"
>
{{ $t(radioItem.label) }}
</el-radio>
</el-radio-group>
<el-switch
v-else-if="item.itemtype === 'Switch'"
v-model="formData[key]"
v-bind="item"
style="width: min-content"
v-on="item"
:title="formData[key]"
></el-switch>
<el-date-picker
v-else-if="item.itemtype === 'DatePicker'"
:type="item.type || 'date'"
:value-format="item.format || 'yyyy-MM-dd'"
:placeholder="$t(item.placeholder) || $t('请选择日期')"
:start-placeholder="$t('开始日期')"
:end-placeholder="$t('结束日期')"
v-model="formData[key]"
v-bind="item"
v-on="item"
:title="formData[key]"
:picker-options="item.pickerOptions ?? datePickerOptions"
@clear="handleClear"
></el-date-picker>
<el-date-picker
v-else-if="item.itemtype === 'DateTimePicker'"
:type="item.type || 'datetime'"
:value-format="item.format || 'yyyy-MM-dd'"
:placeholder="item.placeholder || $t('请选择日期')"
v-model="formData[key]"
v-bind="item"
v-on="item"
:title="formData[key]"
:picker-options="item.pickerOptions ?? datePickerOptions"
@clear="handleClear"
></el-date-picker>
<el-time-picker
v-else-if="item.itemtype === 'TimePicker'"
:placeholder="item.placeholder || $t('请选择时间')"
:value-format="item.format || 'HH:mm:ss'"
v-model="formData[key]"
v-bind="item"
v-on="item"
:title="formData[key]"
@clear="handleClear"
></el-time-picker>
<el-button-group v-else-if="item.itemtype === 'ButtonGroup'">
<el-button
v-for="(buttonItem, buttonIndex) in item.options"
@click="formData[key] = buttonItem.value"
:type="formData[key] == buttonItem.value ? item.checkedType || 'primary' : buttonItem.type"
:key="buttonIndex"
:title="formData[key]"
>
{{ buttonItem.label }}
</el-button>
</el-button-group>
<el-dropdown v-else-if="item.itemtype === 'Dropdown'" v-bind="item" v-on="item">
<span class="el-dropdown-link"> {{ item.text }}<i class="el-icon-arrow-down el-icon--right"></i> </span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="dropdownItem in item.options" :key="dropdownItem.label || dropdownItem.value">
{{ dropdownItem.label || dropdownItem.value }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-input
v-else-if="item.itemtype === 'Textarea'"
type="textarea"
show-word-limit
:rows="item.rows || 3"
v-model="formData[key]"
v-bind="item"
v-on="item"
:title="formData[key]"
:placeholder="$t(item.placeholder || `${$t('请输入')} ${$t(item.label || item.itemlabel || '')}`)"
/>
<el-input-number
v-else-if="item.itemtype === 'InputNumber' && !item.isNativeAction"
v-model="formData[key]"
v-bind="item"
v-on="item"
:title="formData[key]"
@clear="handleClear"
:max="item.max || 1000000000"
></el-input-number>
<el-input-number
v-else-if="item.itemtype === 'InputNumber' && item.isNativeAction"
v-model="formData[key]"
v-bind="item"
:title="formData[key]"
@input.native="item.input"
:max="item.max || 1000000000"
@clear="handleClear"
></el-input-number>
<el-divider v-else-if="item.itemtype === 'Divider'" v-bind="item"
><span v-html="item.content"></span
></el-divider>
<selectTree
v-else-if="item.itemtype === 'SelectTree'"
v-bind="item"
v-on="item"
:formData="formData"
v-model="formData[key]"
:data="item.groupCode ? getSelectOptions(item) : item.data"
></selectTree>
<el-color-picker
v-else-if="item.itemtype === 'ColorPicker'"
v-bind="item"
v-on="item"
v-model="formData[key]"
></el-color-picker>
<!-- <el-upload v-else-if="item.itemtype === 'Upload'" class="my-el-upload" v-bind="item" v-on="item">
<span style="cursor:pointer" class="el-icon-upload2"></span>
<div slot="tip" class="el-upload__tip" v-if="item.tip">{{ item.tip }}</div>
</el-upload> -->
<custom-file
v-else-if="item.itemtype === 'CustomFile'"
v-bind="item"
v-on="item"
v-model="formData[key]"
></custom-file>
<upload-file
v-else-if="item.itemtype === 'UploadFile'"
v-bind="item"
v-on="item"
v-model="formData[key]"
></upload-file>
<page-select
v-else-if="item.itemtype === 'PageSelect'"
style="width: 100%"
v-bind="item"
v-on="item"
:modelval.sync="formData[key]"
:formData="formData"
></page-select>
<mingdu-form
v-else-if="item.itemtype === 'SlotForm'"
class="slotform"
:form-data="item.formData ? formData[item.formData] : formData"
:form-view="item.formView"
v-bind="item.slotFormConfig"
ref="SlotForm"
></mingdu-form>
<template v-else-if="item.itemtype === 'Table'">
<mingdu-table v-bind="item" :data="formData[key]" v-on="item" :ref="key + 'Mdtable'"></mingdu-table>
</template>
<!-- 配置选项 -->
<template v-else-if="item.itemtype === 'ConfigOptions'">
<config-options v-model="formData[key]"></config-options>
</template>
<template v-else-if="item.itemtype === 'TableConfigOptions'">
<table-config-options v-model="formData[key]"></table-config-options>
</template>
<template v-else-if="item.itemtype === 'CustomElement'">
<slot
v-if="item.slotName"
:name="item.slotName"
:form-item="item"
:form-data="formData"
:prop-name="key"
></slot>
<div
v-else-if="item.html"
v-html="
typeof item.html === 'function'
? item.html({
formItem: item,
formView,
formData,
propName: key
})
: item.html
"
></div>
<component
:formItem="item"
:formView="formView"
:formData="formData"
:propName="key"
v-on="item.emitMethods"
v-bind="item"
:is="item.component"
v-else-if="item.component"
></component>
<component
:scope="{ formItem: item, formView, formData, propName: key }"
v-bind="item"
:is="
typeof item.render === 'function'
? handleRendel(item.render, {
formItem: item,
formView,
formData,
propName: key
})
: item.render
"
v-else-if="item.render"
></component>
</template>
<el-input
class="dialogSelect"
v-else-if="item.itemtype === 'DialogSelect'"
v-model="formData[`${key}_label`]"
v-bind="item"
v-on="item"
@focus="openSelectDialog(item, key)"
@input="inputDialogSelect($event, item, key)"
id="dialogSelectInput"
:placeholder="$t(item.placeholder || `${$t('请选择')} ${$t(item.label || item.itemlabel || '')}`)"
>
<el-button
slot="append"
icon="el-icon-circle-close"
v-if="!item.required"
@click="dialogSelectClear(item, key)"
></el-button>
<el-button
slot="append"
icon="el-icon-folder-opened"
v-if="item.shouldInput"
@click="openSelectDialog(item, key, 'click')"
></el-button>
</el-input>
<!-- 仅显示文本 -->
<span v-else-if="item.itemtype === 'Text'" :class="item.class || ''">{{
item.formatter && typeof item.formatter === 'function'
? item.formatter(formData[key], {
formItem: item,
formView,
formData,
propName: key
})
: formData[key]
}}</span>
<span v-else>{{ formData[key] }}</span>
<el-button
v-if="selectedKey === key"
@click.stop="deleteSelectedKeyEvent(key)"
class="deleteSelectedKey"
type="danger"
icon="el-icon-delete"
></el-button>
</el-form-item>
</template>
<slot name="rightContent"></slot>
<!-- <el-button type="primary" style="width: 100px" v-if="formKey" @click="$saveFormView(formView, formKey)"
>保存表单配置</el-button
> -->
</draggable>
<!-- 选择用户 -->
<SelectUser
ref="refSelectUser"
:defaultCheckedList="defaultCheckedUserList"
@on-select="handleSelectUser"
></SelectUser>
<!-- 库位选择-->
<location-select ref="locationSelect" @locationInfo="getLocationInfo"></location-select>
</el-form>
</template>
<script>
import { formItemRules } from './js/form-rules.js'
import mixin from '../mixins/commonUi.js'
import draggable from 'vuedraggable'
import ConfigOptions from './basicComponent/config-options.vue'
import TableConfigOptions from './basicComponent/table-config-options.vue'
import { cloneDeep } from 'lodash'
import selectTree from './basicComponent/selectTree'
import CustomFile from '@/components/customFileUpload/index.vue'
import uploadFile from '@/components/uploadFile'
import PageSelect from './basicComponent/pageSelect.vue'
import customAutocomplete from './basicComponent/myCustomAutocomplete.vue'
import togglePassword from './basicComponent/myPassword.vue'
// import { createRulesBasicPage } from '@/util/validate';
import { optionsConfigs } from '@/common/getBasicDate.js'
import { nanoid } from 'nanoid'
import i18n from '@/lang'
import { mapGetters } from 'vuex'
function validateEmpty(message = '') {
return function (rule, value, callback) {
// console.log('validate Empty= >', value)
typeof value === 'string' && value !== '' && value.trim() === ''
? callback(message || i18n.t('请勿全部输入空格'))
: callback()
}
}
const validatorRange = function ({ type = 'text', min = 0, max = 0, data = {}, required = false }) {
const { itemtype, value, options } = data
// 判断表单项是否必填
if (required === false && (value === '' || value === undefined)) {
return { success: true }
}
if (itemtype === 'Select') {
// const option = (options ?? []).find((item) => item.value === value)
// const len = `${option?.label ?? ''}`.length
// if (len < min || len > max) {
// const message = `请选择长度【${min}~${max}】的数据`
// return { success: false, message }
// } else {
// return { success: true }
// }
return { success: true }
} else if (itemtype === 'Input' || itemtype === 'SelectInput') {
if (type === 'text') {
const len = `${value ?? ''}`.length
if (len >= min && len <= max) {
return { success: true }
} else {
const message = `请输入长度【${min}~${max}】的字符`
return { success: false, message }
}
} else if (type === 'number') {
const nValue = Number(value)
if (isNaN(nValue)) {
return { success: false, message: i18n.t('请输入正确的数字') }
} else if (value !== '' && value !== '0' && /^[0]+$/.test(nValue)) {
return { success: false, message: i18n.t('请勿输入全为0的数字') }
} else if (nValue >= min && nValue <= max) {
return { success: true }
} else {
const message = i18n.t('请输入范围【{0}~{1}】的{2}', [min, max, '数字'])
return { success: false, message }
}
}
} else {
return { success: true }
}
}
export default {
inheritAttrs: false,
name: 'MdForm',
mixins: [mixin],
components: {
draggable,
ConfigOptions,
TableConfigOptions,
selectTree,
CustomFile,
PageSelect,
uploadFile,
customAutocomplete,
SelectUser: () => import('@/components/selectUser'),
locationSelect: () => import('@/components/locationSelect'),
togglePassword
},
props: {
/**
* 表单绑定的值
*@param {paraName}
*/
formData: {
type: Object,
default: () => ({})
},
// 用于查询条件时,去掉值为undefined null 和空字符串的数据
searchData: {
type: Object,
default: () => ({})
},
// 表单是否可拖动
draggable: {
type: Boolean,
default: false
},
// 拖动时的group名
dragGroup: {
type: String,
default: ''
},
formKey: {
type: String,
default: ''
},
isUpdateCustomLabel: {
type: Boolean,
default: true
},
formView: {
type: Object,
default: () => ({})
},
inline: {
type: Boolean,
default: false
},
size: {
type: String,
default: 'small'
},
// label的宽度
labelWidth: {
type: [String, Number],
default: '104px'
},
layoutClass: {
// 可维护多种备选布局方式
type: String,
default: 'default-layout'
},
labelPosition: {
// left right top
type: String,
default: 'right'
},
formColumns: {
// 默认共12列,
type: Number,
default: 12
},
columns: {
// 默认每个表单组件所占的列数,
type: Number,
default: 12
},
updateClearValidate: {
// 默认更新view会校验表单
type: Boolean,
default: true
},
isAutoComputedColumns: {
// 是否自动计算columns
type: Boolean,
default: true
},
// 是否自适应布局
autoLayout: {
type: Boolean,
default: false
},
formItemMinWidth: {
// form-item的最小宽度
type: Number,
default: 0
},
selectedKey: {
type: [Number, String],
default: ''
},
// 设置表单可拖动时的样式名
dragFormClass: {
type: String,
default: ''
},
// 隐藏表单的所有label
hiddenLabel: {
type: Boolean,
default: false
},
// 隐藏某些 form-item
hiddenKeys: {
type: Array,
default: () => []
},
// 隐藏某些类型的表单
hiddenTypes: {
type: Array,
default: () => []
},
// 列间距
columnGap: {
type: [Number, String],
default: 6
},
// 行间距
rowGap: {
type: [Number, String],
default: 16
},
editTableRowIndex: {
// 当用于表格中的单元格的表单的时候的编辑行的索引
type: [Number, String],
default: -1
},
tableRowIndex: {
// 当用于表格中的单元格的表单的时候的行索引
type: [Number, String],
default: -1
},
tableColIndex: {
// 当用于表格中的单元格的表单的时候的行索引
type: [Number, String],
default: -1
},
editFormType: {
// left right top
type: Number,
default: 1
},
showPlaceholderPrefix: {
//placeholder前缀
type: Boolean,
default: true
}
},
data() {
return {
refsName: 'elform',
proxyMethodsArr: ['validate', 'validateField', 'resetFields', 'clearValidate'],
checkboxMultiple: {}, // 代理多选框组数据
formWidth: null,
rules: {},
formDataBackup: {},
focusElement: null,
selectAuto: true,
selectOptions: {}, // 记录选择框的选项的行数据
groupCodeOptions: {}, // 存储groupCode对应的options选项,可以直接通过groupCodeOptions['groupCode']渠道缓存的选项值
selectFilterObj: {},
datePickerOptions: {
disabledDate(time) {
return (
time.getTime() <= new Date('1900-01-01 00:00:00').getTime() ||
time.getTime() >= new Date('2073-12-30 23:59:59').getTime()
)
}
},
defaultCheckedUserList: [], // 已选择的用户
currentFocusData: {} //记住当前操作数据
}
},
inject: {
notebookAndModuleInfo: {
default: {}
}
},
watch: {
autoLayout: {
handler(val) {
// window.removeEventListener('resize', this.onResize)
// console.log('%c 第372行', 'color:red;font-size:2em')
// console.log(val)
if (val) {
// this.$nextTick(() => {
// window.addEventListener('resize', this.onResize)
// this.onResize()
// })
}
},
immediate: true
}
},
created() {
// this.rewriteEventMethods()
},
beforeMount() {
this.initConfig()
},
mounted() {
// console.log('%c 第839行 mounted', 'color:red;font-size:2em')
// console.log(this.formView)
this.$watch(
() => {
return this.formView
},
async () => {
if (!this.formView) {
return
}
console.log('表单json变化')
await this.queryGroupCodeOptions().then(async (res) => {
this.initFormItemConfig()
this.initConfig()
await this.rewriteEventMethods()
})
},
{ deep: true, immediate: true }
)
this.$watch(
() => {
return this.formData
},
(val) => {
let changeField = null
let searchData = {}
Object.keys(this.formData).forEach((i) => {
let v = this.formData[i]
if (
this.formView &&
this.formView[i] &&
(this.formView[i].type === 'Number' || this.formView[i].verify === 'integer')
) {
const nVal = isNaN(Number(v)) || Number(v) === 0 ? '' : Number(v)
this.$set(this.formData, i, nVal)
}
if (this.formView && this.formView[i]) {
// console.log('2222', this.editFormType)
//修改的时候调接口查询对应中文名
if (
this.formView[i].itemtype === 'DialogSelect' &&
(this.formView[i].dialogType === 'selectUser' || this.formView[i].dialogType === 'selectLocation') &&
this.editFormType == 2 &&
!this.formView[i].shouldInput
) {
if (this.formData.hasOwnProperty(i)) {
if (!isNaN(this.formData[i])) {
//数字类型 为userid
this.$request.getUserInfoListById([this.formData[i]]).then((res) => {
let data = res?.data
this.$set(this.formData, i + '_label', data[this.formData[i]])
})
} else {
if (this.formView[i].dialogType === 'selectLocation') {
this.$set(
this.formData,
i + '_label',
this.formData[this.formView[i]['linkSetField']] ||
this.formData[this.formView[i]['labelShowField']] ||
this.formData[i]
)
} else {
this.$set(this.formData, i + '_label', this.formData[i])
}
}
}
}
}
if (v !== undefined && v !== null && v !== '' && !(Array.isArray(v) && v.length === 0)) {
searchData[i] = v
}
if ((v || this.formDataBackup[i]) && JSON.stringify(v) !== JSON.stringify(this.formDataBackup[i])) {
changeField = i
}
})
this.$emit('update:searchData', searchData)
// 不用失焦而是立即执行的input类型
let inputTypes = ['Radio']
if (
document.activeElement &&
document.activeElement !== document.body &&
!this.focusElement &&
!inputTypes.includes(this.formView[changeField]?.itemtype)
) {
if (this.$refs.elform.$el.contains(document.activeElement)) {
this.focusElement = document.activeElement
this.focusElement.addEventListener('blur', this.elementBlur) // 只能监听部分类型的失去焦点事件,一些类型的数据改变应当立即抛出时间
}
} else {
let types = ['Switch', 'DatePicker', 'DateTimePicker'].concat(inputTypes)
if (changeField && types.includes(this.formView[changeField]?.itemtype)) {
this.elementBlur()
}
}
this.formDataBackup = cloneDeep(val)
// if (this.notebookAndModuleInfo?.formUsedToExperimentNotebook) {
// // 当表单用于实验记录页面的时候,监听数据变化,在离开实验记录页面的时候保存数据
// this.$store.commit('record/SET_VALUECHANGE', true)
// }
},
{ deep: true, immediate: true }
)
if (this.autoLayout) {
window.addEventListener('resize', this.onResize)
this.onResize()
}
if (this.draggable) {
document.addEventListener('click', this.documentClick)
}
},
directives: {
selectloadmore: {
bind(el, binding) {
if (binding.value) {
const SELECTWRAP_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap')
SELECTWRAP_DOM.addEventListener('scroll', function () {
const CONDITION = this.scrollHeight - this.scrollTop <= this.clientHeight
if (CONDITION) {
binding.value()
}
})
}
}
}
},
methods: {
findForm(el) {
if (el.parentNode.tagName === 'FORM') {
return el.parentNode
} else {
return this.findForm(el.parentNode)
}
},
initConfig() {
try {
if (!this.isUpdateCustomLabel) {
return
}
// console.log('formKey', this.formKey)
let tarConfig = (this.sysFormConfig || {})[this.formKey] // 获取当前模块配置
console.log('tarConfig', tarConfig)
if (tarConfig) {
for (let key in this.formView) {
let configView = tarConfig[key]
if (!configView) continue
if (configView.itemCustomLabel) {
this.formView[key].itemlabel = configView.itemCustomLabel
}
if (configView.itemAttr === 'required') {
this.$set(this.formView[key], 'required', true)
}
if (configView.itemAttr === 'disabled') {
this.formView[key].disabled = true
}
if (configView.itemAttr === 'readonly') {
this.formView[key].readonly = true
}
if (configView.itemAttr === 'hidden') {
this.formView[key].show = false
}
if (configView.itemAttr === 'normal') {
this.$set(this.formView[key], 'required', false)
}
if (configView?.itemAttr != 'hidden') {
if (this.formView[key].hasOwnProperty('itemAttr')) {
delete this.formView[key].itemAttr
}
// this.$set(this.formView[key], 'itemAttr', '')
}
}
}
} catch {}
},
nativeFileClick() {
this.$refs.nativeFile[0].click()
},
nativeFileChange(item, e) {
this.$refs.nativeFileName[0].value = e.target.files[0].name
item.change && item.change(e)
},
inputDialogSelect(e, item, key) {
if (item.dialogType === 'selectUser') {
this.$set(this.formData, key, e)
}
},
openSelectDialog(data, key, type = 'focus') {
this.currentFocusData = Object.assign({}, data, { key: key }) //记住当前操作数据
if (data.dialogType === 'selectUser') {
//选择用户组件
if (type === 'focus' && data.shouldInput) return
let dataList = Number(this.formData[key]) ? [Number(this.formData[key])].map((item) => ({ userId: item })) : []
this.defaultCheckedUserList = this.formData.hasOwnProperty(key) ? dataList : []
console.log('this.defaultCheckedUserList', this.defaultCheckedUserList)
this.$refs.refSelectUser.show()
//手动失焦,为了解决键盘输入还是会输入值在输入框中
if (type === 'focus') {
let formInput = document.getElementById('dialogSelectInput')
formInput && formInput.blur()
}
} else if (data.dialogType === 'selectLocation') {
if (type === 'focus' && data.shouldInput) return
this.$refs.locationSelect.show()
}
},
dialogSelectClear(data, key) {
this.currentFocusData = Object.assign({}, data, { key: key }) //记住当前操作数据
const item = this.currentFocusData
if (data.dialogType === 'selectUser' || data.dialogType === 'selectLocation') {
this.$set(this.formData, key, '')
this.$set(this.formData, `${key}_label`, '')
if (item.linkSetField) {
// 需要关联的设置的值
if (typeof item.linkSetField === 'string') {
this.$set(this.formData, item.linkSetField, '')
}
if (typeof item.linkSetField === 'object') {
// console.log('%c 第1228行', 'color: red; font-size: 2em')
// console.log(item)
this.$set(this.formData, item.linkSetField.setField, '')
}
if (Array.isArray(item.linkSetField)) {
item.linkSetField.forEach((oItem) => {
this.$set(this.formData, oItem.setField, '')
})
}
}
//选择用户组件
item.change && item.change('')
console.log('formData', this.formData)
}
},
handleSelectUser(user = {}) {
const item = this.currentFocusData
let row = {
label: user.userName,
value: user.id,
account: user.account,
userCode: user.userCode,
info: user
}
let value = row[item['bindValueField'] || 'value' || 'label']
let key = item.key
this.$set(this.formData, key, value)
console.log('row, ', row)
this.$set(this.formData, `${key}_label`, row.label)
console.log('formDatahandleSelectUser', this.formData)
if (item.linkSetField) {
// 需要关联的设置的值
if (typeof item.linkSetField === 'string') {
this.$set(this.formData, item.linkSetField, row[item.linkSetFieldBindKey] || row.label)
}
if (typeof item.linkSetField === 'object') {
// console.log('%c 第1228行', 'color: red; font-size: 2em')
// console.log(item)
this.$set(this.formData, item.linkSetField.setField, row[item.linkSetField.bindField] || row.label)
}
if (Array.isArray(item.linkSetField)) {
item.linkSetField.forEach((oItem) => {
this.$set(this.formData, oItem.setField, row[oItem.bindField])
})
}
}
item.afterSelect &&
item.afterSelect({
value: this.formData[key],
key,
formView: this.formView,
formData: this.formData,
selectOption: row
})
},
getLocationInfo(data) {
if (!data) return
// console.log('getLocationInfo-data---------', data)
const item = this.currentFocusData
let row = {
label: data.name,
showName: data.name,
value: data.id,
info: data
}
let value = row[item['bindValueField'] || 'value' || 'label']
// console.log('item', item, row, value)
let key = item.key
this.$set(this.formData, key, value)
this.$set(this.formData, `${key}_label`, row.label)
if (item.linkSetField) {
// 需要关联的设置的值
if (typeof item.linkSetField === 'string') {
this.$set(this.formData, item.linkSetField, row.label)
}
if (typeof item.linkSetField === 'object') {
// console.log('%c 第1228行', 'color: red; font-size: 2em')
// console.log(item)
this.$set(this.formData, item.linkSetField.setField, row[item.linkSetField.bindField] || row.label)
}
if (Array.isArray(item.linkSetField)) {
item.linkSetField.forEach((oItem) => {
this.$set(this.formData, oItem.setField, row[oItem.bindField])
})
}
}
if (item.recordSelectedRowInfo) {
this.$set(this.formData, item.recordSelectedRowInfo, row)
}
item.afterSelect &&
item.afterSelect({
value: this.formData[key],
key,
formView: this.formView,
formData: this.formData,
selectOption: row
})
},
renderTitle(type = '', data = {}) {
// console.log('render title =>', type, data)
let title = ''
if (type === 'select') {
const { key, item } = data
const array = (item.groupCode ? this.groupCodeOptions[item.groupCode] : item.options) ?? []
const options = typeof item.options === 'function' ? item.options(key, this.formData, this.formView) : array
if (Array.isArray(options)) {
const value = this.formData[key]
const values = Array.isArray(value) ? value : [value]
const matched = options.filter((e) => values.includes(e.value))
title = matched.map((e) => e?.label ?? '').join('、')
}
}
return title
},
handleFilterChange(filter, key) {
this.$set(this.selectFilterObj, key, filter)
},
/**
* formItem 点击时触发
* @param { String } key formItem对应的key值
* @param { Object } item formItem对应的项目
* @param { Object } formView formView数据
* @param { Object } formData formData数据
*/
handleClickFormItem({ key, item, formView, formData }) {
// console.log(key, item, formView, formData)
this.draggable ? this.updateSelectedKey(key) : null
if (item.clickable) {
this.$emit('itemClick', { key, item, formView, formData })
}
},
changeAutoComplete() {
// console.log('changeAutoComplete')
// 选中后让输入框失焦,不然下拉的数据可能还在
document.body.click()
},
clickHandler(key, item) {
typeof item.click === 'function' && item.click(key, item)
},
keyupEnterHandler(key, item) {
typeof item.enterSearch === 'function' && item.enterSearch(item)
this.$emit('enter', { key, itemData: item })
},
keydownHandler(e, key, item) {
if (item.type === 'number') {
let keyCode = e.keyCode
if (keyCode === 69) {
//数字类型输入框禁止输入e
e.returnValue = false
return false
}
return true
}
},
handleClear(item, key) {
console.log('
标签:el,封装,&&,form,formData,item,formView,key
From: https://www.cnblogs.com/yibottlec/p/18429037