2026-02-06 10:19:13 +08:00
2026-02-06 10:19:13 +08:00
2026-02-06 10:19:13 +08:00
2024-04-17 11:07:48 +08:00
2026-02-06 10:19:13 +08:00
2026-02-06 10:19:13 +08:00

FF 组件库

基于 React 的企业级组件库,提供完整的页面开发解决方案。

目录

核心模块

主入口 - ff

详细文档: ff-core/README.md

import ff, { http, cache, configure, func, route } from 'ff'
import { AppContext, AppGlobalParamsContext } from 'ff'

ff (default)

核心应用实例,管理用户认证、环境初始化、组件加载。

主要方法:

// 注册组件供应商
ff.setVendor(key, vendor) // => ff

// 动态加载组件
ff.getWidgetComponent(widgetPath) // => Promise<Component>
// 示例: ff.getWidgetComponent('@pkg/components/Button')

// 获取路由配置
ff.getRoutes() // => Promise<Map>

// 获取菜单配置
ff.getMenus() // => Promise<Array>

// 获取应用配置
ff.getConfigure() // => Promise<Object>

// 获取权限配置
ff.getWidgetOperationAuth() // => Promise<Array>

// 用户登录
ff.signin(username, password) // => Promise

// 用户登出
ff.signout() // => Promise

// 获取当前用户信息
ff.getUser() // => Object | null

// 初始化应用
ff.init(params) // => Promise

http

HTTP 请求封装,支持缓存、去重、Base62 编解码。

HttpResponse 类型:

HttpResponse 实现了 Promise A+ 规范,支持链式调用。

请求失败时,如果没有主动处理错误(即未使用 try-catch 包裹,也未调用 .msg().catch() 方法),系统会通过 unhandledrejection 机制自动弹窗显示错误消息。

interface HttpResponse extends Promise<any> {
  code: number        // 0 或 1 表示成功,其他表示失败
  message: string     // 响应消息
  data: any          // 响应数据
  url: string        // 请求 URL
  res?: string       // 数据资源 UUID (用于 useSubscribeRequest 订阅更新)

  // Promise A+ 方法
  then(onFulfilled, onRejected): Promise<any>
  catch(onRejected): Promise<any>

  // 扩展方法
  resp(callback): Promise<any>     // 接收完整响应对象
  msg(callback, isResp?): Promise<any>  // 弹窗显示消息后执行回调, isResp = true回调接收完整响应对象
}

API 方法:

// 基础请求 - 返回 HttpResponse 对象
http.request(config) // => HttpResponse
http.get(url, config) // => HttpResponse
http.post(url, data, config) // => HttpResponse
http.put(url, data, config) // => HttpResponse
http.delete(url, config) // => HttpResponse

// 列表请求 (自动处理 Base62 编码)
http.list(listCode, params) // => HttpResponse

// Base62 编解码
http.encode(data) // => string
http.decode(str) // => Object

// 缓存控制
http.cache(url, params) // => HttpResponse (永久缓存)
http.refreshCache(isSystem) // 清除缓存

使用示例:

// 1. 基础请求 - then() 自动解析为 data
const users = await http.get('/api/users')
// users 是 response.data

// 2. 获取完整响应对象 - 使用 .resp()
const response = await http.get('/api/users').resp(resp => {
  console.log('状态码:', resp.code)      // 0 或 1
  console.log('消息:', resp.message)    // '操作成功'
  console.log('资源UUID:', resp.res)    // 用于订阅
  console.log('URL:', resp.url)         // '/api/users'
  return resp.data
})

// 3. 成功后弹窗提示 - 使用 .msg()
await http.post('/api/users', userData).msg(() => {
  console.log('用户创建成功,消息已弹窗显示')
})

// 4. 错误处理
try {
  const data = await http.get('/api/users')
} catch (error) {
  console.error('请求失败:', error)
}

cache

缓存管理器。

// 设置缓存
cache.set(key, value, ttl) // ttl: 过期时间(秒)

// 获取缓存
cache.get(key) // => value | null

// 删除缓存
cache.delete(key)

// 清空所有缓存
cache.clear()

// 检查缓存是否存在
cache.has(key) // => boolean

func

函数执行器,支持 Web Worker 沙箱执行。

// 执行 JavaScript 代码字符串
func.exec(code, params, helpers) // => Promise<result>

// 示例
await func.exec(
  'return value * 2',
  { value: 10 },
  { getFieldValue: (name) => form.getFieldValue(name) }
) // => 20

route

路由管理器。

// 路由跳转
route.push(path, params)
route.replace(path, params)

// 获取当前路由
route.getCurrentRoute()

Hooks - ff/hooks

详细文档: ff-core/README.md#hooks-模块-hooksjs

React Hooks 工具集。

import {
  useMergedState,
  useUpdate,
  usePrevious,
  useStateWithCallback,
  useDeepEqualEffect,
  useOptions,
  useSubscribeRequest
} from 'ff/hooks'

useMergedState(defaultValue, options)

合并状态管理,结合内部 state 和外部 props。

const [value, setValue] = useMergedState(defaultValue, {
  value: props.value,
  onChange: props.onChange
})

useUpdate()

强制更新组件。

const forceUpdate = useUpdate()
// 调用 forceUpdate() 强制组件重新渲染

usePrevious(state)

获取上一次的状态值。

const prevValue = usePrevious(value)

useStateWithCallback(initialState)

带回调的状态管理。

const [state, setState] = useStateWithCallback(initialState)
setState(newState, (currentState) => {
  console.log('状态已更新:', currentState)
})

useDeepEqualEffect(callback, deps, compare)

深度对比的 useEffect,只在依赖真正变化时执行。

useDeepEqualEffect(() => {
  // 只有当 deps 深度对比不相等时才执行
}, [complexObject])

useOptions(options, language, type, form, basicForm)

选项数据处理,支持静态数组或动态 JS 代码。

const options = useOptions(
  fieldOptions,     // 选项数据或 JS 代码
  'javascript',     // 'json' | 'javascript'
  'string',         // 值类型
  form,             // 表单实例
  basicForm         // 基础表单实例
)

useSubscribeRequest(param)

WebSocket 订阅请求。

const data = useSubscribeRequest({
  uri: '/api/subscribe',
  params: { id: 123 }
})

工具函数 - ff/utils

详细文档: ff-core/README.md#工具函数模块-utilsjs

通用工具函数集合。

import {
  toPrimitive,
  getPrimitiveType,
  uuid,
  hashJSON,
  replaceKeys,
  deepSome,
  getWidgetPropsData,
  getPkgName,
  getPkgCategory,
  getPkgOwner,
  makeUniqueIdGenerator,
  hasUrlPrefix,
  getUrlWithPrefix
} from 'ff/utils'

toPrimitive(data, type)

类型转换。

toPrimitive('123', 'number')    // => 123
toPrimitive('true', 'boolean')  // => true
toPrimitive('[1,2]', 'array')   // => [1, 2]
toPrimitive('{"a":1}', 'json')  // => { a: 1 }

getPrimitiveType(value)

获取原始类型。

getPrimitiveType(123)      // => 'number'
getPrimitiveType('text')   // => 'string'
getPrimitiveType([1, 2])   // => 'array'

uuid()

生成唯一标识符。

const id = uuid() // => 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'

hashJSON(obj, bits, algo)

计算 JSON 对象的哈希值。

const hash = hashJSON({ name: 'test' }, 128, 'md5')

replaceKeys(obj, keyMap)

批量替换对象键名。

replaceKeys(
  { oldKey: 'value' },
  { oldKey: 'newKey' }
) // => { newKey: 'value' }

数据转换器 - ff/data-converter

详细文档: ff-core/README.md#数据转换器-data-converterjs

中间件模式的数据转换器。

import DataConverter from 'ff/data-converter'

使用方式:

const converter = new DataConverter([
  [middleware1, options1],
  [middleware2, options2]
])

// 转换为值
const value = await converter.toValue(rawData, context)

// 转换为渲染内容
const rendered = await converter.toRender(rawData, context, defaultValue)

WebSocket 订阅 - ff/res-ws

详细文档: ff-core/README.md

WebSocket 资源订阅管理器。

import ResWs from 'ff/res-ws'

// 创建订阅
const ws = new ResWs({
  uri: '/api/subscribe',
  params: { id: 123 },
  onMessage: (data) => console.log(data)
})

// 发送消息
ws.send(data)

// 关闭连接
ws.close()

组件模块

按钮 - ff/button

详细文档: ff-button/README.md

import Button, { auth, useButton } from 'ff/button'

组件 Props:

属性 类型 默认值 说明
type 'primary' | 'default' | 'danger' 'default' 按钮类型
size 'large' | 'middle' | 'small' 'middle' 按钮大小
name string - 按钮文本
icon string - 图标名称
loading boolean false 加载状态
disabled boolean false 禁用状态
data any - 传递给点击事件的数据源
widget string | Component | Function - 组件路径、组件或函数
widgetType 'destroy' | 'redirect' | 'func' | 'component' | 'grid-layout-form' | 'data-list' - 组件类型
widgetData object - 组件默认数据
widgetProps object - 数据结构映射
widgetSetting object - 组件设置
widgetContainerProps WidgetContainerProps - 容器配置
tooltip TooltipConfig - 提示配置
confirm ConfirmConfig - 确认对话框配置
onBeforeClick (data) => void - 点击前回调
onAfterClick (result) => void - 点击后回调

TooltipConfig 类型:

属性 类型 默认值 说明
title string - 提示内容(必填)
placement string - 位置
enabled boolean | number - 是否启用
getPopupContainer Function - 自定义容器

ConfirmConfig 类型:

属性 类型 默认值 说明
title string - 确认内容(必填)
okText string - 确定按钮文本
cancelText string - 取消按钮文本
okType string - 确定按钮类型
placement string - 位置
enabled boolean | number - 是否启用
getPopupContainer Function - 自定义容器
arrow boolean | object - 箭头配置

WidgetContainerProps 类型:

属性 类型 默认值 说明
title string - 标题
placement string - 位置top/left/right/bottom 等)
width number 260 宽度
height number - 高度
zIndex number - 层级
arrow boolean | object { pointAtCenter: true } 箭头配置
align object - 对齐配置
isPopupMountBodyContainer boolean true 是否挂载到 body
getPopupContainer Function - 自定义容器
classNames object - 自定义类名header/body/footer
className string - 根元素类名

组件:

// 默认按钮
<Button type="primary" name="点击" />

// 链接按钮
<Button.Link name="链接" />

// 圆形按钮
<Button.Circle icon="icon-add" />

// 圆角按钮
<Button.Round name="圆角" />

// 虚线按钮
<Button.Dashed name="虚线" />

// 气泡卡片按钮
<Button.Popover content={<div>内容</div>}>
  气泡按钮
</Button.Popover>

权限控制:

// 检查权限
auth.check(uuid) // => boolean

// 设置权限
auth.set(uuids) // uuids: Array<string>

Hook:

const buttonProps = useButton(props)

容器 - ff/container

详细文档: ff-container/README.md

import Container, { PageRender, PopupRender, Popup } from 'ff/container'

FFContainer Props:

属性 类型 默认值 说明
className string - 容器类名
rootClassName string - 根容器类名
style object {} 容器样式
title ReactNode - 标题内容
subTitle ReactNode - 副标题内容
actions ReactNode - 操作按钮区域
extras ReactNode - 扩展区域
children ReactNode - 主体内容

页面容器:

<Container
  title="页面标题"
  subTitle="副标题"
  actions={<Button>操作</Button>}
  extras={<div>扩展区</div>}
>
  页面内容
</Container>

Popup API:

// 打开模态框
Popup.modal(component, props, containerProps) // => Promise

// 通知消息
Popup.notification(content, options) // => Promise

// 成功提示
Popup.success(content, options) // => Promise

// 错误提示
Popup.error(content, options) // => Promise

// 确认对话框
Popup.confirm(content, options) // => Promise<boolean>

// 表单弹窗
Popup.form(fields, containerProps, formProps) // => Promise<object>

数据列表 - ff/data-list

详细文档: ff-data-list/README.md

import DataList, {
  DataListHelper,
  DataListFilter,
  DataListToolbar,
  DataListTable,
  DataListFramework
} from 'ff/data-list'

import { useDataListHelper } from 'ff/data-list/utils'

DataList Props:

属性 类型 默认值 说明
listCode string - 列表资源代码(必填)
isItemGridLayout boolean false 是否使用网格布局
theme string - 自定义主题组件路径
themeProps object - 传递给主题组件的属性
total number 0 数据总条数
page number 0 当前页码
pageSize number 30 每页条数
tab string - 当前选中的 Tab
keyword string - 搜索关键字
condition object - 筛选条件对象
sider string - 侧边栏选中项
payload object {} 额外的请求参数
layouts object | false - 自定义布局组件配置
classNames object {} 各部分的类名配置
onReload function - 重新加载回调
onClickCallback function - 操作按钮点击回调
onPageChange function - 页码变化回调
onPageSizeChange function - 每页条数变化回调
onTabChange function - Tab 变化回调
onKeywordChange function - 关键字变化回调
onConditionChange function - 筛选条件变化回调
onSiderChange function - 侧边栏变化回调

基础使用:

<DataList
  listCode="user-list"
  theme="@pkg/themes/TableTheme"
/>

使用 Hook:

const helper = useDataListHelper('user-list', {
  page: 1,
  pageSize: 20,
  condition: {}
})

// helper 包含:
// - dataSource: 数据源
// - total: 总数
// - page, pageSize: 分页信息
// - onPageChange: 分页回调
// - onConditionChange: 筛选回调
// - onReload: 刷新方法

网格布局 - ff/grid-layout

详细文档: ff-grid-layout/README.md

import GridLayout, { GridLayoutWidget, GridLayoutFramework } from 'ff/grid-layout'
import { useStructure, useField, useFnRun } from 'ff/grid-layout/utils'

GridLayout Props:

属性 类型 默认值 说明
name string - 表单名称
form FormInstance null rc-field-form 实例
basicForm FormInstance null 基础表单实例
style object {} 容器样式
className string - 容器类名
cols number 12 网格列数
rowHeight number 21 行高(px)
containerPadding [number, number] [0, 0] 容器内边距 [y, x]
itemMargin [number, number] [4, 0] 元素间距 [y, x]
formProps object {} 表单额外属性
formFields array [] 表单字段配置
fields array [] 布局字段配置
data object - 表单数据
theme string - 主题框架路径
themeProps object {} 主题框架属性
groups array [{key:'default',label:'默认'}] 分组配置

基础使用:

<GridLayout
  cols={12}
  rowHeight={21}
  fields={fields}
  data={data}
/>

使用 Hook:

// 获取远程配置
const structure = useStructure('layout-code')

// 字段值处理
const [displayValue, rawValue] = useField(name, fieldConfig)

// 执行动态函数
const [result, error] = useFnRun(jsCode, initialValue, params, helpers)

网格表单 - ff/grid-layout-form

详细文档: ff-grid-layout-form/README.md

import GridLayoutForm, { GridLayoutFormHelper } from 'ff/grid-layout-form'
import {
  useFormAction,
  useFormData,
  useFormSubmit,
  useRules
} from 'ff/grid-layout-form/utils'

GridLayoutForm Props:

属性 类型 默认值 说明
name string - 表单名称
form FormInstance null rc-field-form 实例
basicForm FormInstance null 基础表单实例
style object {} 自定义样式
className string - 自定义类名
cols number 24 网格列数
rowHeight number 16 行高(px)
itemMargin [number, number] [8, 16] 字段间距 [x, y]
containerPadding [number, number] [0, 0] 容器内边距 [y, x]
fields array [] 字段配置数组
hides array [] 隐藏字段数组
primaryKey number | string 0 主键值(编辑模式)
formProps object {} 表单额外属性
formFields array [] 表单额外字段
listenChangeFields array - 监听变化的字段名数组
listenChangeFieldsFunc string - 字段变化回调函数
onValuesChange function - 表单值变化回调
theme string - 主题框架组件路径
themeProps object {} 主题框架属性
groups array [{key:'default',label:'默认'}] 分组配置

基础使用:

const [form] = Form.useForm()

<GridLayoutForm
  form={form}
  formCode="user-form"
  primaryKey={userId}
/>

使用 Hook:

// 表单操作 (获取数据 + 提交)
const submit = useFormAction(form, 'user-form', userId)
await submit({
  serialize: (values) => values,
  onSuccess: () => console.log('成功'),
  onFail: (err) => console.error(err)
})

// 仅获取数据
const formData = useFormData('user-form', userId)

// 仅提交
const submit = useFormSubmit('user-form', userId)
await submit(form, options)

// 校验规则
const rules = useRules({
  required: true,
  type: 'string',
  max: 100,
  message: '必填且不超过100字符'
})

图标 - ff/iconfont

详细文档: ff-iconfont/README.md

import Iconfont from 'ff/iconfont'

Iconfont Props:

属性 类型 默认值 说明
type string - 图标类型(iconfont 图标名称,必填)
className string - 自定义类名
style object {} 自定义样式

初始化:

// 应用启动时初始化
Iconfont.init([
  '//at.alicdn.com/t/font_xxx.js',
  '//at.alicdn.com/t/font_yyy.js'
])

使用:

<Iconfont
  type="icon-home"
  style={{ fontSize: '24px', color: '#1890ff' }}
/>

技术栈

  • 框架: React 18.2+
  • 构建: Vite 5.2+
  • 表单: rc-field-form 1.44+
  • UI: Ant Design 5.17+
  • 样式: Less

Peer Dependencies

{
  "react": "^18.2.0",
  "react-dom": "^18.2.0",
  "antd": "^5.17.0",
  "rc-field-form": "^1.44.0",
  "lodash": "^4.17.21",
  "classnames": "^2.5.1"
}

完整示例

import React from 'react'
import ff, { http } from 'ff'
import { useDataListHelper } from 'ff/data-list/utils'
import Button from 'ff/button'
import Container from 'ff/container'
import DataList from 'ff/data-list'
import Iconfont from 'ff/iconfont'

// 初始化
Iconfont.init(['//at.alicdn.com/t/font_xxx.js'])

function App() {
  const helper = useDataListHelper('users', { page: 1 })

  const handleAdd = async () => {
    await http.post('/api/users', { name: 'New User' })
    helper.onReload()
  }

  return (
    <Container
      title="用户管理"
      actions={<Button onClick={handleAdd}>添加</Button>}
    >
      <DataList
        listCode="users"
        {...helper}
      />
    </Container>
  )
}

许可证

作者: what-00@qq.com

Description
No description provided
Readme 4 MiB