欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

基于装饰器 - 以下是我再次使用 TypeScript 处理表单配置的方法。

最编程 2024-06-10 14:19:13
...
【深圳】源创会:5.26下午、轰趴馆等你”

一、先来看实现后的代码

1. view层

<template>
    <atable v-loading="isLoading" :data-list="response.list" show-detail :entity="MaterialEntity" @on-detail="onDetail" @on-edit="onEdit" @on-delete="onDelete" @on-sort-change="request.sort = $event; getList()" />
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { ATable } from '@/airpower/component'
import { MaterialEntity } from '@/model/entity/MaterialEntity'
import { MaterialService } from '@/service/MaterialService'
import { AirRequestPage } from '@/airpower/model/AirRequestPage'
import { AirResponsePage } from '@/airpower/model/AirResponsePage'

const isLoading = ref(false)
const response = ref(new AirResponsePage<MaterialEntity>())
const request = ref(new AirRequestPage(MaterialEntity))

async function getList() {
  response.value = await MaterialService.create(isLoading).getPage(request.value)
}

async function onEdit(row: MaterialEntity) {
  await AirDialog.show(MaterialEditor, row)
  getList()
}

async function onDelete(data: MaterialEntity) {
  await MaterialService.create(isLoading).delete(data.id, '删除物料成功')
  getList()
}

async function onAdd() {
  await AirDialog.show(MaterialEditor)
  getList()
}

async function onDetail(data: MaterialEntity) {
  await AirDialog.show(MaterialDetail, data)
  getList()
}

getList()
</script>

实现的效果如下图

image.png

上面的 ATable 是我们封装的一个表格组件, 默认会按照传入 entity 属性配置的实体类进行表格列的读取——当然,每个列都支持插槽,如下:

<atable :entity="MaterialEntity">
    <template #materialname="{data}">
        我是物料: {{ data.materialName }}
    <template>
</template></template></atable>

这样,即使 materialName 属性标记了乱七八糟的配置, 都不如这个插槽的优先级来得高。

2. Model层

import { ClassName, Dictionary, FieldName } from '@/airpower/decorator/Custom'
import { TableField } from '@/airpower/decorator/TableField'
import { BaseEntity } from '@/base/BaseEntity'

/**
 * # 物料实体
 * @author Hamm
 */
@ClassName('物料')
export class MaterialEntity extends BaseEntity {
  @TableField({
    forceShow: true,
    isCopyField: true,
  })
  @FieldName('物料名称') materialName!: string

  @TableField()
  @FieldName('规格型号') materialSpc!: string

  @Dictionary(MaterialTypeDictionary)
  @TableField({
    showColor: true,
    width: 100,
  })
  @FieldName('物料类型') materialType!: MaterialType
}

/**
 * # 物料类型
 * @author Hamm
 */
export enum MaterialType {
  /**
   * # 公共物料
   */
  PUBLIC = 1,

  /**
   * # 私有物料
   */
  PRIVATE = 2
}

/**
 * # 物料类型枚举字典
 * @author Hamm
 */
export const MaterialTypeDictionary = AirDictionaryArray.createCustom<imaterialtypedictionary>([
  {
    key: MaterialType.PUBLIC,
    label: '公共物料',
    color: AirColor.SUCCESS,
    other: 1,
  },
  {
    key: MaterialType.PRIVATE,
    label: '私有物料',
    color: AirColor.NORMAL,
    other: 2,
  },
])

上述模型中一些项目中的其他依赖就不再一一附上了, 比如:

  • 基类BaseEntity(其中包含了一些固定实体必须包含的字段,如 id createTime 等等)

  • 字典数组创建字典的方法 AirDictionaryArray.createCustom<t extends idictionary>(list: T[]): AirDictionaryArray<t>

  • 自定义的特殊字典 IMaterialTypeDictionary(继承来自标准字典 IDictionary, 一些组件限定了必须传入标准字典以及其子类或接口)

  • @TableField(config: ITableFieldConfig) 的接口 ITableFieldConfig,其实就是包含了一些通用性的表格配置,如下:

    /**
     * # 表格的字段配置接口
     * @author Hamm
     */
    export interface ITableFieldConfig extends IFieldConfig {
      /**
       * # 表格字段宽度
       */
      width?: number;
    
      /**
       * # 枚举字典
       * ---
       * ### ???? 如字典配置了 ```color```, 可使用 ```showColor``` 配置项显示颜色
       */
      dictionary?: AirDictionaryArray<idictionary>;
    
      /**
       * # 如是日期 可传入转换规则
       */
      dateTimeFormatter?: AirDateTimeFormatter | string;
    
      /**
       * # 是否显示枚举字典的颜色状态灯
       * ---
       * ### ???? 如果显示 请确保传入的 ```dictionary``` 配置了 ```color```
       */
      showColor?: boolean;
    
      /**
       * # 是否字段允许排序 默认不排序
       * ---
       * ### ???? ```custom``` 为自定义排序, ```ATable``` 组件将触发 ```onSortChange``` 事件
       */
      sortable?: boolean | 'custom';
    
      /**
       * # 列对齐方式
       */
      align?: 'right' | 'left' | 'center';
    
      /**
       * # 后置文字
       * ---
       * ### ???? 一般用于显示一些类似 单位 的文本
       */
      suffixText?: string;
    
      /**
       * # 是可复制的字段
       * ---
       * ### ???? 该表格列允许一键复制
       */
      isCopyField?: boolean;
    
      /**
       * # 图片字段
       * ---
       * ### ???? 可配置 ```imageWidth```, ```imageHeight``` 等
       */
      isImage?: boolean;
    
      /**
       * # 图片的宽度 默认60
       */
      imageWidth?: number;
    
      /**
       * # 图片的高度 默认60
       */
      imageHeight?: number;
    
      /**
       * # 空数据兜底字符串
       * ---
       * ### ???? 可在 ```AirConfig.defaultTableEmptyValue``` 进行全局兜底,
       * 此配置项将优先使用 仅支持普通字段和挂载字段
       */
      emptyValue?: string
    
      // 还有超多配置,不怕麻烦,反正有 ```TypeScript``` 的自动提示, 这很爽。
    }
    

3. Service层

/**
 * # 物料接口服务
 * @author Hamm
 */
export class MaterialService extends AbstractBaseService<materialentity> {
  entityClass = MaterialEntity

  baseUrl = 'material'
}

同样的,上面的Service类也不再附上 AbstractBaseService<e extends baseentity> 了, 总之,一些通用的增删改查在 Service 基类中已经实现了

二、 为什么要这么写

其实是 Java 写习惯了, 任何东西都习惯用类或者接口来约束下,限制我需要的参数类型和数据结构。

还有 TypeScript 在 vscode 的加持下很多代码提示和重构的方便性。 当然, 只写 JavaScript 或者 AnyScript 是体会不到这种快乐的。

1. 基于 TypeScript 的代码提示和数据类型约束

image.png

image.png

2. 基于 Java Annotation 思路的装饰器配置实现

将所有关于 物料 的表格、表单、数据转换方式、验证、字典、搜索等配置信息全一股脑在 MaterialEntity 这个物料实体中通过装饰器来配置,其他使用的地方(如表格、表单、搜索框等)都可以直接传入这个实体类来读取这些配置,达到一处修改,处处生效的效果。

3. 基于面相对象思维的前端开发

本来这篇文章不太适合在面向对象上描述过多, 但上面都已经提到了很多 TypeScript 的新特性,这里再不说一下有点说不过去了。

先抨击下掘金上很多关于 TypeScript 的文章:

  • 没有新意: 搬运教程、教你配环境和入门、教你用 interface 来约束数据结构
  • 误导用户: 如上一点所说,搞得大伙都忘了 class extends implements 的存在
  • 类型体操: 正如我之前在很多掘金文章下的评论 类型体操是Typescript最好玩的,也恰好是最不应该存在的
  • 还有太多:下次在抨击吧,文章都快给带偏了。

总之,基于面向对象,前端目前还大有可为,一些很古老很传统的思维在前端上或许从来没有淋漓尽致过,但我相信,应该很快了,因为:

  • TypeScript 在前端的流行
  • 装饰器 在前端的普及(隔壁不争气的php都开始出装饰器了)
  • 面向对象、泛型、AOP思想、依赖注入等后端概念向前端的转移
  • Vue.js 这种入门相对较低的前端框架的出现
  • 贩卖前端已死焦虑等文章的出现
  • 还有很多

三、 That's all.

今天就说这么多,有兴趣的可以一起交流学习。

没错,我是个 Java仔运维仔前端仔, 不过,也不仅仅。

2023-07-26 17:31:14 更新

项目已经在Github开源: https://github.com/HammCn/AirPower4T

四、 更多文章可以查看这个专栏

https://juejin.cn/column/7249148919505682491</e></materialentity></idictionary></t></t></imaterialtypedictionary>

推荐阅读