<template>
|
<page-header-wrapper content="高级表单常见于一次性输入和提交大批量数据的场景">
|
<a-card class="card" title="仓库管理" :bordered="false">
|
<repository-form ref="repository" :showSubmit="false" />
|
</a-card>
|
<a-card class="card" title="任务管理" :bordered="false">
|
<task-form ref="task" :showSubmit="false" />
|
</a-card>
|
|
<!-- table -->
|
<a-card>
|
<a-table
|
:columns="columns"
|
:dataSource="data"
|
:pagination="false"
|
:loading="memberLoading"
|
>
|
<template v-for="(col, i) in ['name', 'workId', 'department']" :slot="col" slot-scope="text, record">
|
<a-input
|
:key="col"
|
v-if="record.editable"
|
style="margin: -5px 0"
|
:value="text"
|
:placeholder="columns[i].title"
|
@change="e => handleChange(e.target.value, record.key, col)"
|
/>
|
<template v-else>{{ text }}</template>
|
</template>
|
<template slot="operation" slot-scope="text, record">
|
<template v-if="record.editable">
|
<span v-if="record.isNew">
|
<a @click="saveRow(record)">添加</a>
|
<a-divider type="vertical" />
|
<a-popconfirm title="是否要删除此行?" @confirm="remove(record.key)">
|
<a>删除</a>
|
</a-popconfirm>
|
</span>
|
<span v-else>
|
<a @click="saveRow(record)">保存</a>
|
<a-divider type="vertical" />
|
<a @click="cancel(record.key)">取消</a>
|
</span>
|
</template>
|
<span v-else>
|
<a @click="toggle(record.key)">编辑</a>
|
<a-divider type="vertical" />
|
<a-popconfirm title="是否要删除此行?" @confirm="remove(record.key)">
|
<a>删除</a>
|
</a-popconfirm>
|
</span>
|
</template>
|
</a-table>
|
<a-button style="width: 100%; margin-top: 16px; margin-bottom: 8px" type="dashed" icon="plus" @click="newMember">新增成员</a-button>
|
</a-card>
|
|
<!-- fixed footer toolbar -->
|
<footer-tool-bar :is-mobile="isMobile" :collapsed="sideCollapsed">
|
<span class="popover-wrapper">
|
<a-popover title="表单校验信息" overlayClassName="antd-pro-pages-forms-style-errorPopover" trigger="click" :getPopupContainer="trigger => trigger.parentNode">
|
<template slot="content">
|
<li v-for="item in errors" :key="item.key" @click="scrollToField(item.key)" class="antd-pro-pages-forms-style-errorListItem">
|
<a-icon type="cross-circle-o" class="antd-pro-pages-forms-style-errorIcon" />
|
<div class="">{{ item.message }}</div>
|
<div class="antd-pro-pages-forms-style-errorField">{{ item.fieldLabel }}</div>
|
</li>
|
</template>
|
<span class="antd-pro-pages-forms-style-errorIcon" v-if="errors.length > 0">
|
<a-icon type="exclamation-circle" />{{ errors.length }}
|
</span>
|
</a-popover>
|
</span>
|
<a-button type="primary" @click="validate" :loading="loading">提交</a-button>
|
</footer-tool-bar>
|
</page-header-wrapper>
|
</template>
|
|
<script>
|
import RepositoryForm from './RepositoryForm'
|
import TaskForm from './TaskForm'
|
import FooterToolBar from '@/components/FooterToolbar'
|
import { baseMixin } from '@/store/app-mixin'
|
|
const fieldLabels = {
|
name: '仓库名',
|
url: '仓库域名',
|
owner: '仓库管理员',
|
approver: '审批人',
|
dateRange: '生效日期',
|
type: '仓库类型',
|
name2: '任务名',
|
url2: '任务描述',
|
owner2: '执行人',
|
approver2: '责任人',
|
dateRange2: '生效日期',
|
type2: '任务类型'
|
}
|
|
export default {
|
name: 'AdvancedForm',
|
mixins: [baseMixin],
|
components: {
|
FooterToolBar,
|
RepositoryForm,
|
TaskForm
|
},
|
data () {
|
return {
|
loading: false,
|
memberLoading: false,
|
|
// table
|
columns: [
|
{
|
title: '成员姓名',
|
dataIndex: 'name',
|
key: 'name',
|
width: '20%',
|
scopedSlots: { customRender: 'name' }
|
},
|
{
|
title: '工号',
|
dataIndex: 'workId',
|
key: 'workId',
|
width: '20%',
|
scopedSlots: { customRender: 'workId' }
|
},
|
{
|
title: '所属部门',
|
dataIndex: 'department',
|
key: 'department',
|
width: '40%',
|
scopedSlots: { customRender: 'department' }
|
},
|
{
|
title: '操作',
|
key: 'action',
|
scopedSlots: { customRender: 'operation' }
|
}
|
],
|
data: [
|
{
|
key: '1',
|
name: '小明',
|
workId: '001',
|
editable: false,
|
department: '行政部'
|
},
|
{
|
key: '2',
|
name: '李莉',
|
workId: '002',
|
editable: false,
|
department: 'IT部'
|
},
|
{
|
key: '3',
|
name: '王小帅',
|
workId: '003',
|
editable: false,
|
department: '财务部'
|
}
|
],
|
|
errors: []
|
}
|
},
|
methods: {
|
handleSubmit (e) {
|
e.preventDefault()
|
},
|
newMember () {
|
const length = this.data.length
|
this.data.push({
|
key: length === 0 ? '1' : (parseInt(this.data[length - 1].key) + 1).toString(),
|
name: '',
|
workId: '',
|
department: '',
|
editable: true,
|
isNew: true
|
})
|
},
|
remove (key) {
|
const newData = this.data.filter(item => item.key !== key)
|
this.data = newData
|
},
|
saveRow (record) {
|
this.memberLoading = true
|
const { key, name, workId, department } = record
|
if (!name || !workId || !department) {
|
this.memberLoading = false
|
this.$message.error('请填写完整成员信息。')
|
return
|
}
|
// 模拟网络请求、卡顿 800ms
|
new Promise((resolve) => {
|
setTimeout(() => {
|
resolve({ loop: false })
|
}, 800)
|
}).then(() => {
|
const target = this.data.find(item => item.key === key)
|
target.editable = false
|
target.isNew = false
|
this.memberLoading = false
|
})
|
},
|
toggle (key) {
|
const target = this.data.find(item => item.key === key)
|
target._originalData = { ...target }
|
target.editable = !target.editable
|
},
|
getRowByKey (key, newData) {
|
const data = this.data
|
return (newData || data).find(item => item.key === key)
|
},
|
cancel (key) {
|
const target = this.data.find(item => item.key === key)
|
Object.keys(target).forEach(key => { target[key] = target._originalData[key] })
|
target._originalData = undefined
|
},
|
handleChange (value, key, column) {
|
const newData = [...this.data]
|
const target = newData.find(item => key === item.key)
|
if (target) {
|
target[column] = value
|
this.data = newData
|
}
|
},
|
|
// 最终全页面提交
|
validate () {
|
const { $refs: { repository, task }, $notification } = this
|
const repositoryForm = new Promise((resolve, reject) => {
|
repository.form.validateFields((err, values) => {
|
if (err) {
|
reject(err)
|
return
|
}
|
resolve(values)
|
})
|
})
|
const taskForm = new Promise((resolve, reject) => {
|
task.form.validateFields((err, values) => {
|
if (err) {
|
reject(err)
|
return
|
}
|
resolve(values)
|
})
|
})
|
|
// clean this.errors
|
this.errors = []
|
Promise.all([repositoryForm, taskForm]).then(values => {
|
$notification['error']({
|
message: 'Received values of form:',
|
description: JSON.stringify(values)
|
})
|
}).catch(() => {
|
const errors = Object.assign({}, repository.form.getFieldsError(), task.form.getFieldsError())
|
const tmp = { ...errors }
|
this.errorList(tmp)
|
})
|
},
|
errorList (errors) {
|
if (!errors || errors.length === 0) {
|
return
|
}
|
this.errors = Object.keys(errors)
|
.filter(key => errors[key])
|
.map(key => ({
|
key: key,
|
message: errors[key][0],
|
fieldLabel: fieldLabels[key]
|
}))
|
},
|
scrollToField (fieldKey) {
|
const labelNode = document.querySelector(`label[for="${fieldKey}"]`)
|
if (labelNode) {
|
labelNode.scrollIntoView(true)
|
}
|
}
|
}
|
}
|
</script>
|
|
<style lang="less" scoped>
|
.card{
|
margin-bottom: 24px;
|
}
|
.popover-wrapper {
|
:deep(.antd-pro-pages-forms-style-errorPopover .ant-popover-inner-content) {
|
min-width: 256px;
|
max-height: 290px;
|
padding: 0;
|
overflow: auto;
|
}
|
}
|
.antd-pro-pages-forms-style-errorIcon {
|
user-select: none;
|
margin-right: 24px;
|
color: #f5222d;
|
cursor: pointer;
|
i {
|
margin-right: 4px;
|
}
|
}
|
.antd-pro-pages-forms-style-errorListItem {
|
padding: 8px 16px;
|
list-style: none;
|
border-bottom: 1px solid #e8e8e8;
|
cursor: pointer;
|
transition: all .3s;
|
|
&:hover {
|
background: #e6f7ff;
|
}
|
.antd-pro-pages-forms-style-errorIcon {
|
float: left;
|
margin-top: 4px;
|
margin-right: 12px;
|
padding-bottom: 22px;
|
color: #f5222d;
|
}
|
.antd-pro-pages-forms-style-errorField {
|
margin-top: 2px;
|
color: rgba(0,0,0,.45);
|
font-size: 12px;
|
}
|
}
|
</style>
|