Build a Dynamic Material Table with AirPower-Web, TypeScript, and Vue3

Learn how to quickly set up a material management table using the AirPower-Web library with TypeScript, Vue3, and ElementPlus, covering package installation, entity and service definitions, component integration, customization options, and handling varied backend interfaces, all illustrated with code snippets and diagrams.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Build a Dynamic Material Table with AirPower-Web, TypeScript, and Vue3

Preface

❝ If you are using TypeScript, Vue3, and ElementPlus, you might want to check out this component package. ❞

Previously we mentioned using decorators to configure tables and open‑sourced the AirPower4T project, but many found sub‑repo usage inconvenient.

Therefore we split AirPower4T into independent packages:

AirPower-Transformer – core data transformation library, a wrapper around class-transformer.

AirPower-Enum – core enum library.

AirPower-i18n – core internationalization library.

AirPower-Util – core utility library.

AirPower-Web – the next library of AirPower4T, designed with decoupling.

We will focus on AirPower-Web , which provides core functionality and wraps some ElementPlus components, allowing it to replace AirPower4T.

The target table layout is shown below:

QQ_1745769395699.png
QQ_1745769395699.png

Quick Start

Initialize the project (omitted). Install the package:

Install @airpower/web

You can install via any of the following commands:

npm install @airpower/web
# or
yarn add @airpower/web
# or
cnpm install @airpower/web
# or ...

Declare the entity class for the table

The material table has columns: name (string), materialType (enum), spc (string), code (string). Use AirPower-Enum for the enum:

import { WebColor, WebEnum } from '@airpower/web';

/**
 * # Material type enum
 */
export class MaterialTypeEnum extends WebEnum {
  /** Self‑produced */
  static readonly PRODUCT = new MaterialTypeEnum(1, '自产品')
    .setColor(WebColor.SUCCESS);

  /** Purchased */
  static readonly PURCHASE = new MaterialTypeEnum(2, '外购品')
    .setColor(WebColor.WARNING);
}

Then declare the entity with decorators:

@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;
}

Implement the service for the entity

The service binds the entity and defines the base URL:

export class MaterialService extends AbstractBaseService<MaterialEntity> {
  // Bind the entity class
  entityClass = MaterialEntity;
  // Base API path
  baseUrl = 'material';
}

Now you can start building the page.

Implement the page

Create list.vue:

<template>
  <APanel>
    <!-- Use ATable component -->
    <ATable
      v-loading="isLoading"
      :data-list="response.list"
      :entity="MaterialEntity"
      :service="MaterialService"
      @xxx="onXxx"
    />

    <template #footerLeft>
      <APage :response="response" @changed="onPageChanged" />
    </template>
  </APanel>
</template>

<script lang="ts" setup>
  const {
    isLoading,
    response,
    onPageChanged,
    onXxx,
  } = useTable(MaterialService);
</script>

This completes column definition, custom column selection, and various column states.

❝ If your table needs events such as add, edit, delete, detail, sort, pagination, selection, etc., you can bind them to ATable with @xxx="onXxx" , and most of these events are available from the useTable hook. ❞

More customizations

If your backend differs in API names, data structures, or status codes, you can adjust:

Different API names

Configure baseUrl or override urlForXXX:

export class MaterialService extends AbstractBaseService<MaterialEntity> {
  entityClass = MaterialEntity;
  baseUrl = 'material';

  // Custom page endpoint
  protected urlGetPage: string = 'paaaaaaaaage';
}

Or override the method directly:

export class MaterialService extends AbstractBaseService<MaterialEntity> {
  entityClass = MaterialEntity;
  baseUrl = 'material';

  async getPage(request: QueryRequest<MaterialEntity>, apiUrl?: string): Promise<QueryResponsePage<MaterialEntity>> {
    const responsePage = await this.api('getPage', '物料').post(request, QueryResponsePage<E>);
    responsePage.list = responsePage.list.map(json => Transformer.parse(json, this.entityClass));
    return responsePage;
  }
}

Different data structures

Override relevant methods to adapt to your response format.

Different status codes

Configure global HTTP success code via WebConfig:

// main.ts
WebConfig.successCode = 200000;
// other configurations

Or rewrite service methods to use your own logic.

What else is provided

The library is used in the SPMS_Web project; see the open‑source repository for more examples.

Design diagrams are also available.

That’s all for today.

Design image:

28f801113dca0f76083404c092c82d38.jpg
28f801113dca0f76083404c092c82d38.jpg
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

TypeScriptFrontend DevelopmentVue3table componentAirPower-WebElementPlus
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.