概述
網站開發中,很常見的一個需求:要對清單頁的全部(或大多數)字段進行排序。
實作
先給出實作好的效果:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAnYldHL0FWby9mZvwFN4ETMfdHLkVGepZ2XtxSZ6l2clJ3LcV2Zh1Wa9M3clN2byBXLzN3btgHL9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsQTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5CM4YjNzEGMxE2MxUmZiRWNzYzXzUTNwgDMwMzLcBTMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
前端
注:主要技術棧為antd,TypeScript。
const columns = [
{
title: '名稱',
dataIndex: 'name',
sorter: true,
render: (text: string, record: any) => (
<div style={{maxWidth: 200, lineHeight: 1.2}} > {record?.name}</div>
),
},
]
sorter
指定該字段是否需要排序功能,兩種情況下可以設定為
sorter: false
:
- 對于跨表Join的字段,有些是無法排序;
- 無需排序的列(其實也不叫字段),如操作列。
另外,有些列會有
render
的特殊需求,在加排序功能之前,并沒有配置
dataIndex: 'name'
。但是在引入排序功能後,必須指定該字段,下面代碼裡面的
sorter.field
即依賴于此字段,否則
sorter.field
為
undefined
。
清單元件:
<Vi_Table
loading={dataLoading}
rowKey={(record: any) => record.id}
columns={columns}
dataSource={autoJobListData.list}
pagination={{
...pagination,
position: ['bottomCenter'],
total: autoJobListData.total,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total: any) => (`共 ${total} 條`),
}}
onChange={handleTableChange}
/>
清單
onChange
方法:
const handleTableChange = (pagination: any, filters: any, sorter: any) => {
if (sorter && sorter.field && sorter.order) {
fetchDataWithParams({
...pagination,
sortField: sorter.field,
sortOrder: sorter.order === 'descend' ? 'desc' : 'asc'
});
} else {
fetchDataWithParams({
...pagination,
sortField: undefined,
sortOrder: undefined
});
}
};
後端
Controller接口
@RequestMapping("/list")
public String autoJobList(@RequestBody JSONObject jsonObject) {
return autoWordConfigService.autoJobList(jsonObject);
}
以JSONObject(或Map)來接收若幹參數。
Service方法:
public String autoJobList(JSONObject jsonObject) {
PageHelper.startPage(jsonObject.getInteger("pageNo"), jsonObject.getInteger("pageSize"));
if (StringUtils.isNotBlank(jsonObject.getString("sortField"))) {
jsonObject.put("sortField", StringUtil.camelCaseToUnderscore(jsonObject.getString("sortField")));
}
List<AutoWordConfigWithBlobs> list = autoWordConfigMapper.autoJobList(jsonObject);
PageInfo<AutoWordConfigWithBlobs> pageInfo = new PageInfo<>(list);
return JSONObject.toJSONString(ServiceUtil.returnSuccessData(pageInfo));
}
主要就是分頁功能,以及将駝峰命名轉換為下劃線命名,目地是對資料表裡面下劃線命名的字段進行排序。
Mapper接口:
<select id="autoJobList" resultType="com.xy.cloudiview.common.po.AutoWordConfigWithBlobs"
parameterType="java.util.Map">
select ac.id
FROM auto_word_config ac LEFT JOIN category dc ON ac.category_id = dc.category_id
LEFT JOIN `user` duo on duo.user_id= ac.owner_id
WHERE ac.isactive = 1 AND dc.isactive = 1
<if test="sortOrder == null or sortOrder == ''">
ORDER BY ac.update_time DESC
</if>
<if test="sortField != null and sortOrder != null">
ORDER BY
<choose>
<when test="sortField == 'owner_name' ">
duo.user_name
</when>
<when test="sortField == 'category_name'">
dc.${sortField}
</when>
<otherwise>
ac.${sortField}
</otherwise>
</choose>
<if test="sortOrder == 'asc'">
ASC
</if>
<if test="sortOrder == 'desc'">
DESC
</if>
</if>
</select>
邏輯:
- 如果沒有排序,則預設以更新時間降序擷取清單;否則就以前端指定的字段來排序;
- 跨表join時,需要通過
來選擇哪個表的字段來進行排序;choose...when...otherwise
- 不能使用
,必須使用#{sortField}
。因為前者為了防止SQL注入,會自動加單引号${sortField}
,導緻拼接的SQL執行異常。''
工具類方法:
/**
* 駝峰轉下劃線
*
* @param camel camelCase
* @return Underscore
*/
public static String camelCaseToUnderscore(String camel) {
StringBuilder underscore;
underscore = new StringBuilder(String.valueOf(Character.toLowerCase(camel.charAt(0))));
for (int i = 1; i < camel.length(); i++) {
underscore.append(Character.isLowerCase(camel.charAt(i)) ? String.valueOf(camel.charAt(i))
: "_" + Character.toLowerCase(camel.charAt(i)));
}
return underscore.toString();
}
拓展
局限
隻能指定一個排序字段。
優化
上面的代碼,可以進一步優化:
前端傳參:
sortOrder: sorter.order === 'descend' ? 'DESC' : 'ASC'
後端那一大坨代碼:
<if test="sortOrder == 'asc'">
ASC
</if>
<if test="sortOrder == 'desc'">
DESC
</if>
則可以替換為:
${sortOrder}
規範
上面這個方案,建立在一個開發規範上,即資料表的定義為下劃線命名,後端POJO定義為駝峰命名,傳回給前端的是駝峰命名,并且駝峰命名通過工具類方法可以轉化為下劃線。
if (sorter && sorter.field && sorter.order) {
if (sorter.field === 'id') {
sorter.field = 'boardId'
}
if (sorter.field === 'name') {
sorter.field = 'boardName'
}
fetchDataWithParams({
...pagination,
sortField: sorter.field,
sortOrder: sorter.order === 'descend' ? 'desc' : 'asc'
});
}