Frontend前端

ant-design-vue 遇到的问题和常见场景的实现

2019-04-24  本文已影响0人  前端girl吖

1、select/datepicker/asacader 组件菜单框显示时,滚动页面,菜单框不随父元素滚动,而是根据body页面滚动

原因: ant默认append到body
解决: 通过组件的属性getXxxContainer 修改渲染的父元素

  <template>
    <a-select
      v-decorator="['highestEdu']"
      class="highestEdu"
      placeholder="请选择最高学历"
      :getPopupContainer="() => doc.getElementsByClassName('highestEdu')[0]">
      <a-select-option
        v-for="edu in eductionOptions"
        :value="edu"
        :key="edu"> {{ edu }}</a-select-option>
    </a-select>
  </template>
  <script>
    export default {
      data () {
        return {
            doc: document
        }
     }
    }
    // 自定义封装级联组件遇到直接父组件绑定class无效或直接级联组件绑定class,会出现菜单项一点就消失的问题,需要包裹一层div绑定
// provinceCity.js
  <template>
    <div :class="popupContainer">
      <a-cascader
        :options="provinceCity"
        :placeholder="placeholder"
        v-model="city"
        @change="handleCascader"
        :getPopupContainer="getPopupContainer"></a-cascader>
    </div>
  </template>
  <script>
    export default {
      props: {
        value: { type: Array },
        placeholder: {
          type: String
        },
        popupContainer: {
          type: String
        }
      },
      methods: {
        getPopupContainer () {
          return document.getElementsByClassName(this.popupContainer)[0]
        }  
      }
    </script>
 // form.vue
  <ProvinceCity
    v-decorator="['nativePlace']"
    placeholder="请选择籍贯"
    popupContainer="nativePlace"
    @emitCascader="handleNativePlaceChange"/>

!!! 全局化配置

  <a-config-provider :getPopupContainer="getPopupContainer">
      <router-view style="min-width: 992px;"/>
   </a-config-provider>
  // script
  methods: {
    getPopupContainer (triggerNode) {
      // 触发节点 指某个页面的select,tooltip,menu等含弹框显示的组件,这些组件显示下拉框会触发该方法,不包括datapicker,可全局配置
      return triggerNode
    }
  }

2、ant 内引入moment,日期均返回moment

页面使用直接引入moment、不需要npm i

  import moment from 'moment'  

moment组件值转化

  this.form.date.format('YYYY-MM-DD')

3、form表单里upload选择xlsx文件(只选择一份,后选择的会覆盖前选择文件),最后提交上传(整个form表单form-data处理)

// http.js
postByForm (url, params) {
    return this.Axios.post(url, params, {
      headers: {
        'Content-type': 'multipart/form-data'
      }
    }).catch(e => {
      return Promise.reject(new ApiError(
        'network error', -1
      ))
    })
  }

// apis.js 
// 所有传的参数formData化
export async function updateMultiMember ({accountID, upload}) {
  const formData = new FormData()
  upload.forEach((file) => {
    formData.append('upload[]', file)
  })
  formData.append('accountID', accountID)
  formData.append('appkey', Global.appkey)
  formData.append('channel', Global.channel)
  formData.append('chainTokenArray[]', [Global.chainToken])
  let res = await saveAgents(formData)
  return res
}

// upload.vue
<template>
  <ContentWithFooter
    @emitCommit="handleUpload">
      <a-form
        class="multi-upload-form"
        :form="multiForm">
        <a-form-item
          v-bind="formItemLayout"
          label="选择文件">
          <a-upload
            accept='.xlsx'
            name="upload"
            :remove="handleRemove"
            :beforeUpload="beforeUpload"
            v-decorator="['upload',{
              valuePropName: 'fileList',
              getValueFromEvent: normFile,
              rules: [{required: true, message: '请选择文件'}]
            }]">
            <a-button>上传文件</a-button>
          </a-upload>
        </a-form-item>
      </a-form>
  </ContentWithFooter>
</template>
<script>
import { Utils } from '@/common'
import ContentWithFooter from '@/components/content-with-footer'
export default {
  name: 'member-multi',
  components: { ContentWithFooter },
  data () {
    return {
      formItemLayout: Utils.setFormLayout(2, 22),
      multiForm: this.$form.createForm(this),
      fileList: []
    }
  },
  methods: {
    handleRemove (file) {
      const index = this.fileList.indexOf(file)
      const newFileList = this.fileList.slice()
      newFileList.splice(index, 1)
      this.fileList = newFileList
    },
    beforeUpload (file) {
      this.fileList = [file]
      return false  // return false 实现手动上传
    },
    handleUpload () {
      this.multiForm.validateFields((err, values) => {
        if (!err) {
          // 这边出现一个就是,直接传values.upload,会出现类型错误,虽然打印出来的东西一样,但不再是File类型
          this.$emit('emitMultiFormSubmit', {upload: this.fileList})
        }
      })
    },
    normFile (e) {
      if (Array.isArray(e)) {
        return e
      }
      // return e && e.fileList 
      return e && [e.file]  // 如果单单一个upload组件,不在form表单里,则直接beforeUpload方法里this.fileList = [file]则会实现替换文件,只显示一条记录的动态效果,但form表单里需要在这个方法里设置
    }
  }
}

单纯的upload组件流程都是正常的:

但是,放入form表单,不再是uplaod组件上:fileList="fileList",而是通过

  v-decorator="['upload',{  // form表单项属性
     valuePropName: 'fileList',  // 表示upload组件的fileList属性
     getValueFromEvent: normFile, // 表示upload组件的fileList属性的值
     rules: [{required: true, message: '请选择文件'}]
   }

绑定获取值,这时会出现2个问题

image.png

然后就是上传列表永远只显示最后选择的文件这个过渡效果,需要在form表单项的v-decorator里配置getValueFromEvent: normFile

  normFile (e) {
      if (Array.isArray(e)) {
        return e
      }
      // return e && e.fileList 
      return e && [e.file]  // 如果单单一个upload组件,不在form表单里,则直接beforeUpload方法里this.fileList = [file]则会实现替换文件,只显示一条记录的动态效果,但form表单里需要在这个方法里设置
    }
  }

4、需要form colomu布局,但每项row

image.png
 <a-form :form="basicForm">
    <a-form-item
      label="姓名"
      v-bind="formItemLayout">
      <a-input
        v-decorator="[
          'name',
          {
            rules: [{required: true, message: '请输入姓名'}]
          }]"
        placeholder="请输入姓名"/>
    </a-form-item>
 </a-form>
<script>
  const FORMLAYOUTITEM = {
    labelCol: { span: 2 },
    wrapperCol: { span: 8 }
  }

  export default {
     data () {
        formItemLayout: FORMLAYOUTITEM 
     }
  }

5、table列表分页实现

<a-table
  :columns="columns"
  :dataSource="tableData"
  :loading="isTableLoading"
  :rowKey="record => record.id" 
  :pagination="pagination" // 分页配置
  @change="handleTableChange"/> // 分页、排序、筛选变化时触发

  <script>
    export default {
      data () {
          return {
            pagination: {
              showSizeChanger: true, // 显示当前页显示几条
              total: 0,
              pageSize: 10,
              current: 1
            },
        }
      }
    }
  methods: {
    handleTableChange (pagination) {
      this.pagination.current = pagination.current
      this.pagination.pageSize = pagination.pageSize
      this.getLists()
    }
  }

6、form+table form搜索条件变化,table分页当前页参数重置

image.png
实现:通过ant form创键的时候设置onValuesChange这个optionmemberQueryForm: this.$form.createForm(this, {onValuesChange: this.onFormValuesChange}) form任一表单项的值发生变化时的回调

7、menu 页面刷新,保留点击状态 + 路由跳转,菜单项选中项状态

<template>
  <div>
    <a-menu
      theme="dark"
      mode="inline"
      @openChange="onOpenChange"
      width="auto"
      :openKeys="openKeys"
      :selectedKeys="selectedKey"
      :defaultSelectedKeys="selectedKey">
      <a-sub-menu
        v-if="item.child"
        v-for="item of sidebars"
        :key="item.key">
        <template slot="title">{{ item.name }}</template>
        <a-menu-item
          v-for="subItem of item.child"
          :key="subItem.key"
          @click="$router.push({name:subItem.key})">{{ subItem.name }}</a-menu-item>
      </a-sub-menu>
      <a-menu-item
        v-if="!item.child"
        v-for="item of sidebars"
        :key="item.key"
        @click="$router.push({name:item.key})">{{ item.name }}</a-menu-item>
    </a-menu>
  </div>
</template>
<script>
import { SIDERBARS } from '@/const'
export default {
  name: 'Sidebar',
  data () {
    return {
      sidebars: [],
      allSubmenuKeys: [],
      openKeys: [], // 展开父菜单项
      selectedKey: [] // 选中子菜单项
    }
  },
  created () {
    // 页面刷新 菜单项激活状态
    this.updateSidebars()
  },
  watch: {
    '$route.name': function () {
    // 路由跳轉 菜单项激活状态
      this.updateSidebars()
    }
  },
  methods: {
    onOpenChange (openKeys) {
      // 只展示当前父级菜单
      const lastOpenKey = openKeys.find(key => this.openKeys.indexOf(key) === -1)
      if (this.allSubmenuKeys.indexOf(lastOpenKey) === -1) {
        this.openKeys = openKeys
      } else {
        this.openKeys = lastOpenKey ? [lastOpenKey] : []
      }
    },
    updateSidebars () {
      let that = this
      if (this.$route.path.indexOf('admin') !== -1) {
        this.sidebars = SIDERBARS.admin
      } else if (this.$route.path.indexOf('operation') !== -1) {
        this.sidebars = SIDERBARS.operation
      } else {
        this.sidebars = SIDERBARS.hr
      }
      _.forEach(this.sidebars, function (val, ind) {
        that.allSubmenuKeys.push(val.key)
      })
      if (this.$route.meta.parent) {
        this.openKeys = [this.$route.meta.parent]
      }
      if (this.$route.name) {
        this.$set(this, 'selectedKey', [this.$route.name])
      }
    }
  }
}
</script>

8、form表单编辑状态初始化 通过配置mapPropsToFields

let options = info.chainOrgName ? {
      mapPropsToFields: () => {
        return {
          chainOrgName: this.$form.createFormField({
            value: info.chainOrgName
          }),
          orgDomain: this.$form.createFormField({
            value: info.orgDomain
          }),
          description: this.$form.createFormField({
            value: info.description
          })
        }
      }
    } : {}
    this.nodeForm = this.$form.createForm(this, options)
  },
上一篇下一篇

猜你喜欢

热点阅读