天天看點

一種清單全字段排序功能方案

概述

網站開發中,很常見的一個需求:要對清單頁的全部(或大多數)字段進行排序。

實作

先給出實作好的效果:

一種清單全字段排序功能方案
一種清單全字段排序功能方案
一種清單全字段排序功能方案

前端

注:主要技術棧為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​

​:

  1. 對于跨表Join的字段,有些是無法排序;
  2. 無需排序的列(其實也不叫字段),如操作列。

另外,有些列會有​

​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>      

邏輯:

  1. 如果沒有排序,則預設以更新時間降序擷取清單;否則就以前端指定的字段來排序;
  2. 跨表join時,需要通過​

    ​choose...when...otherwise​

    ​來選擇哪個表的字段來進行排序;
  3. 不能使用​

    ​#{sortField}​

    ​​,必須使用​

    ​${sortField}​

    ​​。因為前者為了防止SQL注入,會自動加單引号​

    ​''​

    ​,導緻拼接的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'
  });
}      

參考