基於業務沉澱元件 manage-table

德萊問前端發表於2022-01-13
2020年下半年,有幾張圖片刷屏:有人騎在自行車上看書,有人邊騎車邊用電腦,有人床上鋪滿了一摞摞書……“邊騎車邊用電腦”的同學被稱為“卷王”登上熱搜。
慢慢的這些同學畢業了,卷王帶著捲走上了社會,帶動了其他人一起卷,卷的人越來越多了,苦不堪言,就導致了一些重複造輪子和造一些毫無意義的輪子的現象出現。

造輪子,本來是件好事,但是隨著內卷的出現,造輪子就慢慢演變成了一個極端,出現了憑空造輪子和重複造輪子的事情,既不能服務於業務,還使得內卷現象越來越嚴重,真正的苦不堪言。

分析當前業務遇到的問題,進而產生新的思路和總結,利用技術的手段提升工作效率,提高開發速度,才是真正的有意義的輪子,也不枉一場。

場景

CMS(content management system)一詞出現已久,通常指的是內容管理系統,是一種位於WEB前端和後端辦公系統或流程之間的軟體系統。在開發cms後臺的過程中,最最常用的應該就是Table了,例如 antd的table:

圖片.png
這應該是最最常用的開發後臺管理系統中使用到的元件了,沒有個Table,都不好意思說是個cms系統。不過在稍微龐大的業務中會存在一個非常常見的問題,就是一個資料來源會有很多很多欄位需要進行展示,如果都展示出來呢,就會存在一個非常不美觀且亂糟糟的感覺,眼花繚亂。同時不同的人,希望看到的欄位也是不一樣的,比如A同學希望看到標題0、1、2、3,B同學希望看到標題1、2、3、4,C同學希望看到標題7、8、9、10等。

這樣就是一個非常個性化的需求了,如果希望後端同學來參與的話,就會增加後端同學的工作量,同時前端工作也不會相應的減少。得益於瀏覽器的localstorage儲存能力,前端就可以實現,根本不需要後端同學的參與。

實現

首先,既然是antd的Table元件,我們肯定是要基於現有的功能去實現這個需求,所以我們需要在Table元件的基礎上套一層,既不能影響Table的展示,同時還能夠定製展示列。那我們就可以列一下需求了:

  1. 不影響Table的展示
  2. 可以選擇自定義展示列
  3. 可以對展示列進行排序
  4. 不會對業務產生其他影響(這是最主要的)

需求既然已經明確,我們就可以開整了,具體的實現,就不多說了,我們可以看下實現後的效果:

圖片.png

打磨

既然已經實現了最初的需求,就可以高枕無憂了。怎麼可能呢?想太多了吧!!!

是的,後來產品說,現在資料展示列太多了,比之前多了三倍,想在對展示列進行選擇的時候進行一下分組,不然都擠在一塊密密麻麻的不好找,嚴重影響工作效率了

WTF!最見不得別人說影響工作效率了,這麼嚴重的問題怎麼現在才說,怎麼不早點提需求過來呢?早點提過來肯定早就實現了啊,不會存在影響工作效率的問題啊.

啊!!!我可真是個口是心非的渣男,可是我知道,小蝌蚪才是渣男,我不配啊!!說多了都是淚啊,還是抓緊做需求吧。看下實現效果:

圖片.png
嗯,完美,就是這麼個效果。對Table的封裝進行了二次修改,在不影響之前的使用方式的基礎上,增加了對分組的能力支援,我可真TM棒!

> 然而,快樂的時光總是那麼短暫啊~~

有一天,我們的另外一個平臺發現,咦,你這個功能還怪好用嘞,能不能給我們也用用,好吧,最簡單直接的方式是複製貼上呀。複製貼上到一半的時候,突然又來了一個人也想用用這個功能,WTMD就很頭大。

這麼說來,還是封裝成一個npm包吧,等我會,我給你們釋出成一個元件包,你們直接安裝使用即可。

npm i manage-table

儘管拿去用吧。

使用

安裝

npm i manage-table
or
yarn add manage-table

manage-table元件有對應的peerDependencies,如果沒有安裝的話,需要手動安裝一下對應的依賴:

"peerDependencies": {
  "@ant-design/icons": "^4.6.4",
  "antd": "^4.12.0",
  "react": "^17.0.0",
  "react-beautiful-dnd": "^13.1.0"
}

使用方式-: 直接引用,使用內建設定

程式碼如下:

import ManageTable  from "manage-table";
import './App.css';
import React from "react";

function App() {
  const mockColumns = new Array(50).fill('').map((_item: string, index) => {
    return {
      dataIndex: 'title' + index,
      key: 'title' + index,
      title: '標題' + index,
      show: index % 3 === 0,
    };
  });
  mockColumns.push({
    dataIndex: 'action',
    key: 'action',
    title: '操作',
    show: true,
  });
  return (
    <div className="App">
      <ManageTable name="testTable" columns={mockColumns}/>
    </div>
  );
}

export default App;

效果如下:

圖片.png

使用方式二:自定義header部分

程式碼如下:

import React from "react";
import { Button } from "antd";
import ManageTable from "manage-table";

export default function App2() {
  const mockColumns = new Array(50).fill("").map((_item, index) => {
    return {
      dataIndex: "title" + index,
      key: "title" + index,
      title: "標題" + index,
      show: index % 3 === 0
    };
  });
  mockColumns.push({
    dataIndex: "action",
    key: "action",
    title: "操作",
    show: true
  });
  const ref = React.createRef();
  const handleShowModal = () => {
    ref.current.showModal();
  };
  const SettingHeader = (
    <div style={{ textAlign: "left" }}>
      <Button onClick={handleShowModal}>自定義設定</Button>
    </div>
  );
  return (
    <div className="App">
      <ManageTable
        ref={ref}
        SettingComp={SettingHeader}
        name="testTable2"
        columns={mockColumns}
      />
    </div>
  );
}

效果如下:

圖片.png

使用方式三:分組展示

程式碼如下:

import React from "react";
import { Button } from "antd";
import ManageTable from "manage-table";

const mockGroup = () => {
  const data = new Array(4).fill('').map((_item:string, index: number) => {
    return {
      title: '分組' + index,
      records: new Array(10).fill('').map((_item: string, indx) => {
        return {
          dataIndex: 'title' + index + '_' + indx,
          key: 'title' + index + '_' + indx,
          title: '標題' + index + '_' + indx,
          show: indx % 5 === 0,
        };
      }),
    };
  });
  // 任何一個索引都可以,不必須是0
  data[0].records.push({
    dataIndex: 'action',
    key: 'action',
    title: '操作列',
    show: true,
  })
  return data;
}

export default function AppGroupRef() {
  const ref: any = React.createRef();

  const handleSet = () => {
    ref.current.showModal();
  }

  const SettingHeader = (
    <div style={{textAlign: 'left'}}>
      <Button type="primary" onClick={handleSet}>自定義設定</Button>
    </div>
  );
  return (
    <div className="App">
      <ManageTable ref={ref} SettingComp={SettingHeader} name="testTableGroup" columns={mockGroup()}/>
    </div>
  );
}

效果如下:

圖片.png

其他方式

除了可以上面三種方式使用之外,還支援固定展示的配置,即部分欄位預設展示且不允許進行排序和刪除。manage-table預設是儲存在瀏覽器的快取裡面的,是跟隨瀏覽器走的,如果不想走瀏覽器快取,而是自定義儲存的話,也是支援的。

具體如下:

ManageTable, 繼承自antd的Table

引數名型別說明
namestring儲存所使用的唯一的key,必傳
columnsManageColumnType[]GroupManageColumn[]列資料, 必傳
refReact.createRef()的返回物件增加皮膚, 非必傳
SettingCompReact.ReactNode自定義設定頭部, 非必傳
setTitleReact.ReactNode、string自定義彈窗的標題,預設'設定顯示欄位', 非必傳
defaultShowKeysstring[]預設顯示的欄位,不需要進行選擇or 排序
initialShowKeysstring[]初始顯示的欄位,自定義儲存
onKeysSelected(keys: string[]) => void儲存鉤子函式,搭配自定義儲存使用

ManageColumnType, 繼承自antd的Table的ColumnType

引數名型別說明
showboolean是否預設顯示

GroupManageColumn, 繼承自antd的Table的ColumnType

引數名型別說明
titlestring組名,必傳
recordsManageColumnType[]列資料, 必傳

寫在最後

歡迎使用和提交反饋。

2022年了,讓我們接著起來吧,停止瞎卷,開啟優卷

馬上放假了,提前祝大家新年快樂吧~

相關文章