Skip to content

在开发对象相关组件时,应该一套标准API搞定所有业务 #21

@hotlong

Description

@hotlong

这是一个架构设计的核心问题。要在 Object UI 中实现“一套标准 API 搞定所有业务”,你需要使用 适配器模式 (Adapter Pattern)。
核心思想是:Object UI 组件不直接调用 axios 或 fetch,而是调用一个标准化的 DataSource 接口。
你需要在你的 RestDataSource 中实现这 5 个标准方法。

  1. 定义标准接口 (The Contract)
    在 @object-ui/types 或 @object-ui/core 中,应该已经定义好了这个接口。这是所有数据源必须遵守的“法律”。
    // packages/core/src/data/DataSource.ts

export interface DataSource {
/**

  • 查询列表 (用于表格、看板、下拉选项)
  • @param resource 资源名称 (如 'users', 'orders')
  • @param params 查询参数 (分页、过滤、排序)
    */
    find(resource: string, params?: any): Promise<any[]>;

/**

  • 查询详情 (用于详情页、编辑表单回显)
  • @param resource 资源名称
  • @param id 记录ID
    */
    findOne(resource: string, id: string | number): Promise;

/**

  • 创建记录 (用于新建表单提交)
  • @param resource 资源名称
  • @param data 表单数据
    */
    create(resource: string, data: any): Promise;

/**

  • 更新记录 (用于编辑表单提交)
  • @param resource 资源名称
  • @param id 记录ID
  • @param data 修改的数据
    */
    update(resource: string, id: string | number, data: any): Promise;

/**

  • 删除记录 (用于删除按钮)
  • @param resource 资源名称
  • @param id 记录ID
    */
    delete(resource: string, id: string | number): Promise;
    }
  1. 实现通用适配器 (The Implementation)
    你需要写一个通用的 RestDataSource 类,把上述标准方法映射到你的 REST API 规范(假设你的 API 遵循 RESTful 风格)。
    // packages/data-rest/src/RestDataSource.ts
    import { DataSource } from '@object-ui/core';

export class RestDataSource implements DataSource {
private baseUrl: string;
private headers: Record<string, string>;

constructor(options: { baseUrl: string; headers?: Record<string, string> }) {
this.baseUrl = options.baseUrl.replace(//$/, ''); // 去掉末尾斜杠
this.headers = {
'Content-Type': 'application/json',
...options.headers,
};
}

// 内部 fetch 封装
private async request(url: string, method: string, body?: any) {
const res = await fetch(${this.baseUrl}${url}, {
method,
headers: this.headers,
body: body ? JSON.stringify(body) : undefined,
});

if (!res.ok) throw new Error(`API Error: ${res.statusText}`);
return res.json();

}

// 1. 实现 Find (Grid / List) -> GET /resource
async find(resource: string, params?: any) {
// 将参数转换为 query string (如 ?page=1&status=active)
const queryString = params ? '?' + new URLSearchParams(params).toString() : '';
return this.request(/${resource}${queryString}, 'GET');
}

// 2. 实现 FindOne (Form Load) -> GET /resource/:id
async findOne(resource: string, id: string | number) {
return this.request(/${resource}/${id}, 'GET');
}

// 3. 实现 Create (Form Submit) -> POST /resource
async create(resource: string, data: any) {
return this.request(/${resource}, 'POST', data);
}

// 4. 实现 Update (Form Submit) -> PUT /resource/:id
async update(resource: string, id: string | number, data: any) {
return this.request(/${resource}/${id}, 'PUT', data);
}

// 5. 实现 Delete (Button Action) -> DELETE /resource/:id
async delete(resource: string, id: string | number) {
return this.request(/${resource}/${id}, 'DELETE');
}
}

  1. UI 如何自动调用这些方法?
    这就是 "Schema-Driven" 的魔力所在。你的 JSON Schema 不需要写代码,只需要声明“意图”。
    场景 A:渲染一个表格 (Grid)
    Object UI 的 GridRenderer 会自动调用 dataSource.find()。
    {
    "type": "grid",
    "resource": "orders", // 👈 告诉 DataSource 查哪个资源
    "columns": [...]
    }
  • 内部行为: 组件加载时 -> 调用 dataSource.find('orders', { page: 1 })。
    场景 B:提交一个表单 (Form)
    Object UI 的 FormRenderer 会根据是否有 docId 自动判断是创建还是更新。
    {
    "type": "form",
    "resource": "orders",
    "body": [...]
    }

  • 新建模式: 点击提交 -> 调用 dataSource.create('orders', formData)。

  • 编辑模式 (传入了 id): 点击提交 -> 调用 dataSource.update('orders', id, formData)。
    场景 C:点击删除按钮 (Action)
    通过配置 Action 来触发删除。
    {
    "type": "button",
    "label": "Delete",
    "actionType": "ajax",
    "confirm": "Are you sure?",
    "api": {
    "method": "delete", // 👈 对应 DataSource.delete
    "resource": "orders",
    "id": "${_id}" // 👈 从当前行数据获取 ID
    }
    }

  1. 最终如何在项目中使用?
    在你的 Host App (前端项目) 中,只需初始化一次,全局注入。
    // src/App.tsx
    import { RestDataSource } from './data/RestDataSource';
    import { SchemaRenderer } from '@object-ui/react';

// 1. 配置你的通用后端地址
const myApi = new RestDataSource({
baseUrl: 'https://api.myapp.com/v1',
headers: { Authorization: 'Bearer xxxxx' }
});

// 2. 注入到渲染器
export default function Page() {
return (
<SchemaRenderer
dataSource={myApi} // 👈 这一步打通了所有 CRUD
schema={myPageSchema}
/>
);
}

总结
你不需要为每个页面写 fetch。
只要你的后端 API 符合一定的规范(或者你在 RestDataSource 里做好了转换),前端只需要传入一个 JSON 配置,增删改查就全部自动完成了。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions