CRUD 添加 loadDataOnce 属性, 用来实现前端分页 排序功能
This commit is contained in:
parent
1b28a905c1
commit
63f3f8aa91
|
@ -49,6 +49,7 @@ import KeyboardsCrudSchema from './CRUD/Keyboards';
|
||||||
import FootableCrudSchema from './CRUD/Footable';
|
import FootableCrudSchema from './CRUD/Footable';
|
||||||
import NestedCrudSchema from './CRUD/Nested';
|
import NestedCrudSchema from './CRUD/Nested';
|
||||||
import MergeCellSchema from './CRUD/MergeCell';
|
import MergeCellSchema from './CRUD/MergeCell';
|
||||||
|
import LoadOnceTableCrudSchema from './CRUD/LoadOnce';
|
||||||
import SdkTest from './Sdk/Test';
|
import SdkTest from './Sdk/Test';
|
||||||
import JSONSchemaForm from './Form/Schem';
|
import JSONSchemaForm from './Form/Schem';
|
||||||
import SimpleDialogSchema from './Dialog/Simple';
|
import SimpleDialogSchema from './Dialog/Simple';
|
||||||
|
@ -315,6 +316,11 @@ const navigations = [
|
||||||
path: 'crud/jump-next',
|
path: 'crud/jump-next',
|
||||||
component: makeSchemaRenderer(JumpNextCrudSchema)
|
component: makeSchemaRenderer(JumpNextCrudSchema)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: '一次性加载',
|
||||||
|
path: 'crud/load-once',
|
||||||
|
component: makeSchemaRenderer(LoadOnceTableCrudSchema)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: '测试',
|
label: '测试',
|
||||||
path: 'crud/test',
|
path: 'crud/test',
|
||||||
|
|
|
@ -0,0 +1,202 @@
|
||||||
|
export default {
|
||||||
|
$schema: "https://houtai.baidu.com/v2/schemas/page.json#",
|
||||||
|
title: "一次性加载,前端分页,前端排序",
|
||||||
|
body: {
|
||||||
|
type: "crud",
|
||||||
|
loadDataOnce: true,
|
||||||
|
api: "/api/sample?waitSeconds=1",
|
||||||
|
filter: {
|
||||||
|
title: "条件搜索",
|
||||||
|
submitText: "",
|
||||||
|
controls: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "keywords",
|
||||||
|
placeholder: "通过关键字搜索",
|
||||||
|
addOn: {
|
||||||
|
label: "搜索",
|
||||||
|
type: "submit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: "id",
|
||||||
|
label: "ID",
|
||||||
|
width: 20,
|
||||||
|
sortable: true,
|
||||||
|
type: "text",
|
||||||
|
toggled: true,
|
||||||
|
remark: 'Bla bla Bla'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "engine",
|
||||||
|
label: "Rendering engine",
|
||||||
|
sortable: true,
|
||||||
|
type: "text",
|
||||||
|
toggled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "browser",
|
||||||
|
label: "Browser",
|
||||||
|
sortable: true,
|
||||||
|
type: "text",
|
||||||
|
toggled: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "platform",
|
||||||
|
label: "Platform(s)",
|
||||||
|
sortable: true,
|
||||||
|
type: "text",
|
||||||
|
toggled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "version",
|
||||||
|
label: "Engine version",
|
||||||
|
type: "text",
|
||||||
|
toggled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "grade",
|
||||||
|
label: "CSS grade",
|
||||||
|
type: "text",
|
||||||
|
toggled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "operation",
|
||||||
|
label: "操作",
|
||||||
|
width: 100,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
type: "button",
|
||||||
|
icon: "fa fa-eye",
|
||||||
|
actionType: "dialog",
|
||||||
|
tooltip: "查看",
|
||||||
|
dialog: {
|
||||||
|
title: "查看",
|
||||||
|
body: {
|
||||||
|
type: "form",
|
||||||
|
controls: [
|
||||||
|
{
|
||||||
|
type: "static",
|
||||||
|
name: "engine",
|
||||||
|
label: "Engine"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "divider"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "static",
|
||||||
|
name: "browser",
|
||||||
|
label: "Browser"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "divider"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "static",
|
||||||
|
name: "platform",
|
||||||
|
label: "Platform(s)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "divider"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "static",
|
||||||
|
name: "version",
|
||||||
|
label: "Engine version"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "divider"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "static",
|
||||||
|
name: "grade",
|
||||||
|
label: "CSS grade"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "divider"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "html",
|
||||||
|
html:
|
||||||
|
"<p>添加其他 <span>Html 片段</span> 需要支持变量替换(todo).</p>"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "button",
|
||||||
|
icon: "fa fa-pencil",
|
||||||
|
tooltip: "编辑",
|
||||||
|
actionType: "drawer",
|
||||||
|
drawer: {
|
||||||
|
position: 'left',
|
||||||
|
size: 'lg',
|
||||||
|
title: "编辑",
|
||||||
|
body: {
|
||||||
|
type: "form",
|
||||||
|
name: "sample-edit-form",
|
||||||
|
api: "/api/sample/$id",
|
||||||
|
controls: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "engine",
|
||||||
|
label: "Engine",
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "divider"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "browser",
|
||||||
|
label: "Browser",
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "divider"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "platform",
|
||||||
|
label: "Platform(s)",
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "divider"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "version",
|
||||||
|
label: "Engine version"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "divider"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
name: "grade",
|
||||||
|
label: "CSS grade",
|
||||||
|
options: ["A", "B", "C", "D", "X"],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "button",
|
||||||
|
icon: "fa fa-times text-danger",
|
||||||
|
actionType: "ajax",
|
||||||
|
tooltip: "删除",
|
||||||
|
confirmText: "您确认要删除?",
|
||||||
|
api: "delete:/api/sample/$id"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
toggled: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,5 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
$schema: "https://houtai.baidu.com/v2/schemas/page.json#",
|
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
|
||||||
title: "增删改查示例",
|
title: "增删改查示例",
|
||||||
remark: "bla bla bla",
|
remark: "bla bla bla",
|
||||||
toolbar: [
|
toolbar: [
|
||||||
|
|
|
@ -298,7 +298,7 @@ module.exports = [
|
||||||
"browser": "Nintendo DS browser",
|
"browser": "Nintendo DS browser",
|
||||||
"platform": "Nintendo DS",
|
"platform": "Nintendo DS",
|
||||||
"version": "8.5",
|
"version": "8.5",
|
||||||
"grade": "C/A<sup>1</sup>"
|
"grade": "C"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"engine": "KHTML",
|
"engine": "KHTML",
|
||||||
|
|
|
@ -22,7 +22,7 @@ module.exports = function(req, res) {
|
||||||
|
|
||||||
|
|
||||||
function index(req, res) {
|
function index(req, res) {
|
||||||
const perPage = parseInt(req.query.perPage, 10) || 10;
|
const perPage = parseInt(req.query.perPage, 10);
|
||||||
const page = req.query.page || 1;
|
const page = req.query.page || 1;
|
||||||
let items = DB.concat();
|
let items = DB.concat();
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ function index(req, res) {
|
||||||
msg: 'ok',
|
msg: 'ok',
|
||||||
data: {
|
data: {
|
||||||
count: items.length,
|
count: items.length,
|
||||||
rows: items.splice((page -1) * perPage, perPage)
|
rows: perPage ? items.splice((page -1) * perPage, perPage) : items.concat()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ interface CRUDProps extends RendererProps {
|
||||||
filterDefaultVisible?: boolean;
|
filterDefaultVisible?: boolean;
|
||||||
syncResponse2Query?: boolean;
|
syncResponse2Query?: boolean;
|
||||||
keepItemSelectionOnPageChange?: boolean;
|
keepItemSelectionOnPageChange?: boolean;
|
||||||
|
loadDataOnce: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class CRUD extends React.Component<CRUDProps, any> {
|
export default class CRUD extends React.Component<CRUDProps, any> {
|
||||||
|
@ -108,6 +109,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||||
'keepItemSelectionOnPageChange',
|
'keepItemSelectionOnPageChange',
|
||||||
'labelTpl',
|
'labelTpl',
|
||||||
'labelField',
|
'labelField',
|
||||||
|
'loadDataOnce'
|
||||||
];
|
];
|
||||||
static defaultProps: Partial<CRUDProps> = {
|
static defaultProps: Partial<CRUDProps> = {
|
||||||
toolbarInline: true,
|
toolbarInline: true,
|
||||||
|
@ -122,12 +124,13 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||||
silentPolling: false,
|
silentPolling: false,
|
||||||
filterTogglable: false,
|
filterTogglable: false,
|
||||||
filterDefaultVisible: true,
|
filterDefaultVisible: true,
|
||||||
|
loadDataOnce: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
control: any;
|
control: any;
|
||||||
lastQuery: any;
|
lastQuery: any;
|
||||||
dataInvalid: boolean = false;
|
dataInvalid: boolean = false;
|
||||||
timer: NodeJS.Timer;
|
timer: number;
|
||||||
mounted: boolean;
|
mounted: boolean;
|
||||||
constructor(props: CRUDProps) {
|
constructor(props: CRUDProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -156,9 +159,10 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
const {location, store, pageField, perPageField, syncLocation} = this.props;
|
const {location, store, pageField, perPageField, syncLocation, loadDataOnce} = this.props;
|
||||||
|
|
||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
|
store.setLoadDataOnce(loadDataOnce);
|
||||||
|
|
||||||
if (syncLocation && location && (location.query || location.search)) {
|
if (syncLocation && location && (location.query || location.search)) {
|
||||||
store.updateQuery(
|
store.updateQuery(
|
||||||
|
@ -205,6 +209,10 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||||
store.setFilterTogglable(!!nextProps.filterTogglable, nextProps.filterDefaultVisible);
|
store.setFilterTogglable(!!nextProps.filterTogglable, nextProps.filterDefaultVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.props.loadDataOnce !== nextProps.loadDataOnce) {
|
||||||
|
store.setLoadDataOnce(!!nextProps.loadDataOnce);
|
||||||
|
}
|
||||||
|
|
||||||
if (props.syncLocation && props.location && props.location.search !== nextProps.location.search) {
|
if (props.syncLocation && props.location && props.location.search !== nextProps.location.search) {
|
||||||
// 同步地址栏,那么直接检测 query 是否变了,变了就重新拉数据
|
// 同步地址栏,那么直接检测 query 是否变了,变了就重新拉数据
|
||||||
store.updateQuery(
|
store.updateQuery(
|
||||||
|
@ -514,7 +522,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
search(values?: any, silent?: boolean, clearSelection?: boolean) {
|
search(values?: any, silent?: boolean, clearSelection?: boolean, forceReload = true) {
|
||||||
const {
|
const {
|
||||||
store,
|
store,
|
||||||
api,
|
api,
|
||||||
|
@ -561,6 +569,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||||
successMessage: messages && messages.fetchSuccess,
|
successMessage: messages && messages.fetchSuccess,
|
||||||
errorMessage: messages && messages.fetchFailed,
|
errorMessage: messages && messages.fetchFailed,
|
||||||
autoAppend: true,
|
autoAppend: true,
|
||||||
|
forceReload,
|
||||||
silent,
|
silent,
|
||||||
pageField,
|
pageField,
|
||||||
perPageField,
|
perPageField,
|
||||||
|
@ -604,7 +613,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||||
pageField,
|
pageField,
|
||||||
perPageField
|
perPageField
|
||||||
);
|
);
|
||||||
this.search();
|
this.search(undefined, undefined, undefined, false);
|
||||||
|
|
||||||
if (autoJumpToTopOnPagerChange && this.control) {
|
if (autoJumpToTopOnPagerChange && this.control) {
|
||||||
(findDOMNode(this.control) as HTMLElement).scrollIntoView();
|
(findDOMNode(this.control) as HTMLElement).scrollIntoView();
|
||||||
|
@ -819,7 +828,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
||||||
pageField,
|
pageField,
|
||||||
perPageField
|
perPageField
|
||||||
);
|
);
|
||||||
this.search();
|
this.search(undefined, undefined, undefined, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
reload(subpath?: string, query?: any) {
|
reload(subpath?: string, query?: any) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
IRendererStore
|
IRendererStore
|
||||||
} from './index';
|
} from './index';
|
||||||
import { ServiceStore } from './service';
|
import { ServiceStore } from './service';
|
||||||
import { extendObject, createObject, isObjectShallowModified } from '../utils/helper';
|
import { extendObject, createObject, isObjectShallowModified, sortArray } from '../utils/helper';
|
||||||
import {
|
import {
|
||||||
Api,
|
Api,
|
||||||
Payload,
|
Payload,
|
||||||
|
@ -32,7 +32,8 @@ export const CRUDStore = ServiceStore
|
||||||
prevPage: 1,
|
prevPage: 1,
|
||||||
page: 1,
|
page: 1,
|
||||||
perPage: 10,
|
perPage: 10,
|
||||||
total: 1,
|
total: 0,
|
||||||
|
loadDataOnce: false, // 配置数据是否一次性加载,如果是这样,由前端来完成分页,排序等功能。
|
||||||
mode: 'normal',
|
mode: 'normal',
|
||||||
hasNext: false,
|
hasNext: false,
|
||||||
selectedAction: types.frozen(),
|
selectedAction: types.frozen(),
|
||||||
|
@ -45,7 +46,7 @@ export const CRUDStore = ServiceStore
|
||||||
})
|
})
|
||||||
.views(self => ({
|
.views(self => ({
|
||||||
get lastPage() {
|
get lastPage() {
|
||||||
return Math.ceil(self.total / (self.perPage < 1 ? 10 : self.perPage));
|
return Math.max(Math.ceil(self.total / (self.perPage < 1 ? 10 : self.perPage)), 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
get filterData() {
|
get filterData() {
|
||||||
|
@ -100,10 +101,27 @@ export const CRUDStore = ServiceStore
|
||||||
|
|
||||||
|
|
||||||
const fetchInitData:(api:Api, data?:object, options?:fetchOptions & {
|
const fetchInitData:(api:Api, data?:object, options?:fetchOptions & {
|
||||||
|
forceReload?: boolean;
|
||||||
loadDataMode?: boolean;
|
loadDataMode?: boolean;
|
||||||
syncResponse2Query?: boolean;
|
syncResponse2Query?: boolean;
|
||||||
}) => Promise<any> = flow(function *getInitData(api:string, data:object, options?:fetchOptions) {
|
}) => Promise<any> = flow(function *getInitData(api:string, data:object, options?:fetchOptions) {
|
||||||
try {
|
try {
|
||||||
|
if (options && options.forceReload === false && self.loadDataOnce && self.total) {
|
||||||
|
let items = self.items.concat();
|
||||||
|
|
||||||
|
if (self.query.orderBy) {
|
||||||
|
const dir = /desc/i.test(self.query.orderDir) ? -1 : 1;
|
||||||
|
items = sortArray(items, self.query.orderBy, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
...self.data,
|
||||||
|
items: items.slice((self.page - 1) * self.perPage, self.page * self.perPage)
|
||||||
|
}
|
||||||
|
self.reInitData(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (fetchCancel) {
|
if (fetchCancel) {
|
||||||
fetchCancel();
|
fetchCancel();
|
||||||
fetchCancel = null;
|
fetchCancel = null;
|
||||||
|
@ -111,12 +129,19 @@ export const CRUDStore = ServiceStore
|
||||||
}
|
}
|
||||||
|
|
||||||
options && options.silent || self.markFetching(true);
|
options && options.silent || self.markFetching(true);
|
||||||
const json:Payload = yield (getRoot(self) as IRendererStore).fetcher(api, createObject(self.data, {
|
const ctx:any = createObject(self.data, {
|
||||||
...self.query,
|
...self.query,
|
||||||
[options && options.pageField || 'page']: self.page,
|
[options && options.pageField || 'page']: self.page,
|
||||||
[options && options.perPageField || 'perPage']: self.perPage,
|
[options && options.perPageField || 'perPage']: self.perPage,
|
||||||
...data
|
...data
|
||||||
}), {
|
});
|
||||||
|
|
||||||
|
// 一次性加载不要发送 perPage 属性
|
||||||
|
if (self.loadDataOnce) {
|
||||||
|
delete ctx[options && options.perPageField || 'perPage'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const json:Payload = yield (getRoot(self) as IRendererStore).fetcher(api, ctx, {
|
||||||
...options,
|
...options,
|
||||||
cancelExecutor: (executor:Function) => fetchCancel = executor
|
cancelExecutor: (executor:Function) => fetchCancel = executor
|
||||||
});
|
});
|
||||||
|
@ -174,12 +199,21 @@ export const CRUDStore = ServiceStore
|
||||||
...rest
|
...rest
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (self.loadDataOnce) {
|
||||||
|
if (self.query.orderBy) {
|
||||||
|
const dir = /desc/i.test(self.query.orderDir) ? -1 : 1;
|
||||||
|
rowsData = sortArray(rowsData, self.query.orderBy, dir);
|
||||||
|
}
|
||||||
|
data.items = rowsData.slice((self.page - 1) * self.perPage, self.page * self.perPage);
|
||||||
|
data.count = data.total = self.total = rowsData.length;
|
||||||
|
}
|
||||||
|
|
||||||
self.items.replace(rowsData);
|
self.items.replace(rowsData);
|
||||||
self.reInitData(data);
|
self.reInitData(data);
|
||||||
options && options.syncResponse2Query !== false && updateQuery(pick(rest, Object.keys(self.query)), undefined, options && options.pageField || 'page', options && options.perPageField || 'perPage');
|
options && options.syncResponse2Query !== false && updateQuery(pick(rest, Object.keys(self.query)), undefined, options && options.pageField || 'page', options && options.perPageField || 'perPage');
|
||||||
|
|
||||||
self.total = parseInt(total || count, 10) || 0;
|
self.total = parseInt(total || count, 10) || 0;
|
||||||
typeof page !== 'undefined' && (self.page = self.pageNum = parseInt(page, 10));
|
typeof page !== 'undefined' && (self.page = parseInt(page, 10));
|
||||||
|
|
||||||
// 分页情况不清楚,只能知道有没有下一页。
|
// 分页情况不清楚,只能知道有没有下一页。
|
||||||
if (typeof hasNext !== 'undefined') {
|
if (typeof hasNext !== 'undefined') {
|
||||||
|
@ -279,6 +313,10 @@ export const CRUDStore = ServiceStore
|
||||||
self.hasInnerModalOpen = value;
|
self.hasInnerModalOpen = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setLoadDataOnce = function(value:boolean) {
|
||||||
|
self.loadDataOnce = value;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
setPristineQuery,
|
setPristineQuery,
|
||||||
updateQuery,
|
updateQuery,
|
||||||
|
@ -290,7 +328,8 @@ export const CRUDStore = ServiceStore
|
||||||
setFilterVisible,
|
setFilterVisible,
|
||||||
setSelectedItems,
|
setSelectedItems,
|
||||||
setUnSelectedItems,
|
setUnSelectedItems,
|
||||||
setInnerModalOpened
|
setInnerModalOpened,
|
||||||
|
setLoadDataOnce
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -617,4 +617,20 @@ export const bulkBindFunctions = function<T extends {
|
||||||
[propName:string]: any
|
[propName:string]: any
|
||||||
}>(context:T, funNames:Array<FunctionPropertyNames<T>>) {
|
}>(context:T, funNames:Array<FunctionPropertyNames<T>>) {
|
||||||
funNames.forEach(key => context[key] = context[key].bind(context));
|
funNames.forEach(key => context[key] = context[key].bind(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sortArray<T extends any>(items:Array<T>, field:string, dir: -1 | 1):Array<T> {
|
||||||
|
return items.sort((a, b) => {
|
||||||
|
let ret: number;
|
||||||
|
const a1 = a[field];
|
||||||
|
const b1 = b[field];
|
||||||
|
|
||||||
|
if (typeof a1 === 'number' && typeof b1 === 'number') {
|
||||||
|
ret = a1 < b1 ? -1 : a1 === b1 ? 0 : 1;
|
||||||
|
} else {
|
||||||
|
ret = String(a1).localeCompare(String(b1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret * dir;
|
||||||
|
})
|
||||||
}
|
}
|
Loading…
Reference in New Issue