likes
comments
collection
share

Vue3 + TypeScript 开发一个 ProTable(第二弹)

作者站长头像
站长
· 阅读数 15

第一弹链接欢迎访问哈:Vue3 + ts 开发一个ProTable,这期先实现了目标功能里面的两个,后序如果再有什么想法,会加入到里面的,仅仅提供一个思路。

增强了那些功能

  • 1. 用到的table原有的propscolumn原有的props, form 原有的props。
  • 2. table支持自定义slot和支持自己维护业务slot(cellField下面的组件)
  • 3. 支持表头复制功能
  • 4. 支持合并行合并列功能
  • 5. 支持用户自定义新增弹窗功能,因为表单最不确定的地方就是添加的地方,复杂的时候涉及好多逻辑,考虑要不要做,感觉还是不要这个功能啦。

代码演示

1、table支持其余props

<pro-table
  type="user"
  :columns="columns"
  :table-attrs="{
    border: true,   // 边框
    rowClassName: tableRowClassName  // 带状态表格
  }"
  :request-url="requestUrl"
  >
</pro-table>

<script lang="ts" setup>
interface User {
  date: string
  name: string
  address: string
}

const tableRowClassName = ({
  row,
  rowIndex
}: {
  row: User
  rowIndex: number
}) => {
  if (rowIndex === 1) {
    console.log(rowIndex)
    return 'warning-row'
  } else if (rowIndex === 3) {
    return 'success-row'
  }
  return ''
}
</script>

效果如下 Vue3 + TypeScript 开发一个 ProTable(第二弹)

2、column支持其余props

表格也可以设置固定列,

<pro-table
  type="user"
  :columns="columns"
  :request-url="requestUrl"
  :table-attrs="{
    border: true
  }"
  >
</pro-table>


const columns: ColumnProps[] = [
  {
    key: 'userName',
    title: '用户名',
    searchType: 'el-input',
    width: 120,
    attrs: {
      sortable: true,
      resizable: true
    }
  }
] 

显示效果

Vue3 + TypeScript 开发一个 ProTable(第二弹)

3、form支持原有props

const formAttrs = reactive<Partial<FormProps>>({
  labelPosition: 'right',
  rules: reactive<FormRules>({
    userName: [
      { required: true, message: '此字段不能为空!' }
    ]
  })
})

实现效果

Vue3 + TypeScript 开发一个 ProTable(第二弹)

4、table slotType JsonField

这里我们就使用一个展示json的插件来帮我们完成查看JSON的功能。

Vue3 + TypeScript 开发一个 ProTable(第二弹)

同样代码写代码的时候,我们只需要确定这个字段是什么类型就可以了,就不用再写一堆的具体代码了。

  const columns: ColumnProps[] = [{
    key: 'jsonParams',
    title: 'JsonField',
    searchType: 'z-json',
    search: false,
    width: 120,
    add: true,
    show: true,
    slotType: 'JsonField'
  }]
yarn add vue-json-viewer@3 

5、table slotType ProgressField

这个思路与上面的一致,....... 要展示成进度条样式的列

6、table slotType SingleArray

这个思路与上面的一致,....... [1,2,3] 或者['a', 'b', 'c']这样的数据

7、table slotType StatusTag

这块主要是将tag组件进行了封装

<template>
<el-tag
  :type="type"
  disable-transitions
  >{{ text }}
</el-tag>
</template>

<script lang="ts" setup>
import { PropType } from 'vue'

type keys = string | 0 | 1

type IStatus = {
  [key in keys] : {
    type: string
    text: string
  }
}
const props = defineProps({
  data: {
    type: String as PropType<keys>,
    required: true
  }
})

const statusMap: IStatus = {
  0: {
    type: 'danger',
    text: '禁用'
  },
  1: {
    type: 'success',
    text: '启用'
  }
}
const type = statusMap[props.data!].type
const text = statusMap[props.data!].text
</script>

使用

  const columns: ColumnProps[] = [{
    key: 'status',
    title: '状态',
    searchType: 'z-select',
    width: 100,
    options: [
      {
        label: '启用',
        value: 1
      },
      {
        label: '禁用',
        value: 0
      }
    ],
    slotType: 'StatusTag'
  }]

效果如下:

Vue3 + TypeScript 开发一个 ProTable(第二弹)

8、table slotType TableField

这个思路与上面的一致,就不再贴代码了....... 表格的列要展示成一个小的table

9、支持用户自己传入 slot

但是我觉得如果是能够抽象出去的slot类型尽量抽离出去,copy次数大于2的代码,直接抽离成一个slot组件在内部处理不同的展示形式。

代码实现,与使用方式

<pro-table
  type="user"
  :columns="columns"
  :request-url="requestUrl"
  :form-attrs="formAttrs"
  >
  <template v-slot:userName="slotProps">
    {{'666'}}
  </template>
</pro-table>


const columns: ColumnProps[] = [{
    key: 'userName',
    title: '用户名',
    searchType: 'el-input',
    width: 120,
    attrs: {
      sortable: true,
      resizable: true
    },
    slot: 'userName'
}]

内部实现,短短一行代码,就是指定slot的名字,同时将row的数据回传给外部可以进行使用,交给别人自定义。

<slot v-if="item.slot" :row="row" :name="item.slot"></slot>

实现效果:

Vue3 + TypeScript 开发一个 ProTable(第二弹)

用到的ts知识

1、Partial

让属性全部变成可选的,

// 在饿了么的column字段上面进行扩展
export type ColumnProps = {
  attrs?: Partial<TableColumnCtx<any>> // 其他属性
}

2、ExtractPropTypes

这个是从element-plus源码里面看见的,大概的作用就是,如果我们要提取公共的props类型,可以借助这个函数进行帮助。在搜这个帮助类型的时候,突然发现之前在知乎上讨论的一片文章 为什么我感觉 Vue 3 TypeScript 还是不行?,其中的有个回答,貌似也提到这个问题。其他参考文章 vue3 defineProps 引入定义的接口报错

示例用法:(暂不支持的)

export const proTableProps = {
  columns: {
    type: Array as PropType<ColumnProps[]> // 列的字段
  },
  type: {
    type: String as PropType<string> // 这个属于业务字段啦, 会在组件内部使用,后面处理如果不传的情况
  },
  requestUrl: {
    type: Object as PropType<RequestUrl>
  },
  tableAttrs: {
    type: Object as PropType<Partial<TableProps<any>>> // 这里的 T 指的是数据项 item
  },
  formAttrs: {
    type: Object as PropType<Partial<FormProps>>
  },
  beforeSubmit: {
    type: Function // 提交之前的钩子
  }
} as const

export type ProTableProps = ExtractPropTypes<typeof proTableProps>

const props = withDefaults(defineProps<ProTableProps>(), {
  columns: () => []
})

按道理来说我们这块,只是把之前的interface换成了type从外部引入的,但是很不幸,将会收到一下的报错

[@vue/compiler-sfc] type argument passed to defineProps() must be a literal type, or a reference to an interface or literal type.

export type ProTableProps = Partial<ExtractPropTypes<typeof proTableProps>>

const x: ProTableProps = {
  columns: []
}

我们来观察一下这个类型,传给defineProps按道理来说是没有问题的,找了一会放弃了。 Vue3 + TypeScript 开发一个 ProTable(第二弹)

后面我参考了element-plus中的写法,把提出去的props还是当做参数使用。这样我们就可以提取公共的props啦。可以在多处使用公共的props类型了。

export const proTableProps = {
  columns: {
    type: Array as PropType<ColumnProps[]>, // 列的字段
    default: () => {
      return []
    }
  },
  type: {
    type: String as PropType<string> // 这个属于业务字段啦, 会在组件内部使用,后面处理如果不传的情况
  },
  requestUrl: {
    type: Object as PropType<RequestUrl>,
    default: () => {
      return {}
    }
  },
  tableAttrs: {
    type: Object as PropType<Partial<TableProps<any>>>, // 这里的 T 指的是数据项 item
    default: () => {
      return {}
    }
  },
  formAttrs: {
    type: Object as PropType<Partial<FormProps>>,
    default: () => {
      return {}
    }
  },
  beforeSubmit: {
    type: Function, // 提交之前的钩子
    default () {
      return 'Default function'
    }
  }
} as const

const props = defineProps(proTableProps)

仓库地址

ProTable前端代码地址