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 NestedCrudSchema from './CRUD/Nested';
|
||||
import MergeCellSchema from './CRUD/MergeCell';
|
||||
import LoadOnceTableCrudSchema from './CRUD/LoadOnce';
|
||||
import SdkTest from './Sdk/Test';
|
||||
import JSONSchemaForm from './Form/Schem';
|
||||
import SimpleDialogSchema from './Dialog/Simple';
|
||||
|
@ -315,6 +316,11 @@ const navigations = [
|
|||
path: 'crud/jump-next',
|
||||
component: makeSchemaRenderer(JumpNextCrudSchema)
|
||||
},
|
||||
{
|
||||
label: '一次性加载',
|
||||
path: 'crud/load-once',
|
||||
component: makeSchemaRenderer(LoadOnceTableCrudSchema)
|
||||
},
|
||||
{
|
||||
label: '测试',
|
||||
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 {
|
||||
$schema: "https://houtai.baidu.com/v2/schemas/page.json#",
|
||||
$schema: "http://amis.baidu.com/v2/schemas/page.json#",
|
||||
title: "增删改查示例",
|
||||
remark: "bla bla bla",
|
||||
toolbar: [
|
||||
|
|
|
@ -298,7 +298,7 @@ module.exports = [
|
|||
"browser": "Nintendo DS browser",
|
||||
"platform": "Nintendo DS",
|
||||
"version": "8.5",
|
||||
"grade": "C/A<sup>1</sup>"
|
||||
"grade": "C"
|
||||
},
|
||||
{
|
||||
"engine": "KHTML",
|
||||
|
|
|
@ -22,7 +22,7 @@ module.exports = function(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;
|
||||
let items = DB.concat();
|
||||
|
||||
|
@ -62,7 +62,7 @@ function index(req, res) {
|
|||
msg: 'ok',
|
||||
data: {
|
||||
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;
|
||||
syncResponse2Query?: boolean;
|
||||
keepItemSelectionOnPageChange?: boolean;
|
||||
loadDataOnce: boolean;
|
||||
}
|
||||
|
||||
export default class CRUD extends React.Component<CRUDProps, any> {
|
||||
|
@ -108,6 +109,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
|||
'keepItemSelectionOnPageChange',
|
||||
'labelTpl',
|
||||
'labelField',
|
||||
'loadDataOnce'
|
||||
];
|
||||
static defaultProps: Partial<CRUDProps> = {
|
||||
toolbarInline: true,
|
||||
|
@ -122,12 +124,13 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
|||
silentPolling: false,
|
||||
filterTogglable: false,
|
||||
filterDefaultVisible: true,
|
||||
loadDataOnce: false,
|
||||
};
|
||||
|
||||
control: any;
|
||||
lastQuery: any;
|
||||
dataInvalid: boolean = false;
|
||||
timer: NodeJS.Timer;
|
||||
timer: number;
|
||||
mounted: boolean;
|
||||
constructor(props: CRUDProps) {
|
||||
super(props);
|
||||
|
@ -156,9 +159,10 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
|||
}
|
||||
|
||||
componentWillMount() {
|
||||
const {location, store, pageField, perPageField, syncLocation} = this.props;
|
||||
const {location, store, pageField, perPageField, syncLocation, loadDataOnce} = this.props;
|
||||
|
||||
this.mounted = true;
|
||||
store.setLoadDataOnce(loadDataOnce);
|
||||
|
||||
if (syncLocation && location && (location.query || location.search)) {
|
||||
store.updateQuery(
|
||||
|
@ -205,6 +209,10 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
|||
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) {
|
||||
// 同步地址栏,那么直接检测 query 是否变了,变了就重新拉数据
|
||||
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 {
|
||||
store,
|
||||
api,
|
||||
|
@ -561,6 +569,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
|||
successMessage: messages && messages.fetchSuccess,
|
||||
errorMessage: messages && messages.fetchFailed,
|
||||
autoAppend: true,
|
||||
forceReload,
|
||||
silent,
|
||||
pageField,
|
||||
perPageField,
|
||||
|
@ -604,7 +613,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
|||
pageField,
|
||||
perPageField
|
||||
);
|
||||
this.search();
|
||||
this.search(undefined, undefined, undefined, false);
|
||||
|
||||
if (autoJumpToTopOnPagerChange && this.control) {
|
||||
(findDOMNode(this.control) as HTMLElement).scrollIntoView();
|
||||
|
@ -819,7 +828,7 @@ export default class CRUD extends React.Component<CRUDProps, any> {
|
|||
pageField,
|
||||
perPageField
|
||||
);
|
||||
this.search();
|
||||
this.search(undefined, undefined, undefined, false);
|
||||
}
|
||||
|
||||
reload(subpath?: string, query?: any) {
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
IRendererStore
|
||||
} from './index';
|
||||
import { ServiceStore } from './service';
|
||||
import { extendObject, createObject, isObjectShallowModified } from '../utils/helper';
|
||||
import { extendObject, createObject, isObjectShallowModified, sortArray } from '../utils/helper';
|
||||
import {
|
||||
Api,
|
||||
Payload,
|
||||
|
@ -32,7 +32,8 @@ export const CRUDStore = ServiceStore
|
|||
prevPage: 1,
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
total: 1,
|
||||
total: 0,
|
||||
loadDataOnce: false, // 配置数据是否一次性加载,如果是这样,由前端来完成分页,排序等功能。
|
||||
mode: 'normal',
|
||||
hasNext: false,
|
||||
selectedAction: types.frozen(),
|
||||
|
@ -45,7 +46,7 @@ export const CRUDStore = ServiceStore
|
|||
})
|
||||
.views(self => ({
|
||||
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() {
|
||||
|
@ -100,10 +101,27 @@ export const CRUDStore = ServiceStore
|
|||
|
||||
|
||||
const fetchInitData:(api:Api, data?:object, options?:fetchOptions & {
|
||||
forceReload?: boolean;
|
||||
loadDataMode?: boolean;
|
||||
syncResponse2Query?: boolean;
|
||||
}) => Promise<any> = flow(function *getInitData(api:string, data:object, options?:fetchOptions) {
|
||||
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) {
|
||||
fetchCancel();
|
||||
fetchCancel = null;
|
||||
|
@ -111,12 +129,19 @@ export const CRUDStore = ServiceStore
|
|||
}
|
||||
|
||||
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,
|
||||
[options && options.pageField || 'page']: self.page,
|
||||
[options && options.perPageField || 'perPage']: self.perPage,
|
||||
...data
|
||||
}), {
|
||||
});
|
||||
|
||||
// 一次性加载不要发送 perPage 属性
|
||||
if (self.loadDataOnce) {
|
||||
delete ctx[options && options.perPageField || 'perPage'];
|
||||
}
|
||||
|
||||
const json:Payload = yield (getRoot(self) as IRendererStore).fetcher(api, ctx, {
|
||||
...options,
|
||||
cancelExecutor: (executor:Function) => fetchCancel = executor
|
||||
});
|
||||
|
@ -174,12 +199,21 @@ export const CRUDStore = ServiceStore
|
|||
...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.reInitData(data);
|
||||
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;
|
||||
typeof page !== 'undefined' && (self.page = self.pageNum = parseInt(page, 10));
|
||||
typeof page !== 'undefined' && (self.page = parseInt(page, 10));
|
||||
|
||||
// 分页情况不清楚,只能知道有没有下一页。
|
||||
if (typeof hasNext !== 'undefined') {
|
||||
|
@ -279,6 +313,10 @@ export const CRUDStore = ServiceStore
|
|||
self.hasInnerModalOpen = value;
|
||||
}
|
||||
|
||||
const setLoadDataOnce = function(value:boolean) {
|
||||
self.loadDataOnce = value;
|
||||
}
|
||||
|
||||
return {
|
||||
setPristineQuery,
|
||||
updateQuery,
|
||||
|
@ -290,7 +328,8 @@ export const CRUDStore = ServiceStore
|
|||
setFilterVisible,
|
||||
setSelectedItems,
|
||||
setUnSelectedItems,
|
||||
setInnerModalOpened
|
||||
setInnerModalOpened,
|
||||
setLoadDataOnce
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -617,4 +617,20 @@ export const bulkBindFunctions = function<T extends {
|
|||
[propName:string]: any
|
||||
}>(context:T, funNames:Array<FunctionPropertyNames<T>>) {
|
||||
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