zhouzhili / blog

个人博客,记录工作学习中遇到的一些知识点,内容以前端为主。
10 stars 1 forks source link

Vue组件扩展及权限管理的实现技巧 #15

Open zhouzhili opened 5 years ago

zhouzhili commented 5 years ago

Vue 组件扩展

最近使用 ant-design-vue 作为 UI 库进行开发,不得不说 ant-design 的外观的确比 ElementUI 要好看,ElementUI 呈现的一种灰蒙蒙的感觉,而 ant-design 就让人感觉很亮,细节动画也很多。

另外,ant-design 的开发方式偏 React,很多示例也是使用 JSX 来编写的,在开发思路上得做一个切换。

在新的项目中,有很多 table 列表以及增删改查工作,列表样式基本都统一,而且下方有分页,列表第一页为序号,如下图所示:

no

本着少写一行就绝不多写一行,因此,我们可以把 table 封装起来,将一些属性设置为默认样式,但是有时候可能又会有所不同,因此,我们既要保持 table 的灵活性,同时又配置一些默认属性,因此,我们可以继承 table 原有的属性,同时进行一些扩展,并设置一些默认属性。这样使用 JSX 十分方便,我们可以如下进行开发一个 STable 进行扩展:

import T from 'ant-design-vue/es/table/Table'

export default {
  data() {
    return {
      selectedRowKeys: [],
      selectedRows: [],
      localPagination: Object.assign(
        {
          showTotal: total => `总共有${total}条数据`,
          showSizeChanger: true,
          pageSize: 10,
          current: 1
        },
        this.pagination
      )
    }
  },
  props: Object.assign({}, T.props, {
    // 是否分页
    showPagination: {
      type: Boolean,
      default: true
    },
    // 是否展示可选框
    showSelect: {
      type: Boolean,
      default: true
    },
    // 显示序号
    showSerialNo: {
      type: Boolean,
      default: true
    }
  }),
  methods: {
    handleTableChange(pagination, filters, sorter) {
      this.localPagination = Object.assign({}, this.pagination, {
        current: pagination.current,
        pageSize: pagination.pageSize
      })
      this.$emit('change', pagination, filters, sorter)
    },
    handleRowSelect(selectedRowKeys, selectedRows) {
      this.selectedRowKeys = selectedRowKeys
      this.$emit('rowSelection', selectedRowKeys, selectedRows)
    }
  },

  render() {
    const props = {}
    Object.keys(T.props).forEach(key => {
      // 分页
      if (key === 'showPagination') {
        if (!this.showPagination) {
          this.localPagination = false
        }
      } else if (key === 'pagination') {
        props[key] = Object.assign({}, this.localPagination, this[key])
      } else if (key === 'rowSelection') {
        // 是否显示第一行的选择框
        if (this.showSelect) {
          props.rowSelection = {
            selectedRows: this.selectedRows,
            selectedRowKeys: this.selectedRowKeys,
            onChange: (selectedRowKeys, selectedRows) => {
              this.handleRowSelect(selectedRowKeys, selectedRows)
            }
          }
        }
      } else if (key === 'rowKey') {
        props[key] = record => record.id
      } else if (key === 'columns') {
        const columns = [].concat(this[key])
        // 添加序号
        if (this.showSerialNo) {
          const { pageSize, current } = this.localPagination
          columns.splice(0, 0, {
            title: '编号',
            dataIndex: 'serialNo',
            key: 'serialNo',
            align: 'center',
            customRender: (text, record, index) => (current - 1) * pageSize + index + 1
          })
        }
        columns.forEach(col => {
          col.align = col.align || 'center'
        })
        props[key] = columns
      } else {
        props[key] = this[key]
      }
    })

    const table = (
      <a-table {...{ props, scopedSlots: { ...this.$scopedSlots } }} onChange={this.handleTableChange}>
        {Object.keys(this.$slots).map(name => (
          <template slot={name}>{this.$slots[name]}</template>
        ))}
      </a-table>
    )

    return <div>{table}</div>
  }
}

在我们的组件中,我们引入ant-design-vue/es/table/Table,并在组件的 Props 中继承 Table 的所有属性,Object.assign({}, T.props,{//扩展属性}),在渲染的时候,我们可以循环 Table 的 Props 属性,如果没有设置属性,我们自己给它设置一些默认属性,最终再把所有的属性绑定到 table 上,需要注意的是,在循环 Table 的 Props 属性时候,对于引用类型不可以直接进行修改,否则会陷入循环渲染。

这样,我们既保留了ant-design-vue/es/table/Table所有可配置的属性,同时也添加了针对项目的默认属性,使用起来十分方便也易于扩展。

增删改查权限的处理

vue 权限管理很多人都说过,不过常见的都是路由权限,登录之后根据用户的权限动态生成路由。这是页面权限,现在系统的权限更变态,不止页面权限,还有页面里面的权限,也就是某些用户只能看不能编辑,某些用户只能新增不能删除等等,其实总结起来就是页面增删改查权限也得配置。

好嘛,需求来了就得开干,首先,我们得知道用户有哪些权限,其次,我们要知道页面上的操作需要哪些权限,如果用户有这个权限,我们就显示这个模块,否则,我们不显示这个。例如如果用户没有修改权限,我们就可以不显示修改按钮。这种功能我们使用 Vue 指令就很好完成,代码如下:

// config: ['GET./api/v1/cachet', 'PATCH./api/v1/users']
import config from './config'
// 判断按钮以及页面各个部分是否有权限,没有的话就会移除当前el
export default {
  bind(el, binding) {
    setTimeout(() => {
      if (!config.includes(binding.value)) {
        el.parentNode.removeChild(el)
      }
    }, 0)
  }
}

然后我们在 main.js 中注册该指令:

import permissionDirective from './directives/permission'

Vue.directive('permission', permissionDirective)

然后在页面中可以这样使用:

<a-button v-permission="'DELETE./api/v1/users'">新建</a-button>