|
@@ -1,209 +1,334 @@
|
|
|
-<template>
|
|
|
- <div>
|
|
|
- <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>
|
|
|
- <form :autoFormCreate="(form) => this.form = form">
|
|
|
- <a-table
|
|
|
- :columns="columns"
|
|
|
- :dataSource="data"
|
|
|
- :pagination="false"
|
|
|
- >
|
|
|
- <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.key)">添加</a>
|
|
|
- <a-divider type="vertical" />
|
|
|
- <a-popconfirm title="是否要删除此行?" @confirm="remove(record.key)">
|
|
|
- <a>删除</a>
|
|
|
- </a-popconfirm>
|
|
|
- </span>
|
|
|
- <span v-else>
|
|
|
- <a @click="saveRow(record.key)">保存</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>
|
|
|
- </form>
|
|
|
- </a-card>
|
|
|
-
|
|
|
- <!-- fixed footer toolbar -->
|
|
|
- <footer-tool-bar :style="{ width: isSideMenu() && isDesktop() ? `calc(100% - ${sidebarOpened ? 256 : 80}px)` : '100%'}">
|
|
|
- <a-button type="primary" @click="validate" :loading="loading">提交</a-button>
|
|
|
- </footer-tool-bar>
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script>
|
|
|
-import RepositoryForm from './RepositoryForm'
|
|
|
-import TaskForm from './TaskForm'
|
|
|
-import FooterToolBar from '@/components/FooterToolbar'
|
|
|
-import { mixin, mixinDevice } from '@/utils/mixin'
|
|
|
-
|
|
|
-export default {
|
|
|
- name: 'AdvancedForm',
|
|
|
- mixins: [mixin, mixinDevice],
|
|
|
- components: {
|
|
|
- FooterToolBar,
|
|
|
- RepositoryForm,
|
|
|
- TaskForm
|
|
|
- },
|
|
|
- data () {
|
|
|
- return {
|
|
|
- description: '高级表单常见于一次性输入和提交大批量数据的场景。',
|
|
|
- loading: 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: '财务部'
|
|
|
- }
|
|
|
- ]
|
|
|
- }
|
|
|
- },
|
|
|
- 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 (key) {
|
|
|
- const target = this.data.filter(item => item.key === key)[0]
|
|
|
- target.editable = false
|
|
|
- target.isNew = false
|
|
|
- },
|
|
|
- toggle (key) {
|
|
|
- const target = this.data.filter(item => item.key === key)[0]
|
|
|
- target.editable = !target.editable
|
|
|
- },
|
|
|
- getRowByKey (key, newData) {
|
|
|
- const data = this.data
|
|
|
- return (newData || data).filter(item => item.key === key)[0]
|
|
|
- },
|
|
|
- cancel (key) {
|
|
|
- const target = this.data.filter(item => item.key === key)[0]
|
|
|
- target.editable = false
|
|
|
- },
|
|
|
- handleChange (value, key, column) {
|
|
|
- const newData = [...this.data]
|
|
|
- const target = newData.filter(item => key === item.key)[0]
|
|
|
- if (target) {
|
|
|
- target[column] = value
|
|
|
- this.data = newData
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 最终全页面提交
|
|
|
- validate () {
|
|
|
- this.$refs.repository.form.validateFields((err, values) => {
|
|
|
- if (!err) {
|
|
|
- this.$notification['error']({
|
|
|
- message: 'Received values of form:',
|
|
|
- description: values
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
- this.$refs.task.form.validateFields((err, values) => {
|
|
|
- if (!err) {
|
|
|
- this.$notification['error']({
|
|
|
- message: 'Received values of form:',
|
|
|
- description: values
|
|
|
- })
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-</script>
|
|
|
-
|
|
|
-<style lang="less" scoped>
|
|
|
- .card{
|
|
|
- margin-bottom: 24px;
|
|
|
- }
|
|
|
-</style>
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <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 :style="{ width: isSideMenu() && isDesktop() ? `calc(100% - ${sidebarOpened ? 256 : 80}px)` : '100%'}">
|
|
|
+ <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>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import RepositoryForm from './RepositoryForm'
|
|
|
+import TaskForm from './TaskForm'
|
|
|
+import FooterToolBar from '@/components/FooterToolbar'
|
|
|
+import { mixin, mixinDevice } from '@/utils/mixin'
|
|
|
+
|
|
|
+const fieldLabels = {
|
|
|
+ name: '仓库名',
|
|
|
+ url: '仓库域名',
|
|
|
+ owner: '仓库管理员',
|
|
|
+ approver: '审批人',
|
|
|
+ dateRange: '生效日期',
|
|
|
+ type: '仓库类型',
|
|
|
+ name2: '任务名',
|
|
|
+ url2: '任务描述',
|
|
|
+ owner2: '执行人',
|
|
|
+ approver2: '责任人',
|
|
|
+ dateRange2: '生效日期',
|
|
|
+ type2: '任务类型'
|
|
|
+}
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'AdvancedForm',
|
|
|
+ mixins: [mixin, mixinDevice],
|
|
|
+ components: {
|
|
|
+ FooterToolBar,
|
|
|
+ RepositoryForm,
|
|
|
+ TaskForm
|
|
|
+ },
|
|
|
+ data () {
|
|
|
+ return {
|
|
|
+ description: '高级表单常见于一次性输入和提交大批量数据的场景。',
|
|
|
+ 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.filter(item => item.key === key)[0]
|
|
|
+ target.editable = false
|
|
|
+ target.isNew = false
|
|
|
+ this.memberLoading = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+ toggle (key) {
|
|
|
+ const target = this.data.filter(item => item.key === key)[0]
|
|
|
+ target.editable = !target.editable
|
|
|
+ },
|
|
|
+ getRowByKey (key, newData) {
|
|
|
+ const data = this.data
|
|
|
+ return (newData || data).filter(item => item.key === key)[0]
|
|
|
+ },
|
|
|
+ cancel (key) {
|
|
|
+ const target = this.data.filter(item => item.key === key)[0]
|
|
|
+ target.editable = false
|
|
|
+ },
|
|
|
+ handleChange (value, key, column) {
|
|
|
+ const newData = [...this.data]
|
|
|
+ const target = newData.filter(item => key === item.key)[0]
|
|
|
+ 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)
|
|
|
+ console.log(tmp)
|
|
|
+ })
|
|
|
+ },
|
|
|
+ errorList (errors) {
|
|
|
+ if (!errors || errors.length === 0) {
|
|
|
+ return null
|
|
|
+ }
|
|
|
+ this.errors = Object.keys(errors).map(key => {
|
|
|
+ if (!errors[key]) {
|
|
|
+ return null
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ 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>
|