@cqsjjb/jjb-data-provider
v0.0.6
Published
A data provider library for application state management, including permission control and dynamic field text replacement
Readme
@cqsjjb/jjb-data-provider
应用数据提供者库,提供权限管理和字段动态文本功能。
功能特性
- 🔐 权限管理:自动获取当前路由的按钮权限,支持权限检查
- 📝 字段动态文本:支持字段代码的动态替换和查询
- 🚀 自动更新:监听路由变化,自动更新权限和字段数据
- 💾 数据缓存:内存缓存机制,避免重复请求,提升性能
- 🎯 类型支持:完整的 TypeScript 类型定义
安装
yarn add @cqsjjb/jjb-data-provider或使用 npm:
npm install @cqsjjb/jjb-data-provider快速开始
1. 在应用根组件中引入 Provider
import React from 'react';
import { AppDataProvider } from '@cqsjjb/jjb-data-provider';
function App() {
return (
<AppDataProvider>
{/* 你的应用组件 */}
</AppDataProvider>
);
}
export default App;2. 使用权限控制组件
import { PermissionCode } from '@cqsjjb/jjb-data-provider';
function MyComponent() {
return (
<div>
{/* 单个权限控制 */}
<PermissionCode code="edit">
<button>编辑</button>
</PermissionCode>
{/* 多个权限控制(只要有一个权限存在就显示) */}
<PermissionCode code={['add', 'edit']}>
<button>操作</button>
</PermissionCode>
</div>
);
}3. 使用 Hooks
方式 1:使用通用 Hook(推荐)
import { useAppData } from '@cqsjjb/jjb-data-provider';
function MyComponent() {
const { checkPermission, replaceFieldCode, checkField, permissions, fields } = useAppData();
// 检查权限
const canEdit = checkPermission('edit');
const canAddOrEdit = checkPermission(['add', 'edit']);
// 替换字段代码
const message = replaceFieldCode('请输入{add}'); // 假设 fields 中有 { fieldCode: 'add', fieldRename: '姓名' }
// 结果: '请输入姓名'
// 查询字段信息
const fieldName = checkField('add'); // 返回字段重命名或空字符串
const fieldNameWithDefault = checkField('add', '默认字段名'); // 返回字段重命名或'默认字段名'
return (
<div>
{canEdit && <button>编辑</button>}
<p>{message}</p>
</div>
);
}方式 2:使用专门的权限 Hook
import { usePermission } from '@cqsjjb/jjb-data-provider';
function MyComponent() {
const checkPermission = usePermission();
const canEdit = checkPermission('edit');
return (
<div>
{canEdit && <button>编辑</button>}
</div>
);
}方式 3:使用字段 Hook
import { useField } from '@cqsjjb/jjb-data-provider';
function MyComponent() {
const { checkField, replaceFieldCode } = useField();
const fieldName = checkField('add');
const message = replaceFieldCode('请输入{add}');
return (
<div>
<label>{fieldName}</label>
<input placeholder={message} />
</div>
);
}方式 4:使用数据 Hook
import { usePermissions, useFields } from '@cqsjjb/jjb-data-provider';
function MyComponent() {
const permissions = usePermissions();
const fields = useFields();
// 遍历权限列表
const hasAnyPermission = permissions.length > 0;
// 遍历字段列表
const fieldMap = fields.reduce((acc, field) => {
acc[field.fieldCode] = field.fieldRename;
return acc;
}, {});
return (
<div>
{hasAnyPermission && <p>您有 {permissions.length} 个权限</p>}
</div>
);
}方式 5:使用 Context(传统方式)
import React, { useContext } from 'react';
import { AppDataContext } from '@cqsjjb/jjb-data-provider';
function MyComponent() {
const { checkPermission, replaceFieldCode, checkField } = useContext(AppDataContext);
// ... 使用方式同方式 1
}API 文档
AppDataProvider
Provider 组件,用于提供权限和字段数据。
Props:
| 属性 | 类型 | 必填 | 说明 | |------|------|------|------| | children | React.ReactNode | 是 | 子组件 |
功能:
- 在组件初始化时自动获取当前路由的按钮权限和字段数据
- 监听路由变化(支持 Navigation API 和 popstate 事件),自动更新权限和字段数据
- 从
sessionStorage中获取token用于 API 请求
PermissionCode
权限控制组件,根据权限 code 控制子组件的显示。
Props:
| 属性 | 类型 | 必填 | 说明 | |------|------|------|------| | code | string | string[] | 是 | 权限 code 或权限 code 数组 | | children | React.ReactNode | 是 | 子组件 |
说明:
- 如果传入数组,只要有一个权限存在就显示子组件
- 如果没有权限,子组件将不会渲染
Hooks API
useAppData
获取应用数据的通用 Hook,返回所有权限和字段相关的方法和数据。
返回值: AppDataContextValue
示例:
const { permissions, fields, checkPermission, checkField, replaceFieldCode } = useAppData();usePermission
权限检查 Hook,专门用于权限检查场景。
返回值: (permissionCode: string | string[]) => boolean
示例:
const checkPermission = usePermission();
const canEdit = checkPermission('edit');
const canAddOrEdit = checkPermission(['add', 'edit']);useField
字段相关操作 Hook,返回字段查询和替换方法。
返回值: { checkField: Function, replaceFieldCode: Function }
示例:
const { checkField, replaceFieldCode } = useField();
const fieldName = checkField('add');
const message = replaceFieldCode('请输入{add}');usePermissions
获取权限列表 Hook,返回权限数组。
返回值: string[]
示例:
const permissions = usePermissions();
// permissions: ['add', 'edit', 'delete']useFields
获取字段列表 Hook,返回字段数组。
返回值: Field[]
示例:
const fields = useFields();
// fields: [{ fieldCode: 'add', fieldRename: '姓名', fieldName: '用户姓名' }]缓存管理 API
clearRouteDataCache
清除指定路由的缓存或清空所有缓存。
参数:
path?: string- 路由路径,如果不传则清除所有缓存
示例:
import { clearRouteDataCache } from '@cqsjjb/jjb-data-provider';
// 清除指定路由的缓存
clearRouteDataCache('/some/path');
// 清除所有缓存
clearRouteDataCache();getRouteDataCache
获取指定路由的缓存数据。
参数:
path: string- 路由路径
返回值: RouteCacheData | undefined
示例:
import { getRouteDataCache } from '@cqsjjb/jjb-data-provider';
const cache = getRouteDataCache('/some/path');
if (cache) {
console.log('权限:', cache.permissions);
console.log('字段:', cache.fields);
}getAllRouteDataCache
获取所有路由的缓存数据。
返回值: Map<string, RouteCacheData>
示例:
import { getAllRouteDataCache } from '@cqsjjb/jjb-data-provider';
const allCache = getAllRouteDataCache();
allCache.forEach((data, path) => {
console.log(`路由 ${path} 的缓存:`, data);
});AppDataContext
Context 对象,通过 useContext Hook 使用(推荐使用 useAppData Hook)。
Context 值类型:
interface AppDataContextValue {
/** 权限列表 */
permissions: string[];
/** 字段列表 */
fields: Field[];
/** 检查权限方法 */
checkPermission: (permissionCode: string | string[]) => boolean;
/** 替换字段代码方法 */
replaceFieldCode: (template: string) => string;
/** 查询字段信息方法 */
checkField: (fieldCode: string, defaultValue?: string) => string;
}permissions
权限列表,字符串数组。由 Provider 自动从后端 API 获取。
fields
字段列表,包含字段的动态文本信息。
interface Field {
/** 字段代码 */
fieldCode: string;
/** 字段重命名 */
fieldRename: string;
/** 字段名称 */
fieldName: string;
}checkPermission
检查权限方法。
参数:
permissionCode: string | string[]- 权限 code 或权限 code 数组
返回值:
boolean- 是否拥有该权限(传入数组时,只要有一个权限存在就返回true)
示例:
const { checkPermission } = useContext(AppDataContext);
// 单个权限检查
const canEdit = checkPermission('edit');
// 多个权限检查(只要有一个权限存在就返回 true)
const canAddOrEdit = checkPermission(['add', 'edit']);replaceFieldCode
替换字段代码方法,将模板字符串中的字段 code 替换为 fieldRename。
参数:
template: string- 模板字符串,例如'请输入{add}'
返回值:
string- 格式化后的字符串
示例:
const { replaceFieldCode } = useContext(AppDataContext);
// 假设 fields 中有 { fieldCode: 'add', fieldRename: '姓名' }
const message = replaceFieldCode('请输入{add}'); // 返回: '请输入姓名'checkField
查询字段信息方法,根据字段code查找对应的字段重命名。
参数:
fieldCode: string- 字段代码defaultValue?: string- 默认值,当字段不存在时返回该值,默认为空字符串
返回值:
string- 字段重命名字符串,如果不存在则返回defaultValue
示例:
const { checkField } = useContext(AppDataContext);
// 使用默认值(空字符串)
const fieldName = checkField('add'); // 返回字段重命名或空字符串
// 自定义默认值
const fieldName2 = checkField('add', '默认字段名'); // 返回字段重命名或'默认字段名'使用示例
示例 1:按钮权限控制
import { PermissionCode } from '@cqsjjb/jjb-data-provider';
function UserList() {
return (
<div>
<h1>用户列表</h1>
<PermissionCode code="add">
<button>新增用户</button>
</PermissionCode>
<PermissionCode code="edit">
<button>编辑</button>
</PermissionCode>
<PermissionCode code="delete">
<button>删除</button>
</PermissionCode>
</div>
);
}示例 2:条件渲染
import { usePermission } from '@cqsjjb/jjb-data-provider';
function UserForm() {
const checkPermission = usePermission();
const canEdit = checkPermission('edit');
return (
<form>
<input type="text" disabled={!canEdit} />
{canEdit && <button type="submit">保存</button>}
</form>
);
}或使用通用 Hook:
import { useAppData } from '@cqsjjb/jjb-data-provider';
function UserForm() {
const { checkPermission } = useAppData();
const canEdit = checkPermission('edit');
return (
<form>
<input type="text" disabled={!canEdit} />
{canEdit && <button type="submit">保存</button>}
</form>
);
}示例 3:动态文本替换
import { useField } from '@cqsjjb/jjb-data-provider';
function SearchForm() {
const { replaceFieldCode } = useField();
return (
<form>
<input
type="text"
placeholder={replaceFieldCode('请输入{add}进行搜索')}
/>
<button type="submit">搜索</button>
</form>
);
}或使用通用 Hook:
import { useAppData } from '@cqsjjb/jjb-data-provider';
function SearchForm() {
const { replaceFieldCode } = useAppData();
return (
<form>
<input
type="text"
placeholder={replaceFieldCode('请输入{add}进行搜索')}
/>
<button type="submit">搜索</button>
</form>
);
}示例 4:字段信息查询
import { useField } from '@cqsjjb/jjb-data-provider';
function FieldDisplay() {
const { checkField } = useField();
const fieldName = checkField('userName', '用户名');
return (
<div>
<label>
{fieldName}:
<input type="text" />
</label>
</div>
);
}示例 5:权限列表遍历
import { usePermissions } from '@cqsjjb/jjb-data-provider';
function PermissionList() {
const permissions = usePermissions();
return (
<div>
<h3>当前权限列表:</h3>
<ul>
{permissions.map(permission => (
<li key={permission}>{permission}</li>
))}
</ul>
</div>
);
}示例 6:字段列表遍历
import { useFields } from '@cqsjjb/jjb-data-provider';
function FieldList() {
const fields = useFields();
return (
<div>
<h3>字段映射:</h3>
<table>
<thead>
<tr>
<th>字段代码</th>
<th>字段重命名</th>
</tr>
</thead>
<tbody>
{fields.map(field => (
<tr key={field.fieldCode}>
<td>{field.fieldCode}</td>
<td>{field.fieldRename}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}示例 7:组合使用多个 Hook
import { usePermission, useField, usePermissions } from '@cqsjjb/jjb-data-provider';
function ComplexComponent() {
// 只获取需要的功能,避免不必要的重渲染
const checkPermission = usePermission();
const { replaceFieldCode } = useField();
const permissions = usePermissions();
const canEdit = checkPermission('edit');
const message = replaceFieldCode('请输入{add}');
const hasPermissions = permissions.length > 0;
return (
<div>
{hasPermissions && (
<div>
<p>{message}</p>
{canEdit && <button>编辑</button>}
</div>
)}
</div>
);
}示例 8:缓存管理
import { clearRouteDataCache, getRouteDataCache } from '@cqsjjb/jjb-data-provider';
// 用户登出时清除所有缓存
function handleLogout() {
// 清除所有路由缓存
clearRouteDataCache();
// 执行登出逻辑...
}
// 权限变更后清除指定路由的缓存
function handlePermissionChange(routePath) {
// 清除指定路由的缓存,下次访问时会重新请求
clearRouteDataCache(routePath);
}
// 查看缓存数据(调试用)
function checkCache(routePath) {
const cache = getRouteDataCache(routePath);
if (cache) {
console.log('权限缓存:', cache.permissions);
console.log('字段缓存:', cache.fields);
} else {
console.log('该路由暂无缓存');
}
}API 接口说明
Provider 会自动调用以下 API 接口:
获取按钮权限
接口地址: GET /system/operation/menus/list/current/buttons-perms
请求参数:
| 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | path | string | 是 | 当前路由路径 |
请求头:
Content-Type: application/json
token: <从 sessionStorage 获取>响应数据:
{
"data": ["add", "edit", "delete"]
}获取字段动态文本
接口地址: GET /system/operation/menus/menu-field/by-path
请求参数:
| 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | path | string | 是 | 当前路由路径 |
请求头:
Content-Type: application/json
token: <从 sessionStorage 获取>响应数据:
{
"data": [
{
"fieldCode": "add",
"fieldRename": "姓名",
"fieldName": "用户姓名",
"visibleEnum": "FALSE"
}
]
}说明: 只有 visibleEnum 为 'FALSE' 的字段会被保存到 fields 中。
缓存机制
为了提升性能,减少不必要的网络请求,AppDataProvider 实现了内存缓存机制:
工作原理
- 首次访问:当首次访问某个路由时,会发起接口请求获取权限和字段数据,并将结果缓存到内存中
- 再次访问:当再次访问相同路由时,直接从缓存中读取数据,不再发起接口请求
- 缓存存储:缓存存储在模块级别的
Map对象中,以路由路径为 key
使用场景
缓存机制在以下场景特别有用:
- 频繁路由切换:用户在多个页面间快速切换时,避免重复请求
- 浏览器前进/后退:使用浏览器导航时,直接使用缓存数据
- 性能优化:减少网络请求,提升页面响应速度
缓存管理
如果需要清除缓存(例如用户权限变更、登出等场景),可以使用缓存管理 API:
import { clearRouteDataCache, getRouteDataCache, getAllRouteDataCache } from '@cqsjjb/jjb-data-provider';
// 清除指定路由的缓存
clearRouteDataCache('/some/path');
// 清除所有缓存(例如用户登出时)
clearRouteDataCache();
// 查看指定路由的缓存
const cache = getRouteDataCache('/some/path');
// 查看所有缓存
const allCache = getAllRouteDataCache();缓存数据结构
interface RouteCacheData {
/** 权限列表 */
permissions?: string[];
/** 字段列表 */
fields?: Field[];
}环境变量
Provider 会从 process.env.app.API_HOST 获取 API 地址,如果不存在则使用 window.location.origin。
注意事项
- Token 存储:组件从
sessionStorage.getItem('token')获取 token,请确保在调用 API 前已设置 token - 路由监听:组件优先使用 Navigation API 监听路由变化,如果不支持则降级使用
popstate事件 - 字段过滤:只有
visibleEnum为'FALSE'的字段会被保存到fields中 - 权限数组:当传入权限数组时,
checkPermission和PermissionCode组件使用some逻辑(只要有一个权限存在就返回true) - 缓存机制:
- 缓存存储在内存中,页面刷新后会清空
- 如果用户权限或字段配置发生变化,建议调用
clearRouteDataCache()清除缓存 - 用户登出时,建议清除所有缓存以确保数据安全
- Hook 使用建议:
- 推荐使用
useAppData获取所有功能(最常用) - 如果只需要权限检查,使用
usePermission更简洁 - 如果只需要字段操作,使用
useField更聚焦 - 如果只需要数据列表,使用
usePermissions或useFields避免不必要的重渲染
- 推荐使用
- 性能优化:使用专门的 Hook(如
usePermission、useField)可以避免在不需要的数据变化时触发组件重渲染
许可证
MIT
作者
jjb-front-team
