针对前台来说,后台某个接口对应的到底是一个表,还是几个表,接口都是一样的。
这里重点看一下后台代码是怎么写的,以及相对单表操作,前台需要注意的一些特殊情况。
后台写一个多表查询的 Sql 语句,然后调用一个底层的函数就行。
/*** overeride* @param params* @return*/@Overridepublic AbstractListResponse selectByAntSearchByPage(AntSearchListParams params){String sql="select S.store_name,S.member_name, A.*,B.class_name class_name_1,C.class_name class_name_2,D.class_name class_name_3 " +"from ec_store_bind_class A " +" ,ec_goods_class B " +" ,ec_goods_class C " +" ,ec_goods_class D " +" ,ec_store S " +"where A.class_1=B.class_id " +" and A.class_2=C.class_id " +" and A.class_3=D.class_id " +" and S.store_id=A.store_id" +" ORDER BY gmt_create desc";return multiSqlBuilder.selectByAntSearchByPage(sql,params);}
详细内容可以看后台相关文档
后台写一次就可以了,根据前台传入的参数动态拼接 Where 条件与排序。
针对上图所示,实现的功能有:
有些代码是重复性工作,可以通过程序自动生成。
首先得到数据结构。
由于是多表,这里在原先的数据结构上,追加了一些附属的结构。 取名叫做StoreBindClassEx
// 店铺绑定商品分类export type StoreBindClass = {storeBindClassId: number; // id [最大位数:11]storeId: number; // 店铺ID [最大位数:11]commissionRate?: number; // 佣金比例class1?: number; // 一级分类 [最大位数:11]class2?: number; // 二级分类 [最大位数:11]class3?: number; // 三级分类 [最大位数:11]classFullName?: string; // 分类全名,包含一级 [最大位数:200]state?: number; // 状态0审核中1已审核 2平台自营店铺 [最大位数:11]gmtCreate?: string; // 记录创建时间gmtModified?: string; // 记录修改时间};// 店铺绑定商品分类扩展表,因为做了多表查询export type StoreBindClassEx = StoreBindClass & {storeName: string;className1: string;className2: string;className3: string;memberName: string;};
这里会生成
import { request } from 'umi';import { PrefixUrl } from '@/services/Common';// -----------------------------常量-------------------------------------------const prifix = `${PrefixUrl}/store/list`;/*** 得到 店铺可发布商品类目 列表* @param data 可以传入:检索条件、排序、分页等检索条件*/export async function getStoreBindClassList(data: API.SearchListParams) {return (request <API.ResponseInfo <API.ResponseListData <Store.StoreBindClass >>>(`${prifix}/getStoreBindClassList`,{method: 'POST',data,}));}/*** 根据Id得到某个 店铺可发布商品类目*/export async function getStoreBindClassById(storeBindClassId: number) {return (request <API.ResponseInfo <Store.StoreBindClass >>(`${prifix}/getStoreBindClassById`,{method: 'GET',params: {storeBindClassId,},}));}/*** 根据Id添加或更新某个 店铺可发布商品类目*/export async function saveStoreBindClass(data: Store.StoreBindClass) {return (request <API.ResponseInfo <number >>(`${prifix}/saveStoreBindClass`,{method: 'POST',data,}));}/*** 删除某个店铺可发布商品类目*/export async function delStoreBindClassById(storeBindClassId: number) {return (request <API.ResponseInfo <number >>(`${prifix}/delStoreBindClassById`,{method: 'GET',params: {storeBindClassId,},}));}
page 部分代码稍微多一点,拿是都是讨论。这里把主要的代码标记出来。
页面中所有的表格代码都是这样,无非把下面三个地方替换了:
StoreBindClassEx
新生成的数据结构rowKey="storeBindClassId"
表格的主键getStoreBindClassList
要检索的函数名。return (<React.Fragment><Help><p>此处可以对商家新申请的经营类目进行 审核/删除 操作</p></Help><ProTable<Store.StoreBindClassEx>actionRef={tableRef}columns={columns}rowKey="storeBindClassId"request={async (params, sort) => {// getWheres 与 getOrderStr 这两个函数,会按照规则,转换成后台能统一识别的查询条件const result = await getStoreBindClassList({current: params.current,pageSize: params.pageSize,wheres: getWheres(params, whereNameRules),order: getOrderStr(sort),});return result.data;}}/></React.Fragment>);
这几行代码就定义出了这个界面
主要定义 Table 中的检索条件与 Table 的显示内容,下面重点的内容有:
查询部分:有些特殊查询,通过hideInTable: true
不显示在表格中。
表格部分:
经营类目:通过render
可以实现多个字段的内容。
通过valueType: 'dateTime'
格式化显示内容
sorter: true
可以进行排序
search: false
不进行查询
search
指定一个特殊的函数,应该格式化查询参数。gmtCreate_between
做了特殊的定义。
search: {transform: (values) => {return {gmtCreate_between: values.join(','),};},},
const columns: ProColumns<Store.StoreBindClassEx>[] = [{title: '经营类目',dataIndex: 'class1',search: false,render: (_: React.ReactNode, row: Store.StoreBindClassEx) => (<React.Fragment>{row.className1} {row.className2} {row.className3}</React.Fragment>),},{title: '店铺',dataIndex: 'storeName',hideInTable: true,},{title: '店铺',dataIndex: 'storeId',search: false,render: (_: React.ReactNode, row: Store.StoreBindClassEx) => (<React.Fragment>{row.storeName}[ {row.storeId}]</React.Fragment>),},{title: '店主账号',dataIndex: 'memberName',sorter: true,search: false,},{title: '申请时间',dataIndex: 'gmtCreate',hideInTable: true,valueType: 'dateRange',search: {transform: (values) => {return {gmtCreate_between: values.join(','),};},},},{title: '提交时间',dataIndex: 'gmtCreate',valueType: 'dateTime',sorter: true,search: false,},{title: '分佣比例',dataIndex: 'commissionRate',search: false,valueType: 'percent',},{title: '申请状态',dataIndex: 'state',valueType: 'select',valueEnum,},{title: '操作',valueType: 'option',render: (_, record) => getOption(record),},];
可以方便的实现 like beteen in
等特殊功能。 例如下面,就是对storeName
进行like
查询。
/** 定义一个`whereNameRules` 转换的规则,其中字段名必须与数据库中的一致。 */const whereNameRules = {storeName: 'storeName_like',};
例如上面的审核与待审核,用自定义就可以了。
const valueEnum = {0: { text: '待审核', status: 'Processing' },1: { text: '已审核', status: 'Success' },};
有些下拉框的数据,是从其他表的中得到的,这应该怎么做呢?
例如下面的例子中,所属等级是从StoreGrade
获取的。
在 service 中定义一个查询函数
/*** 将店铺等级转换成 select table下拉框可以读取的格式。* @param initValues 不是从数据库中读取的一些数值,例如下拉框中的全部。*/export async function getStoreGradeListOptions(initValues: any[]) {const result = await getStoreGradeList();return convertToOptions(initValues, result.data, 'name', 'storeGradeId');}
在 Page 中的column
定义中,使用下面的数据。
{title: '店铺等级',dataIndex: 'gradeId',request: () => getStoreGradeListOptions([]),params: {},valueType: 'select',},