safety-eval-service-frontend/.cursor/skills/search-state-persistence/SKILL.md

175 lines
4.7 KiB
Markdown
Raw Normal View History

2026-08-14 16:54:03 +08:00
---
name: "search-state-persistence"
description: "Search state persistence via tools.router.query. Invoke when implementing search/table pages with SearchForm + Table + pagination that need to preserve query params across refreshes."
---
# 搜索状态持久化
通过 `tools.router.query` 实现搜索条件持久化,刷新页面后搜索条件和分页状态保留在 URL 查询参数中。
## 核心 API
```js
import { tools } from "@cqsjjb/jjb-common-lib";
const { router } = tools;
```
`router.query` 是一个响应式对象,对其赋值会同步更新 URL 查询参数。
## 完整模式
```jsx
import React, { useState, useEffect } from "react";
import { Button, Form, Select, Space, Table, Tag } from "antd";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import SearchForm from "~/components/SearchForm";
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
import { tools } from "@cqsjjb/jjb-common-lib";
const { router } = tools;
const MyPage = (props) => {
const [form] = Form.useForm();
const [loading, setLoading] = useState(false);
/** 搜索/刷新数据 */
const handleSearch = () => {
setLoading(true);
// 实际请求...
setTimeout(() => setLoading(false), 500);
};
/** 重置搜索 */
const handleReset = (values) => {
router.query = {
...router.query,
...values,
current: 1,
size: 10,
};
handleSearch();
};
/** 初始化:将 URL 中的查询条件回填到表单 */
useEffect(() => {
form.setFieldsValue(router.query);
}, []);
const columns = [
// ...列定义
];
return (
<PageLayout title="页面标题">
<SearchForm
style={{ marginBottom: 24 }}
form={form}
loading={loading}
formLine={[
<Form.Item key="fieldName" name="fieldName">
<ControlWrapper.Input label="字段名" placeholder="请输入" allowClear />
</Form.Item>,
]}
onReset={handleReset}
onFinish={(values) => {
router.query = {
...router.query,
...values,
current: 1,
size: 10,
};
handleSearch();
}}
/>
<Table
rowKey="id"
columns={columns}
dataSource={data}
scroll={{ y: props.scrollY }}
loading={loading}
pagination={{
total: total,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => `共 ${total} 条`,
current: router.query.current,
pageSize: router.query.size,
onChange: (page, pageSize) => {
router.query = {
...router.query,
current: page,
size: pageSize,
};
handleSearch();
},
}}
/>
</PageLayout>
);
};
export default AntdTableFuncControl(MyPage);
```
## 关键规则
### 1. 初始化回填
`useEffect` 中将 `router.query` 的值回填到表单,确保刷新后表单显示与 URL 一致:
```js
useEffect(() => {
form.setFieldsValue(router.query);
}, []);
```
**注意**`router.query` 中所有值均为字符串,而 `DatePicker` / `RangePicker` 需要 `dayjs` 对象。当表单包含日期字段时,必须手动转换:
```js
useEffect(() => {
searchForm.setFieldsValue({
...router.query,
dateRange:
router.query.startTime && router.query.endTime
? [dayjs(router.query.startTime), dayjs(router.query.endTime)]
: undefined,
});
}, []);
```
### 2. 搜索/重置写入
搜索提交(`onFinish`) 和重置(`onReset`) 时,将表单值与默认分页合并写入 `router.query`
```js
router.query = {
...router.query,
...values, // 表单值
current: 1, // 重置到第一页
pageSize: 10, // 默认页大小
};
```
### 3. 翻页写入
分页变化时,仅更新 `current``pageSize`,保留已有搜索条件:
```js
onChange: (page, pageSize) => {
router.query = {
...router.query, // 保留已有搜索条件
current: page,
pageSize,
};
handleSearch();
};
```
### 4. 组件装饰器
页面组件必须用 `AntdTableFuncControl` 包裹以支持表格功能:
```js
export default AntdTableFuncControl(MyPage);
```
## 注意事项
- `router.query` 是响应式的:赋值会触发 URL 更新,且刷新后值仍然保留
- `router.query` 中所有值均为字符串类型URL query 的特性),使用 `current``pageSize` 时注意隐式类型转换
- 默认值 `current: 1, pageSize: 10` 是约定,可根据实际需求修改
- `onReset` 回调的 `values` 参数是重置后的表单值(所有字段清空/恢复默认后的值)