在大型中後台項目開發中,尤其是在使用React進行開發時,我們會遇到很多下拉框數據、多選框數據、或者編碼中多處使用到的業務型公共映射表。為了便於維護,可以把這些數據都集中放到一個模塊中,而不是分散的寫在各個地方。
實現思路
我們定義一個IMapExtra接口擴展一下Map實例,建立兩個類MapExtra和DataMap,MapExtra是為了在使用數據時更方便獲取Map類型數據。最後把map(數據映射)都存儲在DataMap類的成員中。
開始
首先我們創建一個DataMap.ts文件用於公共數據管理的模塊,並在裏面創建一個MapExtra類實現IMapExtra接口,MapExtra功能主要是對原生Map進行一個簡單的封裝。原因是我們需要這個MapExtra實例來存儲數據並可更方便的使用.get直接獲取到對應的數據項。
DataMap.ts
import * as utils from '@/utils';
type MapType<T> = Array<[T, T]>;
interface IMapExtra {
map: Map<string| number, string| number>;
has: ( key: string) => boolean;
get: ( key: string) => string| number;
getOptions: () => TYPE.IOption[];
}
class MapExtra implements IMapExtra {
public map;
private _map;
public constructor( map: MapType<string| number>) {
this._map = new Map(map);
this.map = new Map(map);
}
public getOptions: IMapExtra['getOptions'] = () => utils.mapToOption(this._map);
public has: IMapExtra['has'] = ( key) => this._map.has(key);
public get: IMapExtra['get'] = ( key) => this._map.get(key) ?? key;
}
同時上面增加了一個getOptions方法更方便的拿到對應當前數據的下拉框結構,當然這裏還可以擴展map轉其他結構的數據。
mapToOption方法和下拉框結構接口的細節:
import * as TYPE from '@/interface';
//map 轉換成 select option型數組
export const mapToOption = function(map: Map<string | number, string | number>): TYPE.IOption[] {
const option = [];
for (const [k, v] of map) {
option.push({label: v, value: k});
}
return option;
};
interface中導出IOption
export interface IOption<T = string | number | boolean> {
label: string | React.ReactNode;
value: T;
name?: string | number;
}
接下來繼續在文件中創建兩個FC、LC對象和DataMap類,把要存儲的數據通過MapExtra實例化後存到對應屬性中:
...
const FC = {
category: new MapExtra([
['product', '產品'],
['marketplace', '平台'],
]),
timeUnit: new MapExtra([
['month', '月度'],
['quarter', '季度'],
['year', '年度'],
]),
};
const LC = {
remarkAction: new MapExtra([
['created', '創建'],
['edited', '修改'],
['remark', '備註']
]),
infoStatusMap: new MapExtra([
['normal', '正常'],
['invalid', '作廢']
])
};
class DataMap {
private _maps: Record<string, Record<string, IMapExtra>> = {
LC,
FC
};
public getMapsLC: () => Record<keyof typeof LC, IMapExtra> = () => {
return this._maps.LC;
};
public getMapsFC: () => Record<keyof typeof FC, IMapExtra> = () => {
return this._maps.FC;
};
}
const dataMap = new DataMap();
export const getMapsLC = dataMap.getMapsLC();
export const getMapsFC = dataMap.getMapsFC();
DataMap是主要的用於管理整個數據模塊,並導出DataMap對應模塊的該實例方法,getMapsXXX其實就是對外提供直接獲取對應XXX的數據模塊。
成員_maps通過對象形式區分模塊直接導出LC業務模塊的dataMap實例,當後續新增業務模塊時也是一樣的需要新建對應的獲取成員方法和導出。
這裏_maps是定義為了一個私有成員,其實不是真正意義上的私有成員,_maps屬性還是可以通過實例上獲取到的。
最後
下面我們來使用這些數據,如獲取一個下拉數據數組:
1.
import { getMapsLC } from '@/libs/DataMap';
console.log(getMapsFC.timeUnit.getOptions())
2.
//antd下拉框配置
{
name: 'action',
label: '選擇你的類型',
component: {
mode: 'multiple',
type: 'select',
options:getMapsLC.remarkAction.getOptions(),
placeholder: '請選擇一項'
}
},
//antd映射出對應的值
{
title: '表格某一列',
width: 100,
dataIndex: 'action',
render: (text: string) => getMapsLC.remarkAction.get(text)
},
導入getMapsLC之後,getMapsLC.remarkAction.get(XXX) 直接就能獲取到對應數據,getMapsLC.remarkAction.map則是直接取出一個map型的數據,並且可通過mapToOption轉為下拉框數據的值。
完整代碼:
import * as utils from '@/utils';
type MapType<T> = Array<[T, T]>;
interface IMapExtra {
map: Map<string| number, string| number>;
has: ( key: string) => boolean;
get: ( key: string) => string| number;
getOptions: () => TYPE.IOption[];
}
class MapExtra implements IMapExtra {
public map;
private _map;
public constructor( map: MapType<string| number>) {
this._map = new Map(map);
this.map = new Map(map);
}
public getOptions: IMapExtra['getOptions'] = () => utils.mapToOption(this._map);
public has: IMapExtra['has'] = ( key) => this._map.has(key);
public get: IMapExtra['get'] = ( key) => this._map.get(key) ?? key;
}
const FC = {
category: new MapExtra([
['product', '產品'],
['marketplace', '店鋪'],
]),
level: new MapExtra([
['primary', '一級分類'],
['secondary', '二級分類']
]),
timeUnit: new MapExtra([
['month', '月度'],
['quarter', '季度'],
['year', '年度'],
]),
};
const LC = {
remarkAction: new MapExtra([
['created', '創建'],
['edited', '修改'],
['remark', '手動備註']
]),
infoStatusMap: new MapExtra([
['normal', '正常'],
['invalid', '已作廢']
])
};
class DataMap {
private _maps: Record<string, Record<string, IMapExtra>> = {
LC,
FC
};
public getMapsLC: () => Record<keyof typeof LC, IMapExtra> = () => {
return this._maps.LC;
};
public getMapsFC: () => Record<keyof typeof FC, IMapExtra> = () => {
return this._maps.FC;
};
}
const dataMap = new DataMap();
export const getMapsLC = dataMap.getMapsLC();
export const getMapsFC = dataMap.getMapsFC();