React 模板封装之 BaseTable
前言
前面有写过几篇 React 组件封装的文章。今天来记录下 React 模板封装之基础模板 BaseTable。
组件与模板
组件相信大家都知道,只要是相同的框架内,组件可以用于任何项目中。比如 antd 的 table 组件,只要有表格的场景都可以使用 table 组件。而模板是对组件的功能进一步拓展,使其应用于某种特定的业务流程中。比如具备展示功能的 table 表格只需要使用 table 组件即可。如果是具备增删改查功能的 table 表格则需要进一步拓展 table 的功能使其成为一个模板。
一、基础模板 BaseTable
模板说明:
用户只需要配置参数,就可以实现具备增删改查的 table 表格。
效果展示:
使用场景:
特别适合后台管理系统。很多后台管理系统有大部分是由表格构成,并且在实际开发中,大部分的表格并不只是纯展示,而是具备增删改查一套功能,比如由后台维护轮播图,必定会提供对轮播图进行增加、修改、删除等功能。
二、使用案例
参数说明
- modalList :配置接口对应的字段数据集。
- actions:配置接口功能,如配置增删改查接口(url、method、data等)。
- showDetail:当编辑或者新增时以页面形式打开而不是弹框。
- 文中有涉及到的 queryData 参数和 delData 参数,分别表示查询的额外字段和删除的额外字段。额外之外指的是独立于业务场景之外的。比如在查询的时候要需要传一个系统版本或者设备信息等。这两个参数也可以理解为是预留参数,几乎用不到。
从以下的案例可知,除了配置之外,只需要写一行代码即可实现如上图的具备增删改查的文章管理功能。
import React from "react";
import BaseTable from "../../../../template/BaseTable/index";
const modalList = [
{
label: "文章ID",
field: "id",
renderType: "Input",
visible: false,
writable: true,
isUpdate: true,
updateField: "id",
},
{
label: "文章标题",
field: "title",
renderType: "Input",
visible: true,
require: true,
isUpdate: true,
updateField: "title",
isSearch: true,
searchField: "title",
updateWritable: true,
updateVisible: true,
},
{
label: "所属期刊",
require: true,
field: "periodicalName",
require: true,
updateField: "periodicalId",
renderType: "Select",
visible: true,
writable: true,
isUpdate: true,
updateField: "periodicalId",
updateWritable: true,
updateVisible: true,
isSearch: true,
searchField: "periodicalId",
keyValueField: ["periodicalId", "periodicalName"],
sourceApi: { url: "/web/weeksPeriodical/periodicalList", method: "get" },
},
{
label: "所属栏目",
require: true,
field: "columnName",
updateField: "columnId",
isSearch: true,
searchField: "columnId",
renderType: "Select",
require: true,
visible: true,
writable: true,
isUpdate: true,
updateField: "columnId",
updateWritable: true,
updateVisible: true,
keyValueField: ["columnId", "columnName"],
sourceApi: {
url: "/admin/weeksPeriodicalColumn/columnList",
method: "post",
data: { periodicalId: 0 },
},
},
{
label: "更新时间",
field: "updateTime",
renderType: "Picker",
format: "YYYY-MM-DD",
visible: true,
isUpdate: false,
},
{
label: "文章内容",
field: "content",
updateField: "content",
writable: true,
renderType: "Editor",
visible: false,
width: 250,
isUpdate: true,
updateField: "content",
updateWritable: true,
updateVisible: true,
},
{
label: "文章概述",
field: "summarize",
updateField: "summarize",
writable: true,
renderType: "Textarea",
require: true,
visible: false,
width: 360,
isUpdate: true,
updateField: "summarize",
updateWritable: true,
updateVisible: true,
},
{
label: " 外部链接",
field: "url",
updateField: "url",
writable: true,
renderType: "Input",
visible: false,
width: 250,
isUpdate: true,
updateField: "url",
updateWritable: true,
help: "请输入有效的url",
updateVisible: true,
},
{
label: "排序",
field: "sort",
require: true,
updateField: "sort",
writable: true,
renderType: "InputNumber",
require: true,
visible: true,
isUpdate: true,
updateField: "sort",
updateWritable: true,
updateVisible: true,
},
];
const actions = [
{
status: "update",
name: "编辑",
url: "/admin/weeksArticle/update",
method: "post",
showColumn: true,
},
{
status: "copy",
name: "复制链接",
url: "",
method: "",
linkField: "detailPageUrl",
showColumn: true,
},
{
status: "add",
name: "新增",
url: "/admin/weeksArticle/add",
method: "post",
showColumn: false,
},
{
status: "query",
name: "查询",
url: "/admin/weeksArticle/listByPage",
method: "post",
extraField: "list",
showColumn: false,
},
];
function Article(){
return (
<BaseTable actions={actions} modalList={modalList} showDetail={true} />
);
}
export default Article;
三、API 使用指南
modalList :配置接口对应的字段数据集
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
label | 表格的标题,新增或者编辑时的字段标签 | String | 无 |
field | 接口对应的字段 | String | 无 |
renderType | 表格渲染的组件类型 | Array | 无 |
updateRenderType | 新增/编辑时渲染的组件类型,如:表格列表中使用的 Switch 组件,新增/编辑时使用 Radio 组件 | Array | 无 |
require | 是否校验必填字段 | Boolean | 无 |
visible | 表格列表中是否可见 | Boolean | 无 |
isUpdate | 是否作为更新字段 | Boolean | 无 |
updateField | 接口中对应的更新字段,一般与 field相同 | String | 无 |
updateVisible | 更新字段是否可见 | Boolean | 无 |
updateWritable | 更新字段是否可编辑 | Boolean | 无 |
keyValueField | 子节点 | Array | 无 |
sourceApi | 配置接口的参数,如单选或者多选时需要调接口获取数据 | Object | 无 |
actions:配置功能接口
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
status | 接口功能枚举值,枚举值目前有:add/del/update/query/export/import/copy,需要那种功能就填写对应的枚举值,不可更改 | String | 无 |
name | 接口功能名称,与status对应分别是增/删/改/查/导入/导出/复制,与status不同的是名称是可以更改的,比如表示增加时可以写“新增”或者“增加” | Function | 无 |
url | 接口url地址 | Array | 无 |
method | 接口请求方式 | String | 无 |
extraField | 额外字段,可配置查询时数据集字段。比如查询接口返回形式为{code:0,msg:“成功”,data:{list:[{id:1}]} 就需要配置 list 字段。 | Array | 无 |
linkField | 复制时对应的链接地址字段 | String | 无 |
showColumn | 是否显示在列 | Boolean | 无 |
showDetail:当编辑或者新增时以页面形式打开而不是弹框。
属性 | 说明 | 类型 | 默认值 |
---|---|---|---|
showDetail | 当编辑或者新增时以页面形式打开而不是弹框 | Boolean | 无 |
四、源代码
在 BaseTable 目录中包含如下文件。
index.js
import React, { useEffect, useState, useImperativeHandle, useRef } from "react";
import { Table, Modal, message } from "antd";
import DetailView from "./detail";
import SearchByCondition from "./searchByCondition";
import dealWithColumns from "./dealWithColumns";
import { delAPI, addAPI, editAPI, getListAPI,request } from "../commonApi";
import { generate} from "../../utils/tools";
import ModalBox from "./modalBox";
import copy from 'copy-to-clipboard';
import {
EditOutlined,
CopyOutlined,
PlusOutlined,
DeleteOutlined,
InstagramOutlined
} from '@ant-design/icons';
let { confirm } = Modal;
function evaluateList(setDataSource, props,setCount) {
if (props.queryData) {
getList(setCount,setDataSource, props, props.queryData);
} else {
getList(setCount,setDataSource, props, {});
}
}
function dealWithData(treeData, keys) {
return generate(treeData, keys["topId"], keys);
}
function getList(setCount,setDataSource, props, record, initData = "") {
let { actions, modalList } = props;
let action = actions.filter((item) => item.status === "query")[0];
getListAPI(action, modalList, record, initData, (state) => {
if (props.isTreeTable) {
setDataSource(dealWithData(state.dataSource, props.keys));
} else {
setDataSource(state.dataSource);
if(setCount){
setCount(state.count);
}
}
});
}
function summary(count) {
return (
<div style={{ marginLeft: "25px", color: "#1890ff" }}>
一共【{count}】条数据
</div>
);
}
//页码改变
function onChangePage(queryOption,props,setCount,setDataSource, setCurrent, pageSize, current) {
let option = queryOption.current.option||{};
setCurrent(current);
getList(setCount,setDataSource, props, option, { pageCount: current, pageSize }, current);
}
//打开添加模态框
function openAddModal(
props,
setShowDetail,
setShowModal,
setTitle,
setOption,
item,
record
) {
const { showDetail } = props;
if (showDetail) {
setShowDetail(true);
} else {
setShowModal(true);
}
var keys = Object.keys(record);
//判断record不是空对象
if (keys && keys.length>0) {
setTitle("插入");
if (item.data && Array.isArray(item.data)) {
item.data.map((item1) => {
let field = item1.field;
let updateField = item1.updateField;
setOption((data) => {
data[updateField] = record[field];
return JSON.parse(JSON.stringify(data));
});
});
}
} else {
setTitle("新增");
setOption({});
}
}
function lookDetail(setShowDetail, setOption, item,record) {
setShowDetail(true);
setOption((data) => {
for (let i in record) {
data[i] = record[i];
}
return JSON.parse(JSON.stringify(data));
});
}
//打开编辑模态框
function openEditModal(
props,
setShowDetail,
setShowModal,
setTitle,
setOption,
item,
record
) {
const { editCB, showDetail } = props;
if (editCB) {
editCB(record, item, this);
}
setTitle("编辑");
if (showDetail) {
setShowDetail(true);
} else {
setShowModal(true);
}
setOption((data) => {
for (let i in record) {
data[i] = record[i];
}
return JSON.parse(JSON.stringify(data));
});
}
//删除
function delRecord(props, setDataSource,setCount, item,record) {
const { modalList, delData } = props;
confirm({
title: "Are you sure delete this task?",
content: "Some descriptions",
okText: "Yes",
okType: "danger",
cancelText: "No",
onOk: () => {
delAPI(item, modalList, { ...record, ...delData }, () => {
message.success("删除成功");
evaluateList(setDataSource, props,setCount);
});
},
onCancel() {},
});
}
function submit(
title,
option,
showDetail,
current,
props,
setCurrent,
pageSize,
setShowDetail,
setShowModal,
setDataSource,
setCount
) {
const { actions, modalList } = props;
let action = {};
if (title === "新增") {
action = actions.filter((item) => item.status === "add")[0];
addAPI(action, modalList, option, () => {
setShowDetail(false);
setShowModal(false);
setCurrent(1);
evaluateList(setDataSource, props,setCount);
message.success("添加成功");
});
} else if (title === "插入") {
action = actions.filter((item) => item.status === "insert")[0];
editAPI(action, modalList, option, () => {
setShowModal(false);
if (showDetail) {
setShowDetail(false);
getList(setCount,setDataSource, props, {}, { pageCount:current, pageSize }, current);
} else {
getList(setCount,setDataSource, props, {}, { pageCount:current, pageSize }, current);
}
message.success("插入成功");
});
}else if (title === "编辑") {
action = actions.filter((item) => item.status === "update")[0];
editAPI(action, modalList, option, () => {
setShowModal(false);
if (showDetail) {
setShowDetail(false);
getList(setCount,setDataSource, props, {}, { pageCount:current, pageSize }, current);
} else {
getList(setCount,setDataSource, props, {}, { pageCount:current, pageSize }, current);
}
message.success("编辑成功");
});
}
}
function dealWithActions(modalList, actions) {
let isSearch = false;
for (let i of modalList) {
if (i.isSearch) {
isSearch = i.isSearch;
}
}
if (isSearch) {
return actions;
} else {
return actions.filter(
(item) => item.status !== "query" && !item.showColumn
);
}
}
function goBack(setCount,setShowDetail, setDataSource, props) {
setShowDetail(false);
getList(setCount,setDataSource, props, {});
}
function coppyRecord(item,record){
copy(record[item['linkField']]);
message.success('复制成功');
}
function openPage(item,record){
window.open(record[item['linkField']]);
}
function dealWithList(
operateList,
props,
setShowDetail,
setShowModal,
setTitle,
setOption,
setDataSource,
setCount
) {
operateList.map((item) => {
if (item.status === "update") {
item.type = "primary";
item.icon = <EditOutlined />;
item.do = openEditModal.bind(
this,
props,
setShowDetail,
setShowModal,
setTitle,
setOption
);
} else if (item.status === "del") {
item.type = "danger";
item.icon = <DeleteOutlined />;
item.do = delRecord.bind(this, props, setDataSource,setCount);
} else if (item.status === "copy") {
item.type = "";
item.icon = <CopyOutlined />;
item.do = coppyRecord.bind(this);
} else if (item.status === "preview") {
item.type = "dashed";
item.icon = <InstagramOutlined />;
item.do = openPage.bind(this);
}
else if (item.status === "insert") {
item.type = "primary";
item.icon = <PlusOutlined/>;
item.do = openAddModal.bind(
this,
props,
setShowDetail,
setShowModal,
setTitle,
setOption
);
} else {
item.type = "primary";
item.icon = "folder-open";
item.do = lookDetail.bind(this, setShowDetail, setOption);
}
});
return operateList;
}
async function getDataForOptions (item,setRefresh){
let {url,method,data} = item.sourceApi;
let res = await request({
url,
method,
...data
});
if (res.code !== '0') return;
let result = res.data;
result = result.map((item)=>{
return {
title:item.name,
key:item.id,
...item
}
});
result = generate(result, 0, { id: "id", parentId: "parentId", name: "name"});
item.options = result;
setRefresh(refresh=>!refresh)
}
function BaseTable(props, ref) {
let pageCount = 1,
pageSize = 10;
let [title, setTitle] = useState("");
let [dataSource, setDataSource] = useState([]);
let [showDetail, setShowDetail] = useState(false);
let [showModal, setShowModal] = useState(false);
let [count, setCount] = useState(0);
let [current, setCurrent] = useState(1);
let [option, setOption] = useState({});
let [refresh, setRefresh] = useState(0);
let queryOption = useRef(null);
let {
modalList,
actions,
rowSelection,
components,
onRow,
pagination,
onExpand,
expandedRowRender,
onExpandedRowsChange,
expandedRowKeys,
defaultExpandedRowKeys,
labelCol,
wrapperCol
} = props;
let columns = [];
let columnsList = modalList.filter((item) => item.visible);
let columnsUpdateList = modalList.filter(
(item) => item.isUpdate && item.updateVisible
);
let actionsColumn = actions.filter((item) => item.showColumn);
let actionsQuery = dealWithActions(modalList, actions);
if (modalList.length > 0) {
columns = dealWithColumns(
columnsList,
dealWithList(
actionsColumn,
props,
setShowDetail,
setShowModal,
setTitle,
setOption,
setDataSource,
setCount
),
setRefresh
);
};
useEffect(() => {
evaluateList(setDataSource, props,setCount);
let needRequest = modalList.some(item=>item.doApi);
if(needRequest){
let item = modalList.filter(item=>item.doApi)[0];
getDataForOptions(item,setRefresh)
}
}, []);
useImperativeHandle(ref, () => ({
dataSource,
setDataSource,
}));
return (
<div className="cm-bc-white">
{showDetail ? (
<div className="cm-p-02">
<div
onClick={() => goBack(setCount,setShowDetail, setDataSource, props)}
className="cm-flex cm-ai-bl cm-cursor-p cm-border-bottom-ddd cm-ptb-01 cm-c-666"
>
<span className="cm-mr-01 cm-img-01"><</span>
<span>返回</span>
</div>
<div className="cm-c-333 cm-fw-bold cm-fs-020 cm-mtb-01">{title}</div>
<DetailView
option={option}
title={title}
modalList={columnsUpdateList}
submit={() =>
submit(
title,
option,
showDetail,
current,
props,
setCurrent,
pageSize,
setShowDetail,
setShowModal,
setDataSource,
setCount
)
}
/>
</div>
) : (
<div className="cm-p-02">
<div className="cm-flex cm-mb-02 cm-jc-sb">
<SearchByCondition
actions={actionsQuery}
modalList={modalList}
option={option}
ref={queryOption}
setCurrent={setCurrent}
getList={getList.bind(this, setCount,setDataSource, props)}
openAddModal={openAddModal.bind(
this,
props,
setShowDetail,
setShowModal,
setTitle,
setOption
)}
/>
</div>
{expandedRowKeys ? (
<Table
columns={columns}
rowSelection={rowSelection}
dataSource={dataSource}
components={components}
onExpand={onExpand}
expandedRowRender={expandedRowRender}
expandedRowKeys={expandedRowKeys || []}
defaultExpandedRowKeys={defaultExpandedRowKeys || []}
onExpandedRowsChange={onExpandedRowsChange}
onRow={onRow}
rowKey={(record) => record[modalList[0].field]}
footer={() => (count ? summary(count) : null)}
pagination={
pagination === false
? pagination
: {
current: current,
onChange: onChangePage.bind(
this,
queryOption,
props,
setCount,
setDataSource,
setCurrent,
pageSize
),
total: count,
}
}
/>
) : (
<Table
columns={columns}
rowSelection={rowSelection}
dataSource={dataSource}
components={components}
onExpand={onExpand}
expandedRowRender={expandedRowRender}
onExpandedRowsChange={onExpandedRowsChange}
onRow={onRow}
rowKey={(record) => record[modalList[0].field]}
footer={() => (count ? summary(count) : null)}
pagination={
pagination === false
? pagination
: {
current: current,
onChange: onChangePage.bind(
this,
queryOption,
props,
setCount,
setDataSource,
setCurrent,
pageSize
),
total: count,
}
}
/>
)}
</div>
)}
<ModalBox
title={title}
visible={showModal}
labelCol={labelCol}
wrapperCol={wrapperCol}
onOk={() =>
submit(
title,
option,
showDetail,
current,
props,
setCurrent,
pageSize,
setShowDetail,
setShowModal,
setDataSource
)
}
option={option}
onCancel={() => setShowModal(false)}
setRefresh={setRefresh}
modalList={columnsUpdateList}
/>
</div>
);
}
export default React.forwardRef(BaseTable);
detail.js
import React,{useState} from 'react';
import { RenderModal } from '../component';
import {
Form,
Button
} from 'antd'
function DetailView(props){
let modalList = props.modalList;
let {option, title} = props;
const [refresh,setRefresh] = useState(false);
return (
<div>
<Form labelAlign="right"
labelCol={{ span: 6 }}
wrapperCol={{ span: 14 }}
>
<div className="cm-flex cm-jc-sa">
<div className="cm-flex-column">
{modalList.map((item, index) => {
if (item.renderType !== "Editor") {
if(item.updateRenderType){
item.renderType = item.updateRenderType
}
return <Form.Item label={item.label}
required={item.require}
key={index} style={{display: "flex"}}>
{RenderModal(item, option,setRefresh)}
</Form.Item>
}
})}
{title !== '查看' ?
<Button
style={{alignSelf:"center"}}
onClick={props.submit}
type="primary"
>
提交
</Button> : null
}
</div>
<div>
{modalList.map((item, index) => {
if (item.renderType === "Editor") {
return <div>{RenderModal(item, option,setRefresh)}</div>
}
})}
</div>
</div>
</Form>
</div>
)
}
export default DetailView;
dealWithColumns.js
import React from "react";
import { Button, Tag, Popover, Switch, message } from "antd";
import { request } from "../commonApi";
import moment from "moment";
import "moment/locale/zh-cn";
const dateFormat = "YYYY-MM-DD";
let count = 1;
function dealWithColumns(columns, operateList, setRefresh) {
var newCol = [];
columns.map((item) => {
let render = (text) => {
if (item.label === "序号") {
return <span>{count++}</span>;
}
if (Array.isArray(text) && item.keyValueField) {
return (
<div>
{text.map((item1, index) => {
return (
<span key={index}>{item1[item.keyValueField[1]]} </span>
);
})}
</div>
);
} else {
return <span>{text}</span>;
}
};
let renderType = item.renderType;
if (renderType === "Upload") {
render = (text) => (
<img src={text} style={{ width: "160px", height: "90px" }} alt="" />
);
}
if (renderType === "Radio" && Array.isArray(item.radioOptions)) {
render = (text) => <div>{getName(item.radioOptions, Number(text))}</div>;
} else if (renderType === "Tag") {
render = (text) => getTag(item.radioOptions, Number(text));
} else if (renderType === "Popover") {
render = (text, record) => (
<Popover content={record[item.linkField]} title="Link">
<Tag color="blue">
<a href={record[item.linkField]}>{record[item.field]}</a>
</Tag>
</Popover>
);
} else if (renderType === "Switch") {
render = (text, record) => (
<Switch
checked={!!record[item.field]}
onChange={(isShow) =>
updateBannerStatus(isShow, record, item, setRefresh)
}
/>
);
} else if (renderType === "Picker") {
render = (text, record) => moment(text).format(item.format || dateFormat);
} else if (renderType === "MultipleField") {
render = (text, record) => {
console.log(record[item.field]);
return (
<div>
{record[item.field] && Array.isArray(record[item.field])&&record[item.field].map((item1, index) => {
return (
<span key={index}>
{item1[item.keyValueField[1]]}
<span>
{record[item.field].length - 1 !== index ? item.splitKey : " "}
</span>
</span>
);
})}
</div>
);
};
}
newCol.push({
title: item.label,
dataIndex: item.field,
key: item.field,
width: item.width,
render,
});
});
if (operateList && operateList.length && operateList.length > 0) {
newCol.push({
title: "操作",
dataIndex: "button",
render: (text, record) =>
operateList.map((item, index) => {
if (item.status === "copy") {
let text = record[item.linkField];
return (
<Popover content={record[item.linkField]} title="Link">
<Button
type={item.type}
icon={item.icon}
key={index}
className="cm-mr-02 btn"
onClick={() => item.do(item, record)}
>
{item.name}
</Button>
</Popover>
);
} else {
return (
<Button
type={item.type}
icon={item.icon}
key={index}
className="cm-mr-02"
onClick={() => item.do(item, record)}
>
{item.name}
</Button>
);
}
}),
});
}
count = 1;
return newCol;
}
async function updateBannerStatus(isShow, record, item, setRefresh) {
let { id } = record;
let { url, method } = item.sourceApi;
//为了让编辑之后的弹框数据更新,所以重新赋值
record.isShow = Number(isShow);
isShow = Number(isShow);
let res = await request({
url,
method,
data: {
isShow,
id,
},
});
if (res.code !== "0") return;
setRefresh((refresh) => !refresh);
//message.success(isShow === 1 ? '启用成功' : '禁用成功')
}
function getName(arr, val) {
for (let i = 0; i < arr.length; i++) {
let item = arr[i];
if (item.key == val) {
return item.label;
}
}
}
function getTag(arr, val) {
for (let i = 0; i < arr.length; i++) {
let item = arr[i];
if (item.key == val) {
return <Tag color={item.color}>{item.label}</Tag>;
}
}
}
export default dealWithColumns;
modalBox.js
import React from 'react';
import { RenderModal } from '../component';
import {
Modal,
Form
} from 'antd'
function ModalBox(props){
let {modalList,visible, title,option,onOk,onCancel,setRefresh,labelCol,wrapperCol} = props;
return (
<Modal
title={title}
visible={visible}
onOk={onOk}
onCancel={onCancel}
>
<Form
labelAlign="right"
labelCol={{ span: labelCol||8 }}
wrapperCol={{ span: wrapperCol||16 }}
>
{modalList.map((item, index) => {
return (
<Form.Item
required={item.require}
label={item.label}
key={index}
help={item.help}
>
{RenderModal(item, option,setRefresh)}
</Form.Item>
);
})}
</Form>
</Modal>
)
}
export default ModalBox;
searchByCondition.js
import React, { useState,useImperativeHandle} from "react";
import { BASE_URL,getToken,downloadFile } from "utils";
import { Button, Form, Upload, message } from "antd";
import { RenderModal } from "../component";
import "moment/locale/zh-cn";
import { exportAPI } from "../commonApi";
function onChangeImport(info) {
if (info.file.status !== "uploading") {
console.log(info.file, info.fileList);
}
if (info.file.status === "done") {
message.success("文件导入成功");
queryFinish.bind(this);
} else if (info.file.status === "error") {
message.error("文件导入失败");
}
}
function exportFinish(item, record,modalList) {
exportAPI(item, modalList, record, (res) => {
//掉钉钉接口
//变量拼接的命名
if (Array.isArray(item.title.field)) {
var timeRange = [];
item.title.field.map((item) => {
timeRange.push(record[item]);
});
downloadFile(res.result, item.title.text, timeRange);
} else {
downloadFile(res.result, item.title.text);
}
});
}
function queryFinish(props,item, record) {
let { getList,setCurrent } = props;
setCurrent(1);
getList(record);
}
function resetFinish(props, setOption) {
let { getList,setCurrent } = props;
setCurrent(1);
getList({});
setOption({});
}
function dealWithList(operateList, props, setOption) {
let { openAddModal } = props;
let actions = [];
operateList.map((item) => {
if (item.status === "add") {
item.type = "primary";
item.do = openAddModal;
actions.push(item);
}
if (item.status === "query") {
item.type = "dashed";
//点查询时当前页数为1
item.do = queryFinish.bind(this, props);
actions.push(item);
//如果有查询,自动添加重置
actions.push({
type: "danger",
name: "重置",
do: resetFinish.bind(this, props, setOption),
});
}
if (item.status === "import") {
actions.push(item);
}
if (item.status === "export") {
item.type = "dashed";
item.do = exportFinish.bind(props.modalList);
actions.push(item);
}
});
return actions;
}
function SearchByCondition(props,ref) {
let modalList = props.modalList.filter((item) => item.isSearch);
let headers = { DING_TOKEN: getToken() };
const [option, setOption] = useState({});
const [refresh,setRefresh] = useState(false);
let actions = dealWithList(props.actions, props, setOption); //处理不同的button按钮样式
useImperativeHandle(ref, () => ({
option
}));
return (
<div>
{modalList.length > 0 ? (
<div className="cm-mb-01">
<Form layout="inline">
{modalList.map((item, index) => {
return (
<Form.Item label={item.label} key={index}>
{RenderModal(item, option,setRefresh)}
</Form.Item>
);
})}
</Form>
</div>
) : null}
{actions.map((item, index) => {
if (item.status === "import") {
return (
<Upload
key={index}
name="file"
showUploadList={false}
action={BASE_URL + item.url}
accept="excel"
headers={headers}
onChange={(info) => onChangeImport(info)}
>
<Button style={{ background: "#52C41A", color: "#fff" }}>
导入
</Button>
</Upload>
);
} else {
return (
<Button
type={item.type}
key={index}
className="cm-mr-01"
onClick={() => item.do(item, option)}
>
{item.name}
</Button>
);
}
})}
</div>
);
}
export default React.forwardRef(SearchByCondition);
以上可能有部分关联的JS文件没贴出来,因文件太多,就不一一贴出,需要的请留言。
五、总结
注意1:
配置 modalList 时一定要将唯一key放在第一行。
注意2:
使用该模板时,后台查询接口默认返回格式为:{code:0,msg:"",data:[]}。如若返回如下格式:{code:0,msg:"",data:{count:1,list:[{id:1}]}}。则在配置查询功能时需要额外配置 extraField 字段为 “list”。
注意3:
判断接口是否成功是通过 code 为 0,默认取的数据集字段为 data。