Optimized the root .gitignore to exclude virtual environments, node modules, and temp folders to ensure clean and lightweight version tracking. Co-authored-by: Cursor <cursoragent@cursor.com>
446 lines
12 KiB
Markdown
446 lines
12 KiB
Markdown
# Axure API 指南
|
||
|
||
本文档说明如何在本项目原型中使用 Axure API 实现交互功能。
|
||
|
||
## 📌 什么是 Axure API
|
||
|
||
Axure API 是本项目提供的一套接口规范,用于实现组件与 Axure 原型之间的交互。通过 Axure API,组件可以:
|
||
|
||
- **触发事件**:向外部发送事件通知
|
||
- **接收动作**:响应外部调用的动作
|
||
- **暴露变量**:提供内部状态供外部读取
|
||
- **接收配置**:从配置面板接收用户配置
|
||
- **接收数据**:从外部数据源接收数据
|
||
|
||
## 🎯 何时使用 Axure API
|
||
|
||
**使用场景**:
|
||
- 需要与 Axure 原型进行交互
|
||
- 需要在配置面板中提供可配置项
|
||
- 需要接收外部数据源
|
||
- 需要触发事件或响应动作
|
||
|
||
**不使用场景**:
|
||
- 纯展示型组件
|
||
- 不需要与外部交互的独立组件
|
||
- 标准 React 组件即可满足需求
|
||
|
||
## 📋 Axure API 接口规范
|
||
|
||
### 组件定义
|
||
|
||
使用带显式参数类型的 `forwardRef(...)` 包装组件:
|
||
|
||
```typescript
|
||
import React, { forwardRef, useImperativeHandle } from 'react';
|
||
import type { AxureProps, AxureHandle } from '../../common/axure-types';
|
||
|
||
const Component = forwardRef(function MyComponent(
|
||
innerProps: AxureProps,
|
||
ref: React.ForwardedRef<AxureHandle>,
|
||
) {
|
||
// 组件实现
|
||
|
||
useImperativeHandle(ref, function () {
|
||
return {
|
||
getVar: function (name: string) { /* ... */ },
|
||
fireAction: function (name: string, params?: any) { /* ... */ },
|
||
eventList: EVENT_LIST,
|
||
actionList: ACTION_LIST,
|
||
varList: VAR_LIST,
|
||
configList: CONFIG_LIST,
|
||
dataList: DATA_LIST
|
||
};
|
||
}, [/* 依赖项 */]);
|
||
|
||
return <div>Component Content</div>;
|
||
});
|
||
|
||
export default Component;
|
||
```
|
||
|
||
### Props 处理
|
||
|
||
```typescript
|
||
// 安全解构 props 并提供默认值
|
||
const dataSource = innerProps && innerProps.data ? innerProps.data : {};
|
||
const configSource = innerProps && innerProps.config ? innerProps.config : {};
|
||
const onEventHandler = typeof innerProps.onEvent === 'function'
|
||
? innerProps.onEvent
|
||
: function () { return undefined; };
|
||
const container = innerProps && innerProps.container ? innerProps.container : null;
|
||
|
||
// 从 config 获取配置值(避免使用 || 运算符)
|
||
const title = typeof configSource.title === 'string' && configSource.title
|
||
? configSource.title
|
||
: '默认标题';
|
||
```
|
||
|
||
## 📝 API 常量定义
|
||
|
||
### 1. 事件列表(EVENT_LIST)
|
||
|
||
定义组件可以触发的事件:
|
||
|
||
```typescript
|
||
import type { EventItem } from '../../common/axure-types';
|
||
|
||
const EVENT_LIST: EventItem[] = [
|
||
{ name: 'onClick', desc: '点击按钮时触发' },
|
||
{ name: 'onChange', desc: '值改变时触发,传递新值' },
|
||
{ name: 'onSubmit', desc: '提交表单时触发,传递表单数据' }
|
||
];
|
||
```
|
||
|
||
**触发事件**:
|
||
|
||
```typescript
|
||
import { useCallback } from 'react';
|
||
|
||
// ⚠️ 强制规则:payload 必须是字符串类型
|
||
// 如果需要传递复杂数据,请使用 JSON.stringify() 序列化
|
||
const emitEvent = useCallback(function (eventName: string, payload?: string) {
|
||
try {
|
||
onEventHandler(eventName, payload);
|
||
} catch (error) {
|
||
console.warn('事件触发失败:', eventName, error);
|
||
}
|
||
}, [onEventHandler]);
|
||
|
||
// 使用示例:传递简单字符串
|
||
emitEvent('onClick', 'button_clicked');
|
||
|
||
// 使用示例:传递复杂数据(需要序列化)
|
||
emitEvent('onChange', JSON.stringify({ timestamp: Date.now(), value: 'new_value' }));
|
||
```
|
||
|
||
### 2. 动作列表(ACTION_LIST)
|
||
|
||
定义组件可以响应的动作:
|
||
|
||
```typescript
|
||
import type { Action } from '../../common/axure-types';
|
||
|
||
// ⚠️ 强制规则:params 必须是字符串类型
|
||
// 如果需要传递复杂参数,请在 desc 中说明使用 JSON 格式
|
||
const ACTION_LIST: Action[] = [
|
||
{ name: 'reset', desc: '重置表单到初始状态' },
|
||
{ name: 'setValue', desc: '设置指定字段的值,参数格式:JSON 字符串 {"field":"字段名","value":"值"}', params: 'JSON string' },
|
||
{ name: 'submit', desc: '提交表单' }
|
||
];
|
||
```
|
||
|
||
**处理动作**:
|
||
|
||
```typescript
|
||
// ⚠️ 强制规则:params 必须是字符串类型
|
||
// 如果需要接收复杂参数,请使用 JSON.parse() 解析
|
||
const fireActionHandler = useCallback(function (name: string, params?: string) {
|
||
switch (name) {
|
||
case 'reset':
|
||
// 重置逻辑
|
||
setFormData({});
|
||
break;
|
||
case 'setValue':
|
||
// 解析 JSON 字符串参数
|
||
if (params) {
|
||
try {
|
||
const parsed = JSON.parse(params);
|
||
if (parsed.field) {
|
||
setFormData({ ...formData, [parsed.field]: parsed.value });
|
||
}
|
||
} catch (error) {
|
||
console.warn('参数解析失败:', error);
|
||
}
|
||
}
|
||
break;
|
||
case 'submit':
|
||
// 提交逻辑
|
||
handleSubmit();
|
||
break;
|
||
default:
|
||
console.warn('未知的动作:', name);
|
||
}
|
||
}, [formData]);
|
||
```
|
||
|
||
### 3. 变量列表(VAR_LIST)
|
||
|
||
定义组件暴露的内部状态:
|
||
|
||
```typescript
|
||
import type { KeyDesc } from '../../common/axure-types';
|
||
|
||
// ⚠️ name 必须使用小写 + 下划线(snake_case),详见 KeyDesc 说明
|
||
const VAR_LIST: KeyDesc[] = [
|
||
{ name: 'value', desc: '当前输入值(字符串)' },
|
||
{ name: 'is_valid', desc: '表单是否有效(布尔值)' },
|
||
{ name: 'error_message', desc: '错误信息(字符串)' }
|
||
];
|
||
```
|
||
|
||
**暴露变量**:
|
||
|
||
```typescript
|
||
useImperativeHandle(ref, function () {
|
||
return {
|
||
getVar: function (name: string) {
|
||
const vars: Record<string, any> = {
|
||
value: inputValue,
|
||
isValid: isFormValid,
|
||
errorMessage: error
|
||
};
|
||
return vars[name];
|
||
},
|
||
// ... 其他接口
|
||
};
|
||
}, [inputValue, isFormValid, error]);
|
||
```
|
||
|
||
### 4. 配置项列表(CONFIG_LIST)
|
||
|
||
定义配置面板中的可配置项:
|
||
|
||
```typescript
|
||
import type { ConfigItem } from '../../common/axure-types';
|
||
|
||
const CONFIG_LIST: ConfigItem[] = [
|
||
{
|
||
type: 'input',
|
||
: 'title',
|
||
displayName: '标题',
|
||
info: '组件顶部显示的标题文本',
|
||
initialValue: '默认标题'
|
||
},
|
||
{
|
||
type: 'inputNumber',
|
||
attributeId: 'maxLength',
|
||
displayName: '最大长度',
|
||
info: '输入框允许的最大字符数',
|
||
initialValue: 100,
|
||
min: 1,
|
||
max: 1000
|
||
},
|
||
{
|
||
type: 'switch',
|
||
attributeId: 'disabled',
|
||
displayName: '禁用',
|
||
info: '是否禁用组件',
|
||
initialValue: false
|
||
}
|
||
];
|
||
```
|
||
|
||
**配置项类型**:
|
||
- `input`:文本输入框
|
||
- `inputNumber`:数字输入框
|
||
- `switch`:开关
|
||
- `select`:下拉选择
|
||
- `color`:颜色选择器
|
||
- 更多类型参考 `/src/common/config-panel-types.ts`
|
||
|
||
### 5. 数据项列表(DATA_LIST)
|
||
|
||
定义组件接收的数据结构:
|
||
|
||
```typescript
|
||
import type { DataDesc } from '../../common/axure-types';
|
||
|
||
const DATA_LIST: DataDesc[] = [
|
||
{
|
||
name: 'users',
|
||
desc: '用户列表数据',
|
||
keys: [
|
||
{ name: 'id', desc: '用户唯一标识(数字)' },
|
||
{ name: 'name', desc: '用户姓名(字符串)' },
|
||
{ name: 'email', desc: '用户邮箱(字符串)' },
|
||
{ name: 'status', desc: '用户状态(active/inactive)' }
|
||
]
|
||
}
|
||
];
|
||
```
|
||
|
||
**使用数据**:
|
||
|
||
```typescript
|
||
const users = Array.isArray(dataSource.users) ? dataSource.users : [];
|
||
```
|
||
|
||
## 🔧 Container 容器使用
|
||
|
||
`container` 是 AxureProps 提供的 DOM 容器元素,适用于需要直接操作 DOM 的场景(如图表库):
|
||
|
||
```typescript
|
||
import { useRef, useEffect } from 'react';
|
||
import * as echarts from 'echarts/core';
|
||
|
||
const Component = forwardRef(function Chart(
|
||
innerProps: AxureProps,
|
||
ref: React.ForwardedRef<AxureHandle>,
|
||
) {
|
||
const container = innerProps && innerProps.container ? innerProps.container : null;
|
||
const chartRef = useRef<any>(null);
|
||
|
||
useEffect(function () {
|
||
if (!container) return;
|
||
|
||
if (!chartRef.current) {
|
||
chartRef.current = echarts.init(container);
|
||
chartRef.current.setOption({ /* 配置 */ });
|
||
}
|
||
|
||
return function () {
|
||
if (chartRef.current) {
|
||
chartRef.current.dispose();
|
||
chartRef.current = null;
|
||
}
|
||
};
|
||
}, [container]);
|
||
|
||
return null; // 直接使用 container 时可返回 null
|
||
});
|
||
```
|
||
|
||
## ✅ 完整示例
|
||
|
||
```typescript
|
||
/**
|
||
* @name 用户表单
|
||
*/
|
||
|
||
import './style.css';
|
||
import React, { useState, useCallback, useImperativeHandle, forwardRef } from 'react';
|
||
import { Input, Button } from 'antd';
|
||
import type {
|
||
KeyDesc,
|
||
DataDesc,
|
||
ConfigItem,
|
||
Action,
|
||
EventItem,
|
||
AxureProps,
|
||
AxureHandle
|
||
} from '../../common/axure-types';
|
||
|
||
const EVENT_LIST: EventItem[] = [
|
||
{ name: 'onSubmit', desc: '提交表单时触发,传递表单数据(JSON 字符串格式)', payload: 'JSON string' }
|
||
];
|
||
|
||
const ACTION_LIST: Action[] = [
|
||
{ name: 'reset', desc: '重置表单' },
|
||
{ name: 'setData', desc: '设置表单数据,参数格式:JSON 字符串', params: 'JSON string' }
|
||
];
|
||
|
||
// ⚠️ name 必须使用小写 + 下划线(snake_case),详见 KeyDesc 说明
|
||
const VAR_LIST: KeyDesc[] = [
|
||
{ name: 'form_data', desc: '当前表单数据(对象)' }
|
||
];
|
||
|
||
const CONFIG_LIST: ConfigItem[] = [
|
||
{
|
||
type: 'input',
|
||
attributeId: 'submitText',
|
||
displayName: '提交按钮文字',
|
||
info: '提交按钮显示的文字',
|
||
initialValue: '提交'
|
||
}
|
||
];
|
||
|
||
const DATA_LIST: DataDesc[] = [];
|
||
|
||
const Component = forwardRef(function UserForm(
|
||
innerProps: AxureProps,
|
||
ref: React.ForwardedRef<AxureHandle>,
|
||
) {
|
||
const configSource = innerProps && innerProps.config ? innerProps.config : {};
|
||
const onEventHandler = typeof innerProps.onEvent === 'function'
|
||
? innerProps.onEvent
|
||
: function () { return undefined; };
|
||
|
||
const submitText = typeof configSource.submitText === 'string' && configSource.submitText
|
||
? configSource.submitText
|
||
: '提交';
|
||
|
||
const formDataState = useState({ name: '', email: '' });
|
||
const formData = formDataState[0];
|
||
const setFormData = formDataState[1];
|
||
|
||
// ⚠️ 强制规则:payload 必须是字符串类型
|
||
const emitEvent = useCallback(function (eventName: string, payload?: string) {
|
||
try {
|
||
onEventHandler(eventName, payload);
|
||
} catch (error) {
|
||
console.warn('事件触发失败:', error);
|
||
}
|
||
}, [onEventHandler]);
|
||
|
||
const handleSubmit = useCallback(function () {
|
||
// 将复杂数据序列化为 JSON 字符串
|
||
emitEvent('onSubmit', JSON.stringify({ formData }));
|
||
}, [emitEvent, formData]);
|
||
|
||
const handleReset = useCallback(function () {
|
||
setFormData({ name: '', email: '' });
|
||
}, []);
|
||
|
||
useImperativeHandle(ref, function () {
|
||
return {
|
||
getVar: function (name: string) {
|
||
const vars: Record<string, any> = { formData };
|
||
return vars[name];
|
||
},
|
||
fireAction: function (name: string, params?: string) {
|
||
switch (name) {
|
||
case 'reset':
|
||
handleReset();
|
||
break;
|
||
case 'setData':
|
||
// 解析 JSON 字符串参数
|
||
if (params) {
|
||
try {
|
||
const parsed = JSON.parse(params);
|
||
setFormData(parsed);
|
||
} catch (error) {
|
||
console.warn('参数解析失败:', error);
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
console.warn('未知的动作:', name);
|
||
}
|
||
},
|
||
eventList: EVENT_LIST,
|
||
actionList: ACTION_LIST,
|
||
varList: VAR_LIST,
|
||
configList: CONFIG_LIST,
|
||
dataList: DATA_LIST
|
||
};
|
||
}, [formData, handleReset]);
|
||
|
||
return (
|
||
<div className="user-form">
|
||
<Input
|
||
placeholder="姓名"
|
||
value={formData.name}
|
||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||
/>
|
||
<Input
|
||
placeholder="邮箱"
|
||
value={formData.email}
|
||
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||
/>
|
||
<Button type="primary" onClick={handleSubmit}>
|
||
{submitText}
|
||
</Button>
|
||
</div>
|
||
);
|
||
});
|
||
|
||
export default Component;
|
||
```
|
||
|
||
## 📚 参考资源
|
||
|
||
- **类型定义**:`/src/common/axure-types.ts`
|
||
- **配置面板类型**:`/src/common/config-panel-types.ts`
|
||
- **示例代码**:查看 `/src/components/` 和 `/src/prototypes/` 目录下以 `ref-` 开头的文件
|