前言
如果你恰好也在使用 TypeScript 、Vue3 、ElementPlus,那么这个组件包你可以看看。
之前我们在一些文章里提到过如何使用 装饰器 来配置表格,也开源了一个 AirPower4T 的开源项目,但后来很多人反馈说,用子仓库的方式使用有些不太方便?
于是我们将 AirPower4T 这个项目做了拆分,分别独立成了下面的几个包:
-
AirPower-Transformer 核心的数据转换库,类似
class-transformer
的封装。 -
AirPower-Enum 核心的枚举封装库,这个也在之前的文章中提到过。
-
AirPower-i18n 核心的国际化封装库,在我们关于 I18n 的文章中也提到过。
-
AirPower-Util 核心的工具封装库。
-
AirPower-Web AirPower4T 的下一个库,做了解耦设计。
我们今天主要讲的是 AirPower-Web 这个库,这个库提供了 AirPower-Web 的核心功能,并且还封装了 ElementPlus 的一些组件,这样,AirPower-Web 就可以作为替代 AirPower4T 的一个库使用。
我们今天要实现的表格是下面这个图:

快速开始
初始化项目我们就不再提了,这里我们从安装包开始:
安装 @airpower/web
可以选择你喜欢的方式安装:
bash
npm install @airpower/web
# or
yarn add @airpower/web
# or
cnpm install @airpower/web
# or ...
声明表格使用的实体类
我们要实现的是一个物料的表格,按后端接口文档,我们知道表格有这么几个列:
- name: 物料名称 字符串
- materialType: 物料类型 枚举值
- spc: 规格型号 字符串
- code: 物料编码 字符串
而枚举值,我们使用 AirPower-Enum 这个库来封装:
ts
import { WebColor, WebEnum } from '@airpower/web'
/**
* # 物料类型枚举
*/
export class MaterialTypeEnum extends WebEnum {
/** 自产品 */
static readonly PRODUCT = new MaterialTypeEnum(1, '自产品')
// 设置图中的灯,后面装饰器会用到
.setColor(WebColor.SUCCESS)
/** 外购品 */
static readonly PURCHASE = new MaterialTypeEnum(2, '外购品')
.setColor(WebColor.WARNING)
}
于是我们可以很轻易的声明出这个物料的实体类,
请注意仔细看下面的一些注释:
ts
@Model({
// 标记这个东西叫做 物料。。。
label: '物料',
})
export class MaterialEntity extends BaseEntity {
// 标记为表格列
@Table({
// 这个列是可以复制的
copy: true,
// 这个列在列选择器中必须展示,无法取消
force: true,
})
// 标记这个列可以搜索
@Search()
@Field({
// 标记这个列的名字
label: '物料编码',
})
code!: string
@Table()
@Search()
@Field({
label: '物料名称',
})
name!: string
@Table({
// 标记这个列要显示一个枚举的颜色灯
color: true,
// 列的宽度
width: 100,
})
@Search()
@Field({
label: '物料类型',
// 这个列要显示一个枚举字典
dictionary: MaterialTypeEnum,
})
materialType!: number
@Table({
copy: true,
})
@Field({
label: '规格型号',
})
spc!: string
}
实现实体对应的服务
有了这个实体的声明,那么我们可以使用这个物料的类来作为前后端对接数据的标准了。
当然,如果你的后端在你写完后要改,那可以继续标注一些装饰器来解决这些问题,可以参考 @airpower-transformer 这个库。
接下来,我们还要为这个实体类绑定一个服务:
ts
export class MaterialService extends AbstractBaseService<MaterialEntity> {
// 表示这个服务绑定的是 物料 这个实体类
entityClass = MaterialEntity
// 表示这个服务请求后端的 /material/xxxx
baseUrl = 'material'
}
好了,都准备好了,让我们开始写页面了
实现页面
好,接下来可以写页面 list.vue
了:
html
<template>
<APanel>
<!-- 开始使用 ATable 这个我们提供的组件 -->
<ATable
v-loading="isLoading" // 绑定一个 Loading
:data-list="response.list" // 绑定一个数据列表
:entity="MaterialEntity" // 绑定一个实体类
:service="MaterialService" // 绑定一个服务
@xxx="onXxx" // 如果你需要各种方法,可以继续加
/>
<template #footerLeft>
<APage
:response="response"
@changed="onPageChanged"
/>
</template>
</APanel>
</template>
<script lang="ts" setup>
// 从Hooks中拿出这些属性和方法
const {
isLoading,
response,
// 更多你需要的属性
onPageChanged,
onXxx // 更多你需要的事件
} = useTable(MaterialService)
</script>
这就搞定了。
我们通过这种方式实现了:
- 列的定义
- 列的自定义选择(实例图右上角的按钮)
- 列的各种状态
如果你的表格需要各种事件的处理,比如 添加、修改、删除、详情、排序、分页、选择等等等各种方法,都可以为 ATable 绑定各种
@xxx="onXxx"
,基本上很多的事件都可以从useTable
这个 Hook 中拿到。
更多的自定义
你可能会说,我们家后端的标准和你不太一样,比如:接口名字的不同,数据结构的不同,状态码的不同等等,我们来一个个解决:
接口访问的名字不同
你可以通过 baseUrl
属性来配置公共的部分,也可以通过重写 urlForXXX
属性来覆盖我们的默认的访问路径:
ts
export class MaterialService extends AbstractBaseService<MaterialEntity> {
entityClass = MaterialEntity
baseUrl = 'material'
// 则你们的接口地址是 /material/paaaaaaaaage
protected urlGetPage: string = 'paaaaaaaaage'
}
如果连 baseUrl
都需要自定义,你可以直接重写对应的方法:
ts
export class MaterialService extends AbstractBaseService<MaterialEntity> {
entityClass = MaterialEntity
baseUrl = 'material'
getPage(request: QueryRequest<MaterialEntity>, apiUrl?: string): Promise<QueryResponsePage<MaterialEntity>> {
// 等于 /wuliao/getPage
const responsePage: QueryResponsePage<E> = await this.api("getPage","物料").post(request, QueryResponsePage<E>)
responsePage.list = responsePage.list.map(json => Transformer.parse(json, this.entityClass))
return responsePage
}
}
数据结构的不同
和上面一样,你可以通过重写这部分不一样的方法来解决:
ts
export class MaterialService extends AbstractBaseService<MaterialEntity> {
entityClass = MaterialEntity
baseUrl = 'material'
getPage(request: QueryRequest<MaterialEntity>, apiUrl?: string): Promise<QueryResponsePage<MaterialEntity>> {
// 等于 /wuliao/getPage
const responsePage: YourTypeClass = await this.api(apiUrl).post(request, YourTypeClass)
}
// 如果请求的数据类型也不同,那就直接写个新方法?
// getMyPage.....
}
状态码的不同
AirPower-Web
这个包的 Http 服务也是提供了大量灵活的方案来解决这些问题:
通过 WebConfig
你可以通过 WebConfig
来配置全局的 HTTP 服务状态码:
ts
//main.ts
WebConfig.successCode = 200000
// 更多的配置
通过重写 Service 调用你自己的方法
你可以和上面重写 getPage
方法一样,重新调用一个自己的其他方法即可。
还有太多太多
我们本意是不太建议大家各种重写来解决问题的,因为 @airpower/web
这个包本身是和 AirPower4J
这个后端标准后端服务来做兼容的全栈项目的,我们建议如果使用的话,可以考虑和 AirPower4J
这个项目的数据结构做一些兼容处理 :)
还提供了些什么
我们在 SPMS_Web 这个项目里,也使用了 AirPower-Web
这个包,具体可以参考这个开源项目。
当然,你也可以看看我们的设计图。
今天就这样啦,再见了各位。
附上设计图:
