Skip to content

Latest commit

 

History

History
691 lines (559 loc) · 13.9 KB

File metadata and controls

691 lines (559 loc) · 13.9 KB

自定义页面组件技术规范

概述

本文档定义了DrPlayer中用于渲染T4 Action交互功能的自定义页面组件技术规范。这些组件将根据后端返回的Action JSON配置动态生成相应的UI界面,实现灵活的用户交互体验。

架构设计

1. 组件层次结构

ActionRenderer (主渲染器)
├── ActionDialog (弹窗容器)
│   ├── InputAction (单项输入组件)
│   ├── EditAction (多行编辑组件)
│   ├── MultiInputAction (多项输入组件)
│   ├── MenuAction (单选菜单组件)
│   ├── SelectAction (多选菜单组件)
│   ├── MsgBoxAction (消息弹窗组件)
│   ├── WebViewAction (网页视图组件)
│   └── HelpAction (帮助页面组件)
├── ActionButton (动作按钮组件)
└── ActionToast (提示消息组件)

2. 数据流

Action JSON → ActionRenderer → 具体Action组件 → 用户交互 → 回调处理 → API调用

核心组件规范

1. ActionRenderer (主渲染器)

职责:

  • 解析Action JSON配置
  • 根据type字段路由到对应的组件
  • 管理组件的生命周期
  • 处理专项动作

Props接口:

interface ActionRendererProps {
  actionData: string | ActionConfig; // Action JSON字符串或对象
  onAction: (action: string, value: any) => Promise<string>; // 动作回调
  onClose?: () => void; // 关闭回调
}

interface ActionConfig {
  actionId: string;
  type: ActionType;
  [key: string]: any;
}

type ActionType = 'input' | 'edit' | 'multiInput' | 'multiInputX' | 'menu' | 'select' | 'msgbox' | 'webview' | 'help';

核心方法:

class ActionRenderer {
  // 解析Action配置
  parseActionConfig(data: string | ActionConfig): ActionConfig;
  
  // 渲染对应组件
  renderActionComponent(config: ActionConfig): React.ReactNode;
  
  // 处理专项动作
  handleSpecialAction(actionId: string, config: ActionConfig): void;
  
  // 执行动作回调
  executeAction(action: string, value: any): Promise<void>;
}

2. ActionDialog (弹窗容器)

职责:

  • 提供统一的弹窗容器
  • 处理弹窗的显示/隐藏逻辑
  • 管理弹窗的尺寸和位置
  • 处理背景遮罩和点击外部关闭

Props接口:

interface ActionDialogProps {
  visible: boolean;
  title?: string;
  width?: number;
  height?: number;
  bottom?: number;
  canceledOnTouchOutside?: boolean;
  dimAmount?: number;
  onClose: () => void;
  children: React.ReactNode;
}

样式规范:

.action-dialog {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: white;
  border-radius: 8px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
  z-index: 1000;
}

.action-dialog-mask {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 999;
}

3. InputAction (单项输入组件)

职责:

  • 渲染单项输入界面
  • 处理输入验证
  • 支持快速选择选项
  • 显示图片和二维码
  • 处理图片点击坐标

Props接口:

interface InputActionProps {
  config: InputActionConfig;
  onSubmit: (value: any) => void;
  onCancel?: () => void;
}

interface InputActionConfig {
  actionId: string;
  id: string;
  title: string;
  tip: string;
  value?: string;
  msg?: string;
  width?: number;
  button?: number;
  selectData?: string;
  imageUrl?: string;
  imageHeight?: number;
  imageClickCoord?: boolean;
  qrcode?: string;
  qrcodeSize?: string;
  timeout?: number;
  keep?: boolean;
  help?: string;
}

核心功能:

class InputAction {
  // 解析快速选择数据
  parseSelectData(selectData: string): SelectOption[];
  
  // 处理图片点击坐标
  handleImageClick(event: MouseEvent): void;
  
  // 生成二维码
  generateQRCode(url: string, size: string): string;
  
  // 验证输入值
  validateInput(value: string): boolean;
  
  // 处理超时
  handleTimeout(timeout: number): void;
}

4. MultiInputAction (多项输入组件)

职责:

  • 渲染多项输入界面
  • 支持不同类型的输入控件
  • 处理文件选择、日期选择等特殊输入
  • 验证所有输入项

Props接口:

interface MultiInputActionProps {
  config: MultiInputActionConfig;
  onSubmit: (values: Record<string, any>) => void;
  onCancel?: () => void;
}

interface MultiInputActionConfig {
  actionId: string;
  type: 'multiInput' | 'multiInputX';
  title: string;
  width?: number;
  height?: number;
  msg?: string;
  button?: number;
  input: InputItem[];
}

interface InputItem {
  id: string;
  name: string;
  tip: string;
  value?: string;
  selectData?: string;
  inputType?: number;
  multiLine?: number;
  validation?: string;
  help?: string;
  quickSelect?: boolean;
  onlyQuickSelect?: boolean;
  multiSelect?: boolean;
  selectWidth?: number;
  selectColumn?: number;
}

特殊输入类型处理:

class MultiInputAction {
  // 文件夹选择
  handleFolderSelect(itemId: string): void;
  
  // 文件选择
  handleFileSelect(itemId: string): void;
  
  // 日期选择
  handleDateSelect(itemId: string): void;
  
  // 图像选择并转换为Base64
  handleImageSelect(itemId: string): void;
  
  // 多选处理
  handleMultiSelect(itemId: string, options: string[]): void;
  
  // 输入验证
  validateItem(item: InputItem, value: string): boolean;
}

5. MenuAction (单选菜单组件)

职责:

  • 渲染单选菜单界面
  • 支持多列布局
  • 处理选项选择

Props接口:

interface MenuActionProps {
  config: MenuActionConfig;
  onSelect: (action: string) => void;
  onCancel?: () => void;
}

interface MenuActionConfig {
  actionId: string;
  title: string;
  width?: number;
  column?: number;
  option: MenuOption[];
  selectedIndex?: number;
}

interface MenuOption {
  name: string;
  action: string;
}

6. SelectAction (多选菜单组件)

职责:

  • 渲染多选菜单界面
  • 管理选中状态
  • 支持全选/反选功能

Props接口:

interface SelectActionProps {
  config: SelectActionConfig;
  onSubmit: (selectedActions: string[]) => void;
  onCancel?: () => void;
}

interface SelectActionConfig {
  actionId: string;
  title: string;
  width?: number;
  column?: number;
  option: SelectOption[];
}

interface SelectOption {
  name: string;
  action: string;
  selected?: boolean;
}

7. MsgBoxAction (消息弹窗组件)

职责:

  • 显示消息内容
  • 支持HTML格式消息
  • 显示图片

Props接口:

interface MsgBoxActionProps {
  config: MsgBoxActionConfig;
  onClose: () => void;
}

interface MsgBoxActionConfig {
  actionId: string;
  title: string;
  msg?: string;
  htmlMsg?: string;
  imageUrl?: string;
}

8. WebViewAction (网页视图组件)

职责:

  • 嵌入网页内容
  • 处理网页交互
  • 管理缩放和滚动

Props接口:

interface WebViewActionProps {
  config: WebViewActionConfig;
  onClose: () => void;
}

interface WebViewActionConfig {
  actionId: string;
  url: string;
  height?: number;
  textZoom?: number;
}

状态管理

1. Action状态

interface ActionState {
  currentAction: ActionConfig | null;
  isVisible: boolean;
  isLoading: boolean;
  error: string | null;
  history: ActionConfig[]; // 动作历史,支持返回
}

2. 状态管理器

class ActionStateManager {
  private state: ActionState;
  private listeners: ((state: ActionState) => void)[];
  
  // 显示动作
  showAction(config: ActionConfig): void;
  
  // 隐藏动作
  hideAction(): void;
  
  // 执行动作
  executeAction(action: string, value: any): Promise<void>;
  
  // 返回上一个动作
  goBack(): void;
  
  // 清空历史
  clearHistory(): void;
  
  // 订阅状态变化
  subscribe(listener: (state: ActionState) => void): () => void;
}

样式规范

1. 设计系统

:root {
  /* 颜色 */
  --action-primary: #1890ff;
  --action-success: #52c41a;
  --action-warning: #faad14;
  --action-error: #f5222d;
  --action-text: #262626;
  --action-text-secondary: #8c8c8c;
  --action-border: #d9d9d9;
  --action-background: #ffffff;
  --action-background-light: #fafafa;
  
  /* 尺寸 */
  --action-border-radius: 6px;
  --action-padding: 16px;
  --action-margin: 8px;
  
  /* 阴影 */
  --action-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  --action-shadow-hover: 0 4px 12px rgba(0, 0, 0, 0.15);
}

2. 组件样式

/* 输入框 */
.action-input {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--action-border);
  border-radius: var(--action-border-radius);
  font-size: 14px;
  transition: border-color 0.3s;
}

.action-input:focus {
  border-color: var(--action-primary);
  outline: none;
  box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}

/* 按钮 */
.action-button {
  padding: 8px 16px;
  border: 1px solid var(--action-border);
  border-radius: var(--action-border-radius);
  background: var(--action-background);
  cursor: pointer;
  transition: all 0.3s;
}

.action-button-primary {
  background: var(--action-primary);
  border-color: var(--action-primary);
  color: white;
}

.action-button:hover {
  box-shadow: var(--action-shadow-hover);
}

/* 选项 */
.action-option {
  padding: 12px;
  border: 1px solid var(--action-border);
  border-radius: var(--action-border-radius);
  cursor: pointer;
  transition: all 0.3s;
}

.action-option:hover {
  background: var(--action-background-light);
  border-color: var(--action-primary);
}

.action-option-selected {
  background: var(--action-primary);
  border-color: var(--action-primary);
  color: white;
}

API接口规范

1. Action执行接口

interface ActionAPI {
  // 执行动作
  executeAction(action: string, value: any): Promise<ActionResponse>;
}

interface ActionResponse {
  success: boolean;
  data?: any;
  action?: ActionConfig; // 新的动作配置
  toast?: string; // 提示消息
  error?: string;
}

2. 文件操作接口

interface FileAPI {
  // 选择文件夹
  selectFolder(): Promise<string>;
  
  // 选择文件
  selectFile(accept?: string): Promise<File>;
  
  // 选择图片并转换为Base64
  selectImageAsBase64(): Promise<string>;
}

错误处理

1. 错误类型

enum ActionErrorType {
  PARSE_ERROR = 'PARSE_ERROR',
  VALIDATION_ERROR = 'VALIDATION_ERROR',
  NETWORK_ERROR = 'NETWORK_ERROR',
  TIMEOUT_ERROR = 'TIMEOUT_ERROR',
  USER_CANCEL = 'USER_CANCEL'
}

interface ActionError {
  type: ActionErrorType;
  message: string;
  details?: any;
}

2. 错误处理策略

class ActionErrorHandler {
  // 处理解析错误
  handleParseError(error: Error): void;
  
  // 处理验证错误
  handleValidationError(field: string, message: string): void;
  
  // 处理网络错误
  handleNetworkError(error: Error): void;
  
  // 处理超时错误
  handleTimeoutError(): void;
  
  // 显示错误提示
  showError(error: ActionError): void;
}

性能优化

1. 组件懒加载

// 动态导入组件
const InputAction = lazy(() => import('./InputAction'));
const MultiInputAction = lazy(() => import('./MultiInputAction'));
// ...

// 使用Suspense包装
<Suspense fallback={<Loading />}>
  <ActionRenderer />
</Suspense>

2. 虚拟滚动

对于大量选项的菜单组件,使用虚拟滚动优化性能:

import { FixedSizeList as List } from 'react-window';

const VirtualMenuAction = ({ options, onSelect }) => {
  const Row = ({ index, style }) => (
    <div style={style} onClick={() => onSelect(options[index])}>
      {options[index].name}
    </div>
  );

  return (
    <List
      height={300}
      itemCount={options.length}
      itemSize={40}
    >
      {Row}
    </List>
  );
};

3. 防抖处理

对于输入组件,使用防抖优化性能:

import { debounce } from 'lodash';

const debouncedValidation = debounce((value: string) => {
  validateInput(value);
}, 300);

测试规范

1. 单元测试

describe('ActionRenderer', () => {
  test('should parse action config correctly', () => {
    const config = '{"actionId":"test","type":"input"}';
    const result = ActionRenderer.parseActionConfig(config);
    expect(result.actionId).toBe('test');
    expect(result.type).toBe('input');
  });

  test('should handle invalid JSON gracefully', () => {
    const config = 'invalid json';
    expect(() => ActionRenderer.parseActionConfig(config)).toThrow();
  });
});

2. 集成测试

describe('Action Integration', () => {
  test('should complete input action flow', async () => {
    const mockOnAction = jest.fn().mockResolvedValue('success');
    const { getByTestId } = render(
      <ActionRenderer 
        actionData='{"actionId":"test","type":"input","tip":"test"}'
        onAction={mockOnAction}
      />
    );
    
    const input = getByTestId('action-input');
    fireEvent.change(input, { target: { value: 'test value' } });
    
    const submitButton = getByTestId('action-submit');
    fireEvent.click(submitButton);
    
    await waitFor(() => {
      expect(mockOnAction).toHaveBeenCalledWith('test', { test: 'test value' });
    });
  });
});

部署和构建

1. 构建配置

// vite.config.js
export default {
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'action-components': [
            './src/components/actions/InputAction',
            './src/components/actions/MultiInputAction',
            // ...
          ]
        }
      }
    }
  }
};

2. 代码分割

// 路由级别的代码分割
const ActionPage = lazy(() => import('./pages/ActionPage'));

// 组件级别的代码分割
const ActionRenderer = lazy(() => import('./components/ActionRenderer'));

通过以上技术规范,开发团队可以构建出功能完整、性能优良、用户体验良好的Action交互组件系统。