React后台管理(十四)-- 完整示例页面构建教学

文章目录


前言

经过了前面文章的学习,终于到最后一步了,那就是一个管理页面的构建,包括处理列表请求,搜索、重置和展开/收起等功能。

结合之前封装的布局、功能相关组件,在本文只需要按需引入,统一了代码标准,减少重复代码,提高代码的可维护性和可复用性。


一、组件源码+详细注释说明+技术分析

页面技术分析:

(1)React Hooks:使用了React的Hooks,如useState、useEffect和useRef,用于管理状态和副作用

(2)React Router DOM:使用了React Router DOM,实现页面路由和导航

(3)MobX-React lite:使用了MobX-React lite,实现响应式数据管理

(4)Ant Design:使用了Ant Design,用于构建表格、按钮、输入框等用户界面元素

(5)自定义组件:使用了自定义组件,如SearchIndex、SearchItem和SearchButton,用于实现搜索和过滤功能

c 复制代码
// @/page/chainManage/index.jsx
import { useEffect, useState, useRef } from "react";
import { useNavigate } from "react-router-dom";
import { observer } from "mobx-react-lite";
import { Table, Space, Button, Select, message, Input } from "antd";
import { DeleteOutlined } from "@ant-design/icons";
// api
import { ApiCustomersList, ApiDeleteCustomer } from "@/api";
// hook
import { useTable, useSearch } from "@/hook/index.js";
// 函数组件
import SearchIndex from "@/components/search/index.jsx";
import SearchItem from "@/components/search/item.jsx";
import SearchButton from "@/components/search/button.jsx";
import CdList from "@/layout/list/index.jsx";
import CdTabs from "@/layout/tabs/index.jsx";

const Customer = () => {
  // table
  const [loading, setLoading] = useState(false);
  const { getHeight, tableHeight } = useTable();
  // 请求参数初始化
  const { getReq, updateReq } = useSearch({
    name: "",
    liaisonName: "",
    liaisonMobile: "",
    brandStatus: undefined,
  });
  // table ref
  const tableRef = useRef();
  // 消息
  const [messageApi, contextHolder] = message.useMessage();
  // 路由导航
  const navigate = useNavigate();
  // 搜索ref
  const searchRef = useRef();
  // 是否展示更多搜索条件
  const [open, setOpen] = useState(false);
  // 客户参数管理
  const [params, setParams] = useState({
    page: 1,
    per_page: 10,
  });
  // 客户列表管理
  const [customerData, setCustomerData] = useState({
    list: [], // 客户列表
    count: 0, // 客户数量
  });
  // 状态select列表
  const [statusOptions] = useState([
    { value: "1", label: "及格" },
    { value: "0", label: "不及格" },
  ]);
  // 表格column项
  const columns = [
    {
      title: "客户名称",
      dataIndex: "brandName",
      key: "brandName",
      width: 220,
      render: (text) => <a>{text}</a>,
    },
    {
      title: "状态",
      dataIndex: "brandStatus",
      key: "brandStatus",
      ellipsis: true,
    },
    {
      title: "费率",
      dataIndex: "serviceChargeRateStr",
      key: "serviceChargeRateStr",
      width: 220,
      align: "right",
    },
    {
      title: "联系人",
      dataIndex: "liaisonName",
      key: "liaisonName",
      ellipsis: true,
    },
    {
      title: "联系人手机",
      dataIndex: "liaisonMobile",
      key: "liaisonMobile",
      ellipsis: true,
    },
    {
      title: "操作",
      width: 240,
      render: (data) => {
        return (
          <Space>
            <Button type="link" size="small" icon={<i className="iconfont icon-eye" />} onClick={() => handlerEdit(data)}>
              详情
            </Button>
            <Button type="link" size="small" icon={<i className="iconfont icon-edit" />} onClick={() => handlerEdit(data)}>
              编辑
            </Button>
            <Button type="link" size="small" icon={<DeleteOutlined />} onClick={() => handlerDel(data)}>
              删除
            </Button>
          </Space>
        );
      },
      fixed: "right",
    },
  ];
  // 获取客户列表数据
  const getList = async () => {
    setLoading(true); // 页面loading打开
    ApiCustomersList(getReq())
      .then((res) => {
        console.log("getReq", getReq());
        setCustomerData({
          list: res.data?.records || [],
          count: res.data?.total || 0,
        }); // 表格数据
        setLoading(false); // 页面loading关闭
        tableRef.current && tableRef.current.setTotal(res.data?.total); // 设置分页器总条数
      })
      .catch(() => {
        setLoading(false); // 页面loading关闭
      });
  };
  // 切换页码
  const changePage = () => {
    return getList();
  };
  // 设置表格区域高度以及请求列表数据
  useEffect(() => {
    getHeight().then(() => {
      getList();
    });
  }, []);
  // 展开/收起
  const handlerToggle = (value) => {
    setOpen(value);
    getHeight();
  };
  // 搜索
  const handlerSearch = () => {
    console.log("搜索", searchRef.current.getFieldValue());
    searchRef.current.getFieldValue() && updateReq(searchRef.current.getFieldValue());
    tableRef.current.reset();
    getList();
  };
  // 重置
  const handlerReset = () => {
    searchRef.current.reset();
    getList();
  };
  // 状态select下拉
  const onStatusChange = (val) => {
    searchRef.current.setFieldValue("brandStatus", val);
  };
  // 编辑
  const handlerEdit = (data) => {
    navigate(`/layout/publish?id=${data.id}`);
  };
  // 删除
  const handlerDel = async (data) => {
    ApiDeleteCustomer({ id: data.id }).then((res) => {
      // 刷新列表
      setParams({
        ...params,
        page: 1,
      });
      getList();
      messageApi.open({
        type: "success",
        content: "删除成功",
      });
    });
  };

  return (
    <>
      {contextHolder}
      <CdList tableRef={tableRef} page={changePage}>
        {{
          tabs: (
            <CdTabs title="客户管理">
              <Space size={10}>
                <Button type="primary">新增</Button>
                <Button>导 出</Button>
              </Space>
            </CdTabs>
          ),
          search: (
            <>
              {/* 筛选区域 */}
              <SearchIndex searchRef={searchRef}>
                <SearchItem label="客户名:" name="name" labelWidth="60px">
                  <Input placeholder="请输入名称/简称" maxLength={100} />
                </SearchItem>
                <SearchItem label="联系人:" name="liaisonName" labelWidth="60px">
                  <Input placeholder="请输入" maxLength={100} />
                </SearchItem>
                <SearchItem label="联系人手机:" name="liaisonMobile" labelWidth="90px">
                  <Input placeholder="请输入" maxLength={20} />
                </SearchItem>
                {open ? (
                  <SearchItem label="状态:" name="brandStatus" labelWidth="45px">
                    <Select placeholder="请选择" options={statusOptions} onChange={onStatusChange} allowClear />
                  </SearchItem>
                ) : null}
                <SearchItem span={open ? 24 : 6}>
                  <SearchButton toggle={handlerToggle} reset={handlerReset} search={handlerSearch}></SearchButton>
                </SearchItem>
              </SearchIndex>
            </>
          ),
          default: (
            <>
              {/* 客户列表区域 */}
              <Table
                rowKey="id"
                loading={loading}
                columns={columns}
                dataSource={customerData.list}
                scroll={{
                  y: tableHeight,
                }}
                pagination={false}
              />
            </>
          ),
        }}
      </CdList>
    </>
  );
};

export default observer(Customer);

二、效果展示


总结

关注本栏目,会实时更新。

相关推荐
EricWang135819 分钟前
[OS] 项目三-2-proc.c: exit(int status)
服务器·c语言·前端
September_ning19 分钟前
React.lazy() 懒加载
前端·react.js·前端框架
晴天飛 雪29 分钟前
React 守卫路由
前端框架·reactjs
web行路人29 分钟前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
番茄小酱00130 分钟前
Expo|ReactNative 中实现扫描二维码功能
javascript·react native·react.js
超雄代码狂1 小时前
ajax关于axios库的运用小案例
前端·javascript·ajax
长弓三石1 小时前
鸿蒙网络编程系列44-仓颉版HttpRequest上传文件示例
前端·网络·华为·harmonyos·鸿蒙
小马哥编程1 小时前
【前端基础】CSS基础
前端·css
嚣张农民1 小时前
推荐3个实用的760°全景框架
前端·vue.js·程序员
周亚鑫2 小时前
vue3 pdf base64转成文件流打开
前端·javascript·pdf