fix: 修复编辑出问题的bug

perf: 优化嵌套的form

perf: 优化嵌套的form

perf: 优化其那套form

perf: 优化nestfield, 但报错存在问题

perf: 优化
This commit is contained in:
ibuler
2021-03-01 16:41:42 +08:00
committed by Orange
parent 6fde735cbd
commit 917d95cc7b
8 changed files with 259 additions and 252 deletions

View File

@@ -0,0 +1,43 @@
<template>
<DataForm
:fields="fields"
:form="value"
style="margin-left: -26%;margin-right: -6%"
v-bind="kwargs"
v-on="$listeners"
/>
</template>
<script>
import { DataForm } from '@/components'
export default {
name: 'NestedField',
components: {
DataForm
},
props: {
fields: {
type: Array,
default: () => []
},
value: {
type: Object,
default: () => ({})
}
},
data() {
return {
kwargs: {
hasReset: false,
hasSaveContinue: false,
defaultButton: false
}
}
}
}
</script>
<style scoped>
</style>

View File

@@ -1,15 +1,13 @@
<template>
<DataForm ref="dataForm" v-loading="loading" :fields="totalFields" v-bind="$attrs" v-on="$listeners">
<FormGroupHeader v-for="(group, i) in groups" :slot="'id:'+group.name" :key="'group-'+group.name" :title="group.title" :line="i != 0" />
<DataForm ref="dataForm" v-loading="loading" :fields="totalFields" :form="iForm" v-bind="$attrs" v-on="$listeners">
<FormGroupHeader v-for="(group, i) in groups" :slot="'id:'+group.name" :key="'group-'+group.name" :title="group.title" :line="i !== 0" />
</DataForm>
</template>
<script>
import DataForm from '../DataForm'
import FormGroupHeader from '@/components/FormGroupHeader'
// import { optionUrlMeta } from '@/api/common'
import rules from '@/components/DataForm/rules'
import Select2 from '@/components/Select2'
import { FormFieldGenerator } from '@/components/AutoDataForm/utils'
export default {
name: 'AutoDataForm',
components: {
@@ -31,6 +29,10 @@ export default {
return []
}
},
form: {
type: Object,
default: () => ({})
},
fieldsMeta: {
type: Object,
default: () => ({})
@@ -38,175 +40,51 @@ export default {
},
data() {
return {
meta: {},
remoteMeta: {},
totalFields: [],
loading: true,
groups: []
groups: [],
iForm: this.form
}
},
mounted() {
this.optionUrlMeta()
this.optionUrlMetaAndGenerateColumns()
},
methods: {
optionUrlMeta() {
optionUrlMetaAndGenerateColumns() {
this.$store.dispatch('common/getUrlMeta', { url: this.url }).then(data => {
this.meta = data.actions[this.method.toUpperCase()] || {}
this.remoteMeta = data.actions[this.method.toUpperCase()] || {}
this.generateColumns()
this.cleanFormValue()
}).catch(err => {
this.$log.error(err)
}).finally(() => {
this.loading = false
})
},
generateFieldByType(type, field, fieldMeta) {
switch (type) {
case 'choice':
type = 'radio-group'
if (!fieldMeta.read_only) {
field.options = fieldMeta.choices.map(v => {
return { label: v.display_name, value: v.value }
})
}
break
case 'datetime':
type = 'date-picker'
field.el = {
type: 'datetime'
}
break
case 'field':
type = ''
field.component = Select2
if (fieldMeta.required) {
field.el.clearable = false
}
break
case 'string':
type = 'input'
if (!fieldMeta.max_length) {
field.el.type = 'textarea'
field.el.rows = 3
}
if (fieldMeta.write_only) {
field.el.type = 'password'
}
break
case 'boolean':
type = 'checkbox'
break
default:
type = 'input'
break
}
if (type === 'radio-group') {
if (!fieldMeta.read_only) {
const options = fieldMeta.choices.map(v => {
return { label: v.display_name, value: v.value }
})
if (options.length > 4) {
type = 'select'
field.el.filterable = true
}
}
}
field.type = type
return field
},
generateFieldByName(name, field) {
switch (name) {
case 'email':
field.el.type = 'email'
break
case 'password':
field.el.type = 'password'
break
case 'comment':
field.el.type = 'textarea'
break
}
return field
},
generateFieldByOther(field, fieldMeta) {
const filedRules = field.rules || []
if (fieldMeta.required) {
if (field.type === 'input') {
filedRules.push(rules.Required)
} else {
filedRules.push(rules.RequiredChange)
}
}
field.rules = filedRules
return field
},
generateField(name) {
let field = { id: name, prop: name, el: {}, attrs: {}}
const fieldMeta = this.meta[name] ||
((this.meta['attrs']) ? (this.meta['attrs']['children'][name]) : false) ||
((this.meta['meta']) ? (this.meta['meta']['children'][name]) : {})
field.label = fieldMeta.label
field.helpText = fieldMeta.help_text
field = this.generateFieldByType(fieldMeta.type, field, fieldMeta)
field = this.generateFieldByName(name, field)
field = this.generateFieldByOther(field, fieldMeta)
field = Object.assign(field, this.fieldsMeta[name] || {})
_.set(field, 'attrs.error', '')
return field
},
generateFieldGroup(data) {
const [groupTitle, fields] = data
this.groups.push({
id: groupTitle,
title: groupTitle,
name: fields[0]
})
return this.generateFields(fields)
},
generateFieldAttrs(name) {
const fields = []
Object.keys(this.meta[name]['children']).forEach((key, i) => {
const filed = this.generateField(key)
fields.push(filed)
})
return fields
},
generateFields(data) {
let fields = []
for (let field of data) {
if (field instanceof Array) {
const items = this.generateFieldGroup(field)
fields = [...fields, ...items]
} else if (typeof field === 'string') {
field = this.generateField(field)
fields.push(field)
} else if (field instanceof Object) {
this.errors[field.prop] = ''
_.set(field, 'attrs.error', '')
fields.push(field)
}
}
return fields
},
generateColumns() {
this.totalFields = this.generateFields(this.fields)
const generator = new FormFieldGenerator()
this.totalFields = generator.generateFields(this.fields, this.fieldsMeta, this.remoteMeta)
this.groups = generator.groups
this.$log.debug('Total fields: ', this.totalFields)
},
setFieldError(name, error) {
const field = this.totalFields.find((v) => v.prop === name)
if (!field) {
return
_cleanFormValue(form, remoteMeta) {
for (const [k, v] of Object.entries(remoteMeta)) {
if (v.default === undefined) {
continue
}
if (typeof error === 'object') {
const str = error
error = ''
Object.keys(str).forEach(key => {
error += `${parseInt(key) + 1}.${str[key][0]} `
})
const valueSet = form[k]
if (valueSet !== undefined) {
continue
}
// if (field.attrs.error === error) {
// error += '.'
// }
field.attrs.error = error
if (v.type === 'nested object' && typeof valueSet === 'object') {
this._cleanFormValue(valueSet, v.children)
}
form[k] = v.default
}
},
cleanFormValue() {
this._cleanFormValue(this.iForm, this.remoteMeta)
}
}
}

View File

@@ -0,0 +1,149 @@
import Vue from 'vue'
import Select2 from '@/components/Select2'
import NestedField from '@/components/AutoDataForm/components/NestedField'
import rules from '@/components/DataForm/rules'
export class FormFieldGenerator {
constructor() {
this.groups = []
}
generateFieldByType(type, field, fieldMeta, fieldRemoteMeta) {
switch (type) {
case 'choice':
type = 'radio-group'
if (!fieldRemoteMeta.read_only) {
field.options = fieldRemoteMeta.choices.map(v => {
return { label: v.display_name, value: v.value }
})
}
break
case 'datetime':
type = 'date-picker'
field.el = {
type: 'datetime'
}
break
case 'field':
type = ''
field.component = Select2
if (fieldRemoteMeta.required) {
field.el.clearable = false
}
break
case 'string':
type = 'input'
if (!fieldRemoteMeta.max_length) {
field.el.type = 'textarea'
field.el.rows = 3
}
if (fieldRemoteMeta.write_only) {
field.el.type = 'password'
}
break
case 'boolean':
type = 'checkbox'
break
case 'nested object':
field.component = NestedField
field.label = ''
field.labelWidth = 0
field.el.fields = this.generateNestFields(field, fieldMeta, fieldRemoteMeta)
Vue.$log.debug('All fields in generate: ', field.el.allFields)
break
default:
type = 'input'
break
}
if (type === 'radio-group') {
if (!fieldRemoteMeta.read_only) {
const options = fieldRemoteMeta.choices.map(v => {
return { label: v.display_name, value: v.value }
})
if (options.length > 4) {
type = 'select'
field.el.filterable = true
}
}
}
field.type = type
return field
}
generateNestFields(field, fieldMeta, fieldRemoteMeta) {
const fields = []
const nestedFields = fieldMeta.fields || []
const nestedFieldsMeta = fieldMeta.fieldsMeta || {}
const nestedFieldsRemoteMeta = fieldRemoteMeta.children || {}
for (const name of nestedFields) {
const f = this.generateField(name, nestedFieldsMeta, nestedFieldsRemoteMeta)
fields.push(f)
}
Vue.$log.debug('NestFields: ', fields)
return fields
}
generateFieldByName(name, field) {
switch (name) {
case 'email':
field.el.type = 'email'
break
case 'password':
field.el.type = 'password'
break
case 'comment':
field.el.type = 'textarea'
break
}
return field
}
generateFieldByOther(field, fieldMeta, fieldRemoteMeta) {
const filedRules = field.rules || []
if (fieldRemoteMeta.required) {
if (field.type === 'input') {
filedRules.push(rules.Required)
} else {
filedRules.push(rules.RequiredChange)
}
}
field.rules = filedRules
return field
}
generateField(name, fieldsMeta, remoteFieldsMeta) {
let field = { id: name, prop: name, el: {}, attrs: {}}
const remoteFieldMeta = remoteFieldsMeta[name] || {}
Vue.$log.debug('FieldsMeta: ', fieldsMeta, name)
const fieldMeta = fieldsMeta[name] || {}
Vue.$log.debug('FieldMeta is: ', fieldMeta)
field.label = remoteFieldMeta.label
field.helpText = remoteFieldMeta.help_text
field = this.generateFieldByType(remoteFieldMeta.type, field, fieldMeta, remoteFieldMeta)
field = this.generateFieldByName(name, field)
field = this.generateFieldByOther(field, fieldMeta, remoteFieldMeta)
const el = Object.assign(field.el || {}, fieldMeta.el || {})
field = Object.assign(field, fieldMeta || {}, { el: el })
return field
}
generateFieldGroup(field, fieldsMeta, remoteFieldsMeta) {
const [groupTitle, fields] = field
this.groups.push({
id: groupTitle,
title: groupTitle,
name: fields[0]
})
return this.generateFields(fields, fieldsMeta, remoteFieldsMeta)
}
generateFields(_fields, fieldsMeta, remoteFieldsMeta) {
let fields = []
for (let field of _fields) {
if (field instanceof Array) {
const items = this.generateFieldGroup(field, fieldsMeta, remoteFieldsMeta)
fields = [...fields, ...items]
} else if (typeof field === 'string') {
field = this.generateField(field, fieldsMeta, remoteFieldsMeta)
fields.push(field)
} else if (field instanceof Object) {
this.errors[field.prop] = ''
fields.push(field)
}
}
return fields
}
}

View File

@@ -47,7 +47,7 @@ export default {
// 初始值
form: {
type: Object,
default: () => { return {} }
default: () => ({})
},
moreButtons: {
type: Array,

View File

@@ -12,7 +12,7 @@ export default {
return {
fields: [
[this.$t('common.Basic'), ['name', 'type', 'domain']],
[this.$t('applications.DBInfo'), ['host', 'port', 'database']],
[this.$t('applications.DBInfo'), ['attrs']],
[this.$t('common.Other'), ['comment']]
],
fieldsMeta: {

View File

@@ -16,7 +16,7 @@ export default {
},
fields: [
[this.$t('common.Basic'), ['name', 'type', 'domain']],
[this.$t('applications.kubernetes'), ['cluster']],
[this.$t('applications.kubernetes'), ['attrs']],
[this.$t('common.Other'), ['comment']]
],
fieldsMeta: {

View File

@@ -15,6 +15,7 @@ export default {
const fieldsMap = REMOTE_APP_TYPE_FIELDS_MAP[appType]
const appTypeMeta = REMOTE_APP_TYPE_META_MAP[appType]
const pathInitial = REMOTE_APP_PATH_DEFAULT_MAP[appType]
console.log(fieldsMap)
return {
initial: {
@@ -23,71 +24,15 @@ export default {
},
fields: [
[this.$t('common.Basic'), ['name', 'type']],
[appTypeMeta.title, fieldsMap],
[appTypeMeta.title, ['attrs']],
[this.$t('common.Other'), ['comment']]
],
url: '/api/v1/applications/applications/',
getUrl() {
const params = this.$route.params
let url = `/api/v1/applications/applications/`
if (params.id) {
url = `${url}${params.id}/`
}
return `${url}?type=${this.$route.query.type}`
},
performSubmit(validValues) {
const params = this.$route.params
const baseUrl = `/api/v1/applications/applications/`
const url = (params.id) ? `${baseUrl}${params.id}/` : baseUrl
const method = this.getMethod()
switch (validValues.type) {
case 'chrome': {
validValues.attrs = {
chrome_target: validValues.chrome_target,
chrome_username: validValues.chrome_username,
chrome_password: validValues.chrome_password,
asset: validValues.asset,
path: validValues.path
}
break
}
case 'mysql_workbench': {
validValues.attrs = {
mysql_workbench_ip: validValues.mysql_workbench_ip,
mysql_workbench_port: validValues.mysql_workbench_port,
mysql_workbench_name: validValues.mysql_workbench_name,
mysql_workbench_username: validValues.mysql_workbench_username,
mysql_workbench_password: validValues.mysql_workbench_password,
asset: validValues.asset,
path: validValues.path
}
break
}
case 'vmware_client': {
validValues.attrs = {
vmware_password: validValues.vmware_password,
vmware_username: validValues.vmware_username,
vmware_target: validValues.vmware_target,
asset: validValues.asset,
path: validValues.path
}
break
}
case 'custom': {
validValues.attrs = {
custom_cmdline: validValues.custom_cmdline,
custom_target: validValues.custom_target,
custom_username: validValues.custom_username,
custom_password: validValues.custom_password,
asset: validValues.asset,
path: validValues.path
}
break
}
}
validValues.category = 'remote_app'
return this.$axios[method](`${url}?type=${validValues.type}`, validValues)
fieldsMeta: {
type: {
readonly: true
},
attrs: {
fields: fieldsMap,
fieldsMeta: {
asset: {
rules: [{ required: true }],
@@ -100,34 +45,27 @@ export default {
}
}
}
},
type: {
type: 'select',
options: [
{
label: appTypeMeta.title,
value: appTypeMeta.name
}
],
disabled: true
},
asset_info: {
type: 'input',
hidden: () => true
},
hello: {
type: 'input'
},
domain: {
el: {
multiple: false,
clearable: true,
ajax: {
url: '/api/v1/assets/domains/'
}
}
},
...fieldsMap
url: '/api/v1/applications/applications/',
getUrl() {
const params = this.$route.params
let url = `/api/v1/applications/applications/`
if (params.id) {
url = `${url}${params.id}/`
}
return `${url}?type=${this.$route.query.type}`
},
performSubmit(validValues) {
this.$log.debug('Validated data: ', validValues)
const params = this.$route.params
const baseUrl = `/api/v1/applications/applications/`
const url = (params.id) ? `${baseUrl}${params.id}/` : baseUrl
const method = this.getMethod()
validValues.category = 'remote_app'
return this.$axios[method](`${url}?type=${validValues.type}`, validValues)
}
}
},

View File

@@ -9,7 +9,6 @@
import { GenericCreateUpdatePage } from '@/layout/components'
import UserPassword from '@/components/UserPassword'
import RoleCheckbox from '@/views/users/User/components/RoleCheckbox'
import { getDayFuture } from '@/utils/common'
import { mapGetters } from 'vuex'
import rules from '@/components/DataForm/rules'
@@ -20,12 +19,12 @@ export default {
data() {
return {
initial: {
password_strategy: 0,
mfa_level: 0,
role: 'User',
source: 'local',
org_roles: ['User'],
date_expired: getDayFuture(36500, new Date()).toISOString()
// password_strategy: 0,
// mfa_level: 0,
// role: 'User',
// source: 'local',
// org_roles: ['User'],
// date_expired: getDayFuture(36500, new Date()).toISOString()
},
fields: [
[this.$t('users.Account'), ['name', 'username', 'email', 'groups']],