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

4.7 KiB
Raw Blame History

name description
search-state-persistence 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

import { tools } from "@cqsjjb/jjb-common-lib";
const { router } = tools;

router.query 是一个响应式对象,对其赋值会同步更新 URL 查询参数。

完整模式

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 一致:

useEffect(() => {
  form.setFieldsValue(router.query);
}, []);

注意router.query 中所有值均为字符串,而 DatePicker / RangePicker 需要 dayjs 对象。当表单包含日期字段时,必须手动转换:

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

router.query = {
  ...router.query,
  ...values,         // 表单值
  current: 1,        // 重置到第一页
  pageSize: 10,      // 默认页大小
};

3. 翻页写入

分页变化时,仅更新 currentpageSize,保留已有搜索条件:

onChange: (page, pageSize) => {
  router.query = {
    ...router.query,   // 保留已有搜索条件
    current: page,
    pageSize,
  };
  handleSearch();
};

4. 组件装饰器

页面组件必须用 AntdTableFuncControl 包裹以支持表格功能:

export default AntdTableFuncControl(MyPage);

注意事项

  • router.query 是响应式的:赋值会触发 URL 更新,且刷新后值仍然保留
  • router.query 中所有值均为字符串类型URL query 的特性),使用 currentpageSize 时注意隐式类型转换
  • 默认值 current: 1, pageSize: 10 是约定,可根据实际需求修改
  • onReset 回调的 values 参数是重置后的表单值(所有字段清空/恢复默认后的值)