# FF 组件库 基于 React 的企业级组件库,提供完整的页面开发解决方案。 ## 目录 - [核心模块](#核心模块) - [主入口 - `ff`](#主入口---ff) - [ff (default)](#ff-default) - [http](#http) - [cache](#cache) - [func](#func) - [route](#route) - [Hooks - `ff/hooks`](#hooks---ffhooks) - [工具函数 - `ff/utils`](#工具函数---ffutils) - [数据转换器 - `ff/data-converter`](#数据转换器---ffdata-converter) - [WebSocket 订阅 - `ff/res-ws`](#websocket-订阅---ffres-ws) - [组件模块](#组件模块) - [按钮 - `ff/button`](#按钮---ffbutton) - [容器 - `ff/container`](#容器---ffcontainer) - [数据列表 - `ff/data-list`](#数据列表---ffdata-list) - [网格布局 - `ff/grid-layout`](#网格布局---ffgrid-layout) - [网格表单 - `ff/grid-layout-form`](#网格表单---ffgrid-layout-form) - [图标 - `ff/iconfont`](#图标---fficonfont) - [技术栈](#技术栈) - [Peer Dependencies](#peer-dependencies) - [完整示例](#完整示例) - [许可证](#许可证) ## 核心模块 ### 主入口 - `ff` > **详细文档:** [ff-core/README.md](src/ff-core/README.md) ```javascript import ff, { http, cache, configure, func, route } from 'ff' import { AppContext, AppGlobalParamsContext } from 'ff' ``` #### ff (default) 核心应用实例,管理用户认证、环境初始化、组件加载。 **主要方法:** ```javascript // 注册组件供应商 ff.setVendor(key, vendor) // => ff // 动态加载组件 ff.getWidgetComponent(widgetPath) // => Promise // 示例: ff.getWidgetComponent('@pkg/components/Button') // 获取路由配置 ff.getRoutes() // => Promise // 获取菜单配置 ff.getMenus() // => Promise // 获取应用配置 ff.getConfigure() // => Promise // 获取权限配置 ff.getWidgetOperationAuth() // => Promise // 用户登录 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` 机制自动弹窗显示错误消息。 ```typescript interface HttpResponse extends Promise { code: number // 0 或 1 表示成功,其他表示失败 message: string // 响应消息 data: any // 响应数据 url: string // 请求 URL res?: string // 数据资源 UUID (用于 useSubscribeRequest 订阅更新) // Promise A+ 方法 then(onFulfilled, onRejected): Promise catch(onRejected): Promise // 扩展方法 resp(callback): Promise // 接收完整响应对象 msg(callback, isResp?): Promise // 弹窗显示消息后执行回调, isResp = true,回调接收完整响应对象 } ``` **API 方法:** ```javascript // 基础请求 - 返回 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) // 清除缓存 ``` **使用示例:** ```javascript // 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 缓存管理器。 ```javascript // 设置缓存 cache.set(key, value, ttl) // ttl: 过期时间(秒) // 获取缓存 cache.get(key) // => value | null // 删除缓存 cache.delete(key) // 清空所有缓存 cache.clear() // 检查缓存是否存在 cache.has(key) // => boolean ``` #### func 函数执行器,支持 Web Worker 沙箱执行。 ```javascript // 执行 JavaScript 代码字符串 func.exec(code, params, helpers) // => Promise // 示例 await func.exec( 'return value * 2', { value: 10 }, { getFieldValue: (name) => form.getFieldValue(name) } ) // => 20 ``` #### route 路由管理器。 ```javascript // 路由跳转 route.push(path, params) route.replace(path, params) // 获取当前路由 route.getCurrentRoute() ``` --- ### Hooks - `ff/hooks` > **详细文档:** [ff-core/README.md#hooks-模块-hooksjs](src/ff-core/README.md#hooks-模块-hooksjs) React Hooks 工具集。 ```javascript import { useMergedState, useUpdate, usePrevious, useStateWithCallback, useDeepEqualEffect, useOptions, useSubscribeRequest } from 'ff/hooks' ``` #### useMergedState(defaultValue, options) 合并状态管理,结合内部 state 和外部 props。 ```javascript const [value, setValue] = useMergedState(defaultValue, { value: props.value, onChange: props.onChange }) ``` #### useUpdate() 强制更新组件。 ```javascript const forceUpdate = useUpdate() // 调用 forceUpdate() 强制组件重新渲染 ``` #### usePrevious(state) 获取上一次的状态值。 ```javascript const prevValue = usePrevious(value) ``` #### useStateWithCallback(initialState) 带回调的状态管理。 ```javascript const [state, setState] = useStateWithCallback(initialState) setState(newState, (currentState) => { console.log('状态已更新:', currentState) }) ``` #### useDeepEqualEffect(callback, deps, compare) 深度对比的 useEffect,只在依赖真正变化时执行。 ```javascript useDeepEqualEffect(() => { // 只有当 deps 深度对比不相等时才执行 }, [complexObject]) ``` #### useOptions(options, language, type, form, basicForm) 选项数据处理,支持静态数组或动态 JS 代码。 ```javascript const options = useOptions( fieldOptions, // 选项数据或 JS 代码 'javascript', // 'json' | 'javascript' 'string', // 值类型 form, // 表单实例 basicForm // 基础表单实例 ) ``` #### useSubscribeRequest(param) WebSocket 订阅请求。 ```javascript const data = useSubscribeRequest({ uri: '/api/subscribe', params: { id: 123 } }) ``` --- ### 工具函数 - `ff/utils` > **详细文档:** [ff-core/README.md#工具函数模块-utilsjs](src/ff-core/README.md#工具函数模块-utilsjs) 通用工具函数集合。 ```javascript import { toPrimitive, getPrimitiveType, uuid, hashJSON, replaceKeys, deepSome, getWidgetPropsData, getPkgName, getPkgCategory, getPkgOwner, makeUniqueIdGenerator, hasUrlPrefix, getUrlWithPrefix } from 'ff/utils' ``` #### toPrimitive(data, type) 类型转换。 ```javascript toPrimitive('123', 'number') // => 123 toPrimitive('true', 'boolean') // => true toPrimitive('[1,2]', 'array') // => [1, 2] toPrimitive('{"a":1}', 'json') // => { a: 1 } ``` #### getPrimitiveType(value) 获取原始类型。 ```javascript getPrimitiveType(123) // => 'number' getPrimitiveType('text') // => 'string' getPrimitiveType([1, 2]) // => 'array' ``` #### uuid() 生成唯一标识符。 ```javascript const id = uuid() // => 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' ``` #### hashJSON(obj, bits, algo) 计算 JSON 对象的哈希值。 ```javascript const hash = hashJSON({ name: 'test' }, 128, 'md5') ``` #### replaceKeys(obj, keyMap) 批量替换对象键名。 ```javascript replaceKeys( { oldKey: 'value' }, { oldKey: 'newKey' } ) // => { newKey: 'value' } ``` --- ### 数据转换器 - `ff/data-converter` > **详细文档:** [ff-core/README.md#数据转换器-data-converterjs](src/ff-core/README.md#数据转换器-data-converterjs) 中间件模式的数据转换器。 ```javascript import DataConverter from 'ff/data-converter' ``` **使用方式:** ```javascript 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](src/ff-core/README.md) WebSocket 资源订阅管理器。 ```javascript 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](src/ff-button/README.md) ```javascript 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` | - | 根元素类名 | **组件:** ```jsx // 默认按钮 } extras={
扩展区
} > 页面内容 ``` **Popup API:** ```javascript // 打开模态框 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 // 表单弹窗 Popup.form(fields, containerProps, formProps) // => Promise ``` --- ### 数据列表 - `ff/data-list` > **详细文档:** [ff-data-list/README.md](src/ff-data-list/README.md) ```javascript 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` | - | 侧边栏变化回调 | **基础使用:** ```jsx ``` **使用 Hook:** ```javascript 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](src/ff-grid-layout/README.md) ```javascript 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:'默认'}]` | 分组配置 | **基础使用:** ```jsx ``` **使用 Hook:** ```javascript // 获取远程配置 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](src/ff-grid-layout-form/README.md) ```javascript 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:'默认'}]` | 分组配置 | **基础使用:** ```jsx const [form] = Form.useForm() ``` **使用 Hook:** ```javascript // 表单操作 (获取数据 + 提交) 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](src/ff-iconfont/README.md) ```javascript import Iconfont from 'ff/iconfont' ``` **Iconfont Props:** | 属性 | 类型 | 默认值 | 说明 | |------|------|--------|------| | type | `string` | - | 图标类型(iconfont 图标名称,必填) | | className | `string` | - | 自定义类名 | | style | `object` | `{}` | 自定义样式 | **初始化:** ```javascript // 应用启动时初始化 Iconfont.init([ '//at.alicdn.com/t/font_xxx.js', '//at.alicdn.com/t/font_yyy.js' ]) ``` **使用:** ```jsx ``` --- ## 技术栈 - **框架:** React 18.2+ - **构建:** Vite 5.2+ - **表单:** rc-field-form 1.44+ - **UI:** Ant Design 5.17+ - **样式:** Less ## Peer Dependencies ```json { "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" } ``` ## 完整示例 ```jsx 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 ( 添加} > ) } ``` ## 许可证 作者: what-00@qq.com