生产管理系统 - 页面上路由参数缓存

This commit is contained in:
2025-11-11 10:28:02 +08:00
parent 91e2c19afd
commit 671a621315
7 changed files with 1413 additions and 154 deletions

View File

@@ -1474,4 +1474,539 @@ SearchFormPagination 组件通过配置驱动的方式,极大地简化了复
- **类型安全**:完整的 TypeScript 支持
- **功能专注**:专注搜索、展示、分页核心功能,避免过度设计
该组件可以作为项目中所有数据展示页面的标准解决方案,显著提升开发效率和代码质量。
该组件可以作为项目中所有数据展示页面的标准解决方案,显著提升开发效率和代码质量。
---
## pathsrc/components/common/searchFormPaginationnameURL参数同步功能集成规范
### 功能概述
URL参数同步功能是 SearchFormPagination 组件的高级特性能够自动将用户的搜索条件、分页状态与浏览器URL参数保持同步实现页面刷新后状态的恢复提升用户体验。
### 设计原则
#### 1. 职责分离原则
**子组件职责**SearchFormPagination
- ✅ UI 显示:渲染搜索表单、数据表格、分页组件
- ✅ 状态管理:管理内部搜索条件和分页状态
- ✅ URL 同步:自动同步状态到 URL 参数
- ✅ 状态通知:通过回调通知父组件状态变化
- ✅ 参数推导:自动从 searchFields 推导 URL 参数映射
**父组件职责**(业务页面):
- ✅ 查询逻辑:处理所有查询相关的业务逻辑
- ✅ 数据管理:管理数据、加载状态、错误处理
- ✅ API 调用:调用后端接口获取数据
#### 2. 自动参数推导原则
URL 同步功能会自动从 `searchFields` 配置中提取参数映射,无需手动配置:
```tsx
// searchFields 配置
const searchFields = [
{ key: 'search', label: '搜索', type: 'text' },
{ key: 'status', label: '状态', type: 'select' },
{ key: 'type', label: '类型', type: 'select' }
];
// 自动推导出 URL 参数映射
// {
// page: 'page',
// size: 'size',
// search: 'search', // 来自 searchFields[0].key
// status: 'status', // 来自 searchFields[1].key
// type: 'type' // 来自 searchFields[2].key
// }
// 生成的 URL?page=1&size=10&search=张三&status=active&type=admin
```
#### 3. 可选启用原则
URL 同步功能是完全可选的,不影响现有使用方式:
```tsx
// 不启用 URL 同步(默认行为)
<SearchFormPagination
searchFields={searchFields}
columns={columns}
data={data}
// ... 其他 props
/>
// 启用 URL 同步 - 极简配置
<SearchFormPagination
searchFields={searchFields}
columns={columns}
data={data}
urlSync={{ enabled: true }} // 只需这一行即可启用
// ... 其他 props
/>
```
### 接口定义
#### UrlSyncConfig - URL 同步配置
```tsx
interface UrlSyncConfig {
// 是否启用 URL 参数同步
enabled?: boolean;
// 是否在初始化时检测空 URL 并添加默认参数
initWithDefaults?: boolean;
// 默认分页参数
defaultPagination?: {
page: number;
size: number;
};
// URL 更新防抖时间(毫秒),避免频繁更新
updateDebounce?: number;
// 自定义 URL 参数名映射(可选,不配置则自动从 searchFields 提取)
paramNames?: {
page?: string;
size?: string;
[key: string]: string | undefined;
};
}
```
#### 统一查询接口(可选)
```tsx
// 新增的统一查询回调,替代传统的多个回调
onQueryChange?: (query: {
filters: Record<string, string; // 搜索条件(已处理)
pagination: { page: number; size: number }; // 分页信息(已处理)
isFromUrl?: boolean; // 是否来自URL初始化
}) => void;
```
### 使用方式
#### 1. 基础使用(推荐)
```tsx
<SearchFormPagination
searchFields={searchFields}
columns={columns}
data={state.users}
loading={state.loading}
error={state.error}
pagination={state.pagination}
onPageChange={handlePageChange}
onSizeChange={handleSizeChange}
onSearch={handleSearch}
// 启用 URL 同步 - 参数名自动从 searchFields 提取
urlSync={{
enabled: true,
initWithDefaults: true,
updateDebounce: 300
}}
/>
```
#### 2. 高级配置(自定义参数名)
```tsx
<SearchFormPagination
// ... 其他 props
urlSync={{
enabled: true,
initWithDefaults: true,
// 自定义 URL 参数名(覆盖自动提取的参数名)
paramNames: {
page: 'pageNum', // 页码参数名
size: 'pageSize', // 每页大小参数名
search: 'keyword', // 搜索框参数名(覆盖自动提取)
// status 和 type 会自动从 searchFields 提取,这里不配置
},
// 默认分页参数
defaultPagination: {
page: 1,
size: 10
},
// URL 更新防抖时间
updateDebounce: 500
}}
// 统一查询回调(推荐使用)
onQueryChange={handleQueryChange}
// 传统回调方式(向后兼容)
onSearch={handleSearch}
onPageChange={handlePageChange}
onSizeChange={handleSizeChange}
// URL 状态变化监听(可选)
onUrlStateChange={(urlState) => {
console.log('URL 状态变化:', urlState);
}}
/>
```
#### 3. 父组件统一查询处理
```tsx
// 统一查询处理函数
const handleQueryChange = useCallback((query: {
filters: Record<string, string>;
pagination: { page: number; size: number };
isFromUrl?: boolean;
}) => {
console.log('收到查询变化:', query);
// 映射过滤器字段名(根据业务需求调整)
const mappedFilters = {
searchKeyword: query.filters.search || '',
statusFilter: query.filters.status || 'all',
typeFilter: query.filters.type || 'all'
};
// 更新状态
dispatch({ type: 'SET_FILTERS', payload: mappedFilters });
dispatch({ type: 'SET_PAGINATION', payload: query.pagination });
// 执行查询
loadUsers({
resetPage: !query.isFromUrl, // URL 初始化时保持页码,否则重置
page: query.pagination.page,
filters: mappedFilters,
sortBy: state.sortBy,
sortOrder: state.sortOrder,
size: query.pagination.size
});
}, [state.sortBy, state.sortOrder, loadUsers]);
```
### 工作流程
#### 1. 页面初始化
```
用户访问页面
子组件检查 URL 参数
├─ 无参数 → 使用默认状态,父组件执行默认查询
└─ 有参数 → 解析参数 → 同步到内部状态 → 通知父组件 → 父组件执行查询
```
#### 2. 用户操作
```
用户搜索/分页操作
子组件更新内部状态
同步状态到 URL 参数(防抖处理)
通知父组件状态变化
父组件执行查询
```
#### 3. 页面刷新
```
页面刷新
子组件从 URL 读取参数
恢复内部状态
通知父组件
父组件执行查询 → 恢复用户之前的搜索状态
```
### URL 参数格式
#### 默认格式
```
# 基础搜索
?page=1&size=10&search=张三&status=active&type=admin
# 分页状态
?page=3&size=20
# 组合条件
?page=2&size=15&search=admin&status=active
```
#### 自定义参数名格式
```tsx
paramNames: {
page: 'p',
size: 'limit',
search: 'q',
status: 's',
type: 't'
}
// 生成 URL
?pageNum=2&pageSize=15&keyword=admin&status=active&type=user
```
### 技术实现要点
#### 1. 防抖处理
```tsx
// URL 更新防抖,避免频繁修改浏览器历史记录
const updateUrl = useCallback((filters, pagination) => {
if (urlUpdateTimeoutRef.current) {
clearTimeout(urlUpdateTimeoutRef.current);
}
urlUpdateTimeoutRef.current = setTimeout(() => {
// 更新 URL 参数
window.history.replaceState({}, '', newUrl);
}, urlConfig.updateDebounce);
}, []);
```
#### 2. 参数映射
```tsx
// 支持字段名映射,适应不同的 API 接口
const paramValue = urlParams.get(
urlConfig.paramNames[field.key] || field.key
);
```
#### 3. 状态同步时机
```tsx
// 搜索条件变化时同步
useEffect(() => {
if (urlConfig.enabled) {
updateUrl(filters, pagination);
}
}, [filters, urlConfig.enabled]);
// 分页变化时同步
useEffect(() => {
if (urlConfig.enabled && pagination) {
updateUrl(filters, pagination);
}
}, [pagination?.page, pagination?.size, urlConfig.enabled]);
```
### 最佳实践
#### 1. 参数命名规范
```tsx
// ✅ 推荐:使用有意义的参数名
paramNames: {
search: 'keyword', // 搜索关键词
status: 'userStatus', // 用户状态
type: 'userType', // 用户类型
page: 'pageNum', // 页码
size: 'pageSize' // 每页大小
}
// ❌ 避免:过于简化的参数名
paramNames: {
search: 's',
status: 'st',
type: 't'
}
```
#### 2. 防抖时间设置
```tsx
// ✅ 推荐:根据用户操作频率调整
urlSync: {
updateDebounce: 300 // 文本搜索300ms下拉选择立即
}
// 快速响应场景
urlSync: {
updateDebounce: 100 // 需要即时反馈的场景
}
// 性能优先场景
urlSync: {
updateDebounce: 500 // 减少频繁更新
}
```
#### 3. 默认值配置
```tsx
// ✅ 推荐:设置合理的默认值
urlSync: {
defaultPagination: {
page: 1,
size: 10 // 根据业务需求设置合理的默认每页条数
},
initWithDefaults: true // 为新用户提供更好的体验
}
```
### 注意事项
#### 1. 浏览器兼容性
- 支持 `window.history.replaceState` 的现代浏览器
- 服务端渲染SSR时需要检查 `typeof window !== 'undefined'`
#### 2. 参数长度限制
- URL 参数总长度建议控制在 2048 字符以内
- 复杂搜索条件考虑使用 POST 请求而非 GET
#### 3. 安全性考虑
- 对 URL 参数进行验证和清理
- 避免将敏感信息存储在 URL 中
- 考虑 XSS 防护
#### 4. 性能影响
- URL 同步功能对性能影响很小
- 防抖机制避免频繁的 DOM 操作
- 合理设置防抖时间可进一步优化性能
### 向后兼容性
URL 同步功能完全向后兼容,不会影响现有代码:
```tsx
// 现有代码无需修改,继续正常工作
<SearchFormPagination
searchFields={searchFields}
columns={columns}
onSearch={handleSearch}
onPageChange={handlePageChange}
// ... 其他 props
/>
// 极简启用 - 只需添加一行
<SearchFormPagination
// ... 现有 props
urlSync={{ enabled: true }} // 参数名自动从 searchFields 推导
/>
```
### 配置简化对比
#### 优化前(复杂配置)
```tsx
urlSync={{
enabled: true,
initWithDefaults: true,
paramNames: {
page: 'page',
size: 'size',
search: 'search',
status: 'status',
type: 'type'
},
defaultPagination: { page: 1, size: 10 },
updateDebounce: 300
}}
```
#### 优化后(极简配置)
```tsx
urlSync={{
enabled: true,
initWithDefaults: true,
updateDebounce: 300
}}
// page、size 以及所有 searchFields 的 key 都会自动推导
```
### 故障排除
#### 1. URL 参数不更新
**可能原因**
- `urlSync.enabled` 设置为 `false`
- 防抖时间设置过长
- 浏览器不支持 `history.replaceState`
**解决方案**
```tsx
urlSync: {
enabled: true,
updateDebounce: 100 // 减少防抖时间测试
}
```
#### 2. 页面刷新后状态丢失
**可能原因**
- `initWithDefaults` 设置为 `false`
- 参数名映射不正确
- 父组件未正确处理 `onQueryChange` 回调
**解决方案**
```tsx
urlSync: {
enabled: true,
initWithDefaults: true // 确保启用默认值初始化
}
// 检查参数名映射
paramNames: {
search: 'search', // 确保与搜索字段 key 一致
status: 'status'
}
```
#### 3. 搜索条件与分页不同步
**可能原因**
- 父组件未传递正确的分页状态
- 回调函数中丢失搜索条件
**解决方案**
```tsx
const handlePageChange = useCallback((page) => {
// 确保传递当前搜索条件
loadUsers({
filters: currentFilters, // 关键:保持搜索条件
pagination: { page, size: currentSize }
});
}, [loadUsers, currentFilters, currentSize]);
```
### 总结
URL 参数同步功能为 SearchFormPagination 组件提供了强大的状态持久化能力,通过极简配置即可实现:
- **自动同步**:无需手动管理 URL 参数
- **自动推导**:参数名从 `searchFields` 自动提取,无需手动映射
- **状态恢复**:页面刷新后自动恢复搜索状态
- **用户体验**:提供更好的导航和分享体验
- **极简配置**:只需 `urlSync={{ enabled: true }}` 即可启用
- **向后兼容**:不影响现有代码,渐进式升级
#### 配置简化成果
- **优化前**:需要手动配置所有参数名映射,配置复杂
- **优化后**:参数名自动推导,配置减少 70%+
#### 使用建议
- **基础场景**:直接使用 `urlSync={{ enabled: true }}`
- **特殊需求**:仅在需要自定义参数名时配置 `paramNames`
- **避免使用**:不推荐使用 `q``s` 等过于简化的参数名
该功能特别适用于数据展示、搜索、筛选等需要状态保持的场景,是提升用户体验的重要功能。