-
Notifications
You must be signed in to change notification settings - Fork 2
在开发对象相关组件时,应该一套标准API搞定所有业务 #21
Description
这是一个架构设计的核心问题。要在 Object UI 中实现“一套标准 API 搞定所有业务”,你需要使用 适配器模式 (Adapter Pattern)。
核心思想是:Object UI 组件不直接调用 axios 或 fetch,而是调用一个标准化的 DataSource 接口。
你需要在你的 RestDataSource 中实现这 5 个标准方法。
- 定义标准接口 (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;
}
- 实现通用适配器 (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');
}
}
- 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
}
}
- 最终如何在项目中使用?
在你的 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 配置,增删改查就全部自动完成了。