跳转至

Work 业务组件开发规范

📄 创建: 刘泽伟 2026-08-18 · 修改: 刘泽伟_w 2026-08-20

https://doc.xpku.com/raw/tech/frontend/business-component-spec.md

本文档为 business-component-spec Skill 的完整规范,供 AI 按此规范开发 BAMS-Work 业务组件(devextreme-vue 表格 + antd 抽屉表单 + PageVo 接口 + UI 规范接入)。

本规范用于在 BAMS-Work 项目中按统一规范开发一个完整的业务组件,涵盖:组件骨架创建、接口封装、字典配置、devextreme-vue 增删改查表格、antd 抽屉表单、根组件配置、UI 规范接入,以及开发完成后启动开发服务器验证。

依赖检查(开发前必做)

开始开发前,先检查以下依赖是否已安装/可用,缺失时先向用户确认或补齐:

依赖 Skill(在当前 AI 工具的 Skill 目录下检查)

通过当前 AI 工具的 Skill 面板/技能列表(如 Skill 工具返回的可用技能)确认依赖 Skill 是否已加载,缺失时先向用户确认或补齐:

依赖 Skill 用途
component-page-creator 创建组件骨架
api-standard-helper PageVo 接口规范
component-ui-spec-integration UI 规范接入

Skill 目录位置:项目内自定义 Skill 位于 <项目根>/.trae/skills/;AI 工具内置/全局 Skill 位于工具安装目录的 skills 目录下(如 ~/.trae-cn/builtin/global/skills/)。以当前 AI 工具实际加载的 Skill 为准。

依赖 MCP

依赖 MCP 用途 检查方式
mcp_dx 查询 DevExpress 组件文档(DataGrid/Form 等) 查看 MCP 服务器是否启用
mcp_Zhi_Shi_Ku 读取知识库 UI 规范文档 查看 MCP 服务器是否启用

运行环境

  • 项目根目录需存在 work/scripts/create-ui-component.js(骨架脚本)
  • 当前环境 yarn 不在 PATH,需使用 nvm 下的 node:~/.nvm/versions/node/v22.14.0/bin/node(实际版本以 ls ~/.nvm/versions/node 为准)
  • 启动开发服务器前需存在 vue.config.dev.js(缺失时按 vue.config.dev_template.js 复制生成)

触发条件

当用户包含以下意图时触发:

  • "开发/创建/生成一个业务组件"
  • "实现一个增删改查的表格"
  • "使用 devextreme-vue 开发页面/表格"
  • "表单使用 antd 抽屉从右侧滑出"
  • "组件接入 UI 规范"
  • 明确提到基于 energy-ui / device-ui / mes-ui 等目录开发组件

操作流程

第 1 步:创建组件骨架

执行项目根目录预设脚本,注意当前环境 yarn 不在 PATH,需用 nvm 下的 node 直接执行脚本,并通过 echo "n" 跳过交互式 yarn install:

echo "n" | ~/.nvm/versions/node/v22.14.0/bin/node work/scripts/create-ui-component.js --dir energy-ui --name ui-xxx --desc "组件描述"

命名规范:

  • 业务组件:ui-xxx(kebab-case)
  • 页面组件:page-xxx
  • 基础组件:base-xxx(仅 base-ui 目录)

生成的标准目录结构:

ui-xxx/
├── index.js                        # 组件入口
├── package.json                    # 包名 @<目录>/ui-xxx
└── src/
    ├── component.vue               # 根组件(只做通用配置)
    ├── apis/index.js               # 接口封装
    ├── constants/index.js          # 字典配置
    ├── i18n/index.js               # 国际化(模板已生成)
    └── business/
        ├── MainContent.vue         # 主业务组件(表格)
        └── components/             # 子组件(抽屉表单等)

第 2 步:接口封装(apis/index.js)

必须严格遵循 api-standard-helper skill 的 PageVo 请求规范通用响应结构

  • 使用 @bams-app/service 提供的 bams.post / bams.get 发起请求
  • 查询参数通过 new bams.PageVo({...}) 封装
  • PageVo 字段:current(页码,默认1)、size(每页条数,默认20)、likeMap(模糊查询)、andMap(等值查询)、geMap/leMap(范围查询)、orderBy(排序)、param(扩展参数)
  • 后端响应统一为 { code, msg, data: { records, total, size, current, pages }, errorMsg },列表数据取 data.records
import { bams } from "@bams-app/service";

// 分页查询
export const getList = params => {
  const pageVo = new bams.PageVo({
    current: params.page ?? 1,
    size: params.pageSize ?? 20,
    likeMap: { code: params.keyword, name: params.keyword },
    andMap: { category: params.category, enableState: params.enableState },
    orderBy: { updatedTime: "desc" },
  });
  return bams.post(config.baseURL, "/bdXxx/list", pageVo);
};

// 新增或修改
export const saveOrUpdate = params => bams.post(config.baseURL, "/bdXxx/saveOrUpdate", params);

// 按 Id 删除
export const deleteById = id => bams.post(config.baseURL, "/bdXxx/delete/" + id);

// 批量删除
export const batchDelete = ids => bams.post(config.baseURL, "/bdXxx/batchDelete", ids);

注意:

  • 接口路径(如 /bdXxx/list)为占位约定,需按实际后端确认后替换
  • 查询参数的空值(空字符串、null、undefined)不要传入,避免污染查询条件
  • 拦截器已统一处理 code !== "200" 的异常,业务层直接消费 data 即可

第 3 步:字典配置(constants/index.js)

集中管理下拉/状态等字典:

export const enableStateOptions = [
  { label: "启用", value: 1 },
  { label: "禁用", value: 0 },
];

第 4 步:主表格组件(business/MainContent.vue)

页面组件一律使用 devextreme-vue 组件库。

依赖引入

import DxButton from "devextreme-vue/button";
import DxTextBox from "devextreme-vue/text-box";
import DxSelectBox from "devextreme-vue/select-box";
import DxDataGrid, { DxColumn, DxPaging, DxScrolling, DxSelection } from "devextreme-vue/data-grid";
import { DxPagination } from "devextreme-vue/pagination";
import message from "@bams-app/message"; // 轻提示

表格要点

  • DxDataGrid:data-source 绑定列表数据、key-expr="id"@selection-changed 记录选中行
  • DxSelection mode="multiple" show-check-boxes-mode="always":多选用于批量删除
  • 操作列:cell-template="operationCell" 配合 <template #operationCell="{ data }"> 渲染「编辑/删除」按钮,数据取 data.data
  • 列值映射:状态/类别等字典字段需映射为文本列(如 enableStateTextcategoryText
  • DxPagination 分页:v-model:page-index / v-model:page-size,监听 @option-changed 区分 pageIndexpageSize 变化触发刷新

页面结构

工具栏(搜索区 + 新增/删除按钮)
  ├── DxTextBox 关键字搜索(@enter-key 触发查询)
  ├── DxSelectBox 类别/状态筛选
  ├── 查询 / 重置按钮
  └── 新增 / 删除(删除需勾选行,二次确认)
DxDataGrid 表格
DxPagination 分页条
抽屉表单组件(v-model:visible / :mode / :edit-data / @saved)

查询参数构建

const buildQueryParams = () => {
  const params = {
    page: pageState.current,
    pageSize: pageState.size,
    keyword: searchForm.keyword?.trim() || "",
    category: searchForm.category,
    enableState: searchForm.enableState,
  };
  return params;
};

(将业务查询条件组装为接口层入参,PageVo 的 likeMap/andMap 封装放在 apis 层完成,空值由 apis 层过滤。)

第 5 步:抽屉表单组件(business/components/XxxFormDrawer.vue)

表单使用 antd Drawer 从右侧滑出,表单项使用 devextreme-vue 的 DxForm

核心实现

<a-drawer placement="right" :open="visibleState" :width="560" destroyOnClose @close="handleClose">
    <DxForm
        :form-data="formModel"
        :items="formItems"
        :col-count="2"
        :show-validation-summary="true"
        :validation-group="formValidationGroup"
        label-mode="outside"
    />
</a-drawer>

关键约定

  • Props:visiblemode(add/edit)、editData;Emits:update:visiblesaved
  • 校验:validationEngine.validateGroup(group) 提交前校验,validationEngine.resetGroup(group) 关闭时重置
  • 表单项:dataField + editorType(dxTextBox/dxSelectBox/dxNumberBox/dxTextArea)+ validationRules
  • 编辑回填:watch(visible) 打开时按 mode 初始化 formModel,编辑态回填 editData
  • 保存:saveOrUpdate 成功 → message.success → 关闭抽屉 → emit("saved") 通知父组件刷新列表
  • 数值/文本归一化:保存前对 sort 等数值、空字符串做归一化处理

第 6 步:根组件配置(component.vue)

根组件不写业务逻辑,使用 ComponentContainer 包裹(内置主题变量处理):

<template>
  <ComponentContainer>
    <MainContent />
  </ComponentContainer>
</template>
<script setup>
  import { ComponentContainer } from "@bams-app/components";
  import MainContent from "./business/MainContent.vue";
  defineOptions({
    name: "uiXxx",
    dicts: [],
    config: { pageTitle: "xxx" },
  });
</script>

第 7 步:UI 规范接入

依据知识库 tech/frontend/theme/ui-spec.md(MCP 服务 开发文档,工具 get_doc):

  1. 业务组件只消费 var(--bams-*) 变量,不操作主题状态(不使用 useThemeStore
  2. 常用替换对照(以 light 模式实际值为准校验匹配度,差异大不要强行替换):
硬编码 替换变量
#fff 背景 var(--bams-surface-bg)
链接蓝#1677ff var(--bams-link-color)
错误红#ff4d4f var(--bams-error-color)
灰色文本#999 var(--bams-text-tertiary)
主文本 var(--bams-text-color)
  1. 颜色替换前需先确认变量定义(work/packages/theme/src/styles/theme-light.lessroot-tokens.less),替换后全局 grep 确认无遗漏硬编码
  2. 局部主题仅在组件有独立主题诉求时引入(LocalThemeContainer 等,配置必须抽取到 src/theme/xxxTheme.js
  3. 弹层继承:普通全局变量方案下 devextreme/antd 弹层挂载 body 仍可继承 :root 变量,无需 getPopupContainer;若引入局部主题则必须设置

第 8 步:启动开发服务器验证(开发完成后必做)

开发完成后,引导用户带上环境标识启动开发服务器预览组件:

  1. 前置检查:确认项目根目录存在 vue.config.dev.js(缺失时执行 cp vue.config.dev_template.js vue.config.dev.js 生成,并按需修改代理)
  2. 启动命令yarn 不在 PATH 时用 node 直跑,需带环境标识与组件名):
# 方式一:yarn 可用时(dev:ui 后先跟环境标识,再跟 --component 组件名)
yarn dev:ui <环境标识> --component ui-xxx

# 方式二:当前环境(yarn 不在 PATH)
~/.nvm/versions/node/v22.14.0/bin/node work/cli/shared-env/run-shared-env-command.js ui serve <环境标识> --component ui-xxx

示例(使用 bxjc 环境启动 ui-base-data):

~/.nvm/versions/node/v22.14.0/bin/node work/cli/shared-env/run-shared-env-command.js ui serve bxjc --component ui-base-data
  1. 环境标识列表(对应 .envs/.env.dev-<标识> 文件,端口以各文件 PORT 为准):
环境标识 环境文件 说明 端口
bams .env.dev-bams BAMS 生产管理系统 8090
apex .env.dev-apex APEX 系统 8700
bxjc .env.dev-bxjc 变速箱集成系统 8085
mock .env.dev-mock Mock 模拟环境 8088
pdc .env.dev-pdc PDC 系统 8702
qls .env.dev-qls QLS 系统 8090
production .env.production 生产环境模板(仅构建,不用于 dev) -
  1. 如何创建新环境

  2. 方法一(推荐):运行交互式创建工具 yarn create-env(支持基于现有环境快速复制或逐项自定义,自动推荐可用端口并可选生成快捷启动脚本)

  3. 方法二(手动)

    cd .envs
    cp .env.dev-bams .env.dev-<新标识>
    

    然后修改以下关键配置:

    • PROXY_TARGET:后端服务地址(端口号为基准)
    • PORT:开发服务器端口,必须与 PROXY_TARGET 端口保持一致,否则代理异常
    • VUE_APP_TITLE / VUE_APP_SYS_NAME:系统名称
    • VUE_APP_SYS_CODE:系统唯一编码
    • VUE_APP_LOGIN_URL / VUE_APP_WEBSOCKET_URL:对应服务地址
    • 命名规范:文件必须为 .env.dev-<标识> 格式,标识仅允许小写字母、数字和连字符,且以字母或数字开头
    • 组件名解析:短名(如 ui-xxx)会自动在 workspaces 中匹配 scope 并解析为包名(如 @energy-ui/ui-xxx);存在多个同名组件时会交互式选择,非 TTY 环境需传完整包名或绝对路径
    • 访问地址:默认 http://localhost:<PORT>(端口取自环境文件的 PORT;若被占用 vue-cli-service 会自动切换并在终端输出提示)
    • 验证内容:打开页面后检查表格加载、搜索、新增/编辑抽屉、删除确认、分页是否正常,并观察终端有无编译错误

自检清单

  • 组件名符合 ui-xxx kebab-case 命名,目录位于指定 UI 目录下
  • 接口遵循 api-standard-helper 规范(bams.post + bams.PageVo,likeMap/andMap/orderBy),路径经后端确认
  • 表格使用 DxDataGrid + DxPagination,操作列含编辑/删除
  • 表单使用 antd Drawer(placement="right")+ DxForm,校验分组正确,保存后 emit("saved")
  • 字典集中在 constants 管理,状态/类别列已映射文本
  • 根组件使用 ComponentContainer,配置了 pageTitle
  • 样式无硬编码颜色,全部使用 var(--bams-*),颜色替换经过匹配度校验
  • 未自动格式化现有代码,保留原代码风格
  • 无 IDE 诊断错误
  • 已启动 dev 服务器验证组件可运行

注意事项

  • 开发前先完成「依赖检查」:三个 Skill、两个 MCP 是否就绪,缺少时先向用户确认
  • 当前环境 yarn 不在 PATH,建骨架/启动 dev 均用 nvm 下 node 直接执行脚本
  • 绝对禁止自动格式化现有代码,保留用户原始代码风格(工作区规则)
  • 页面组件一律使用 devextreme-vue;表单抽屉使用 antd Drawer,轻提示统一引入 @bams-app/message
  • 接口封装规范以 api-standard-helper skill 为准(bams.post / bams.get + bams.PageVo),不一致时以该 skill 为权威