2019-03-07 | antd | UNLOCK

Ant design Vue

初期使用感受

element-UI 和 Ant Design Vue 的区别

我司现在和以前的后台主要 UI 框架是 element-ui,在 Ant Design Vue 官宣之前就一直在关注,但是由于项目时间的不允许和 UI 框架稳定性的多方面考虑,一直都没机会尝试使用 antd 来搭建一个新的后台模版。

table

基本功能

自带分页、搜索、排序、自定义操作等功能
使用 slot 插入其他组件、展示数据

日常使用参数

<a-table
  :columns="columns"
  rowKey="id"
  :dataSource="data"
  :pagination="pagination"
  @change="handleTableChange"
>
  <template #cover_url="cover_url">
    <my-image :src="cover_url"></my-image>
  </template>

  <template #has_pano_new="has_pano_new">
    <span>{{has_pano_new}}slot插值</span>
  </template>

  <template #action="text, record">
    <my-button
      @click="changeStatus(record)"
      :status="record.status"
    ></my-button>
    <my-button @click="toEdit(record)" ghost type="edit"></my-button>
    <my-button @click="remove(record)" ghost type="remove"></my-button>
  </template>
</a-table>
data() {
    return {
        data: [
             {
              title: '操作',
              dataIndex: 'action',
              scopedSlots: { customRender: 'action' }
            }
        ]
    }
}

参数:
columns: 表格每行的数据和定义表头
dataSource: 表格全部数据,dataSourcecolumns 里的数据值都需要指定 key 值。对于 dataSource 默认将每列数据的 key 属性作为唯一的标识。
rowKey: 每行单独的 key,必须值,不可重复和不赋值,一般为 id > pagination: 表格自带的分页,接受一个 Obj 对象
handleTableChange: 翻页触发的回调

slot 插值
#action="text, record" 相当于v-slot:action="text,record"
slot中包含两个参数,第一个是当前列的值,参照cover_urlrecord代表的是当前行对应的值,相等于elment-uiscope.row,都可以取得当前行的所有数据

排序和筛选

不需要指定具体的 onFiltersorter 函数,而是在把筛选和排序的参数发到服务端来处理。
也可以通过 slot 来自定义筛选功能

    [
        { title: 'Name',
          dataIndex: 'name',
          sorter: true,
          scopedSlots: { customRender: 'name' }
        },
        {
            title: 'Gender',
            dataIndex: 'gender',
            filters: [
              { text: 'Male', value: 'male' },
              { text: 'Female', value: 'female' },
            ],
        }
    ]

    handleTableChange (pagination, filters, sorter) {
        // do something
    }

form

最重要的表单数据填充、验证和提交

Ant-design-vue 的表单验证

<a-form-item v-bind="mapLayout" label="图片附件">
  <hotel-upload
    v-decorator="['attach', {initialValue: [],rules:[{validator: imageLengthValidate}]}]"
    :limit="100"
    :validate="validateImageSize"
  >
  </hotel-upload>
</a-form-item>

使用到的表单验证一般都会定义在 rules 中,但是也不能排除特出情况

例如在图片上传时需要判断图片的 size,主要氛围两种情况:

  • 在上传到服务器之前,使用 new Image()URL.createObjectURL(file)提前获得本地图片大小判断,不符合 size 就阻止上传;
  • 上传之后在提交时触发提交事件,主动触发表单的验证,使用图片的 URL 实例化本地图片在判断是否合规,决定是否可以提交

简单的自定义表单验证

为什么 form 验证的 callback 必须被调用

// 表单中的input或者其他组件
// v-decorator ="['name', {initialValue: '', rules: [{required: true, message: 'some....'}, {validator: validator}]}]"

validator(file, value, callback) {
    if(value === '....'){
        // 验证没通过
        callback('验证没通过')
    } else {
        callback()
    }
    // 无论验证是否通过,callback必须被调用
}

以此类推,我们可以为表单添加多个验证规则 rules:[{required: true, message: 'some....'}, {validator: imageLengthValidate}]

验证图片宽高大小的方式

  1. 上传至服务器之后的验证(通过表单验证的形式)
// 验证方法 utils.js
export function imageInfo(src) {
  return new Promise((resolve, reject) => {
    // 通过图片上传后的地址来验证
    if (!/[jpeg|png|jpg|gif|svg|ico]/gi.test(src)) {
      message.warning('请上传图片类型文件')
      return
    }
    const img = new Image()
    img.src = src
    img.onload = function() {
      const { width, height } = this
      resolve({
        height,
        width
      })
    }
    img.onerror = e => {
      message.error('图片加载失败')
      reject(e)
    }
  })
}

//表单中附加的验证方法
const imagesSize = async (file, value, callback) => {
  let error = undefined
  // 因为验证方法中使用是异步,所以不能使用map来对数组进行遍历,使用for循环可以很好的解决这这个问题
  for (let i = 0; i < value.length; i++) {
    // 循环遍历验证,但是问题是即使某张图验证失败了,不能准确定位到不合规格的图片
    const { url } = value[i]
    const { width, height } = await imageInfo(url)
    if (width !== 1920 || height !== 1276) {
      error = '尺寸限制:1920x1276'
      break
    }
  }
  callback(error)
}
  1. 上传之前的验证
    utils.js 中定义一个公共验证的方法,在需要的时候通过 props 传到上传组件中
// utils.js
export function beforeImageInfo(file) {
  // 无论失败还是成功都需要返回一个  `Promise`
  return new Promise((resolve, reject) => {
    if (!/^image\/[jpeg|png|jpg|gif|svg|ico]/gi.test(file.type)) {
      message.warning('请上传图片类型文件')
      return
    }
    const img = new Image()
    img.src = URL.createObjectURL(file)
    img.onload = function() {
      const { width, height } = this
      resolve({
        height,
        width
      })
    }
    img.onerror = e => {
      message.error('图片加载失败')
      reject(e)
    }
  })
}
  1. 如何在封装的组件中使用验证,拦截一切我们不需要的内容
// 封装的组件中使用验证
async beforeUpload(file) {
      if (file.size / 1000 > this.size) {
        const message = '图片过大';
        this.$message.warning(message);
        return Promise.reject(message);
      }
      if (this.validate) {
        await this.validate(file);
      }
      // 上传之前验证,不通过不上传
      this.formData.token = await qnToken(this.tokenType);
      return true;
    }