/// import * as React from 'react'; import classNames from 'classnames'; import assign from 'object-assign'; import Hammer from 'react-hammerjs'; const prefix = 'tsp-component-ListView'; const distance = 1000; class ListView extends React.Component { constructor(props: TspComponentListViewProps, state: TspComponentListViewState) { super(props, state); this.onTouchmove = this.onTouchmove.bind(this); this.success = this.success.bind(this); this.timeoutCallback = this.timeoutCallback.bind(this); } public static defaultProps: TspComponentListViewProps = { // delay: 0, request: () => null, params: undefined, rowRender: () => null, emptyView: null, failView: null, timeoutView: null, loadingView: null, pageStart: 1, dataKey: ['result'] }; public state: TspComponentListViewState = { render: [] }; /** * 数据源字段 */ public static dataKey: string[]; /** * 页数字段 */ public static pageKey: string; /** * 每页条数字段 */ public static pageSizeKey: string; /** * 容器元素 */ private elem: HTMLElement; /** * 加载数据时底部提示文字 */ private loadingElem: HTMLElement; /** * setTimeout的返回对象 */ // tslint:disable-next-line:typedef private timeoutInstance; /** * 用于渲染的数据 */ private data: any[] = []; /** * 来自于接口的源数据 */ private sourceData: any[] = []; /** * 数据源字段 */ private get dataKey(): string[] { return this.props.dataKey ? this.props.dataKey : ListView.dataKey; } /** * 页数字段 */ private get pageKey(): string { return this.props.pageKey ? this.props.pageKey : ListView.pageKey; } /** * 每页条数字段 */ private get pageSizeKey(): string { return this.props.pageSizeKey ? this.props.pageSizeKey : ListView.pageSizeKey; } /** * 页数 */ private page: number; /** * 每页条数 */ private pageSize: number; /** * 加载状态 */ private status: TspComponentListViewStatus; /** * 滚动容器 */ private scrollContainerElem: HTMLElement; /** * 节流,每250毫秒触发一次,true: 可以触发,false: 不能触发 */ private throttle: boolean = true; /** * 节流setTimeout实例 */ // tslint:disable-next-line:typedef private throttleTimeoutInstance; /** * 加载中状态是否结束 */ private loadingOver: boolean; public componentWillUnmount(): void { this.elem.removeEventListener('touchmove', this.onTouchmove); clearTimeout(this.timeoutInstance); clearTimeout(this.throttleTimeoutInstance); } public componentDidMount(): void { this.elem = this.refs.elem as HTMLElement; this.loadingElem = this.refs.loading as HTMLElement; if (this.props.scrollContainerId) { this.scrollContainerElem = document.getElementById(this.props.scrollContainerId); } this.page = this.props.params.params[this.pageKey]; this.pageSize = this.props.params.params[this.pageSizeKey]; this.setState({ render: this.props.loadingView }); this.elem.addEventListener('touchmove', this.onTouchmove); if (this.props.delay) { this.timeoutInstance = setTimeout(() => { this.request(); }, this.props.delay); } else { this.request(); } } public componentWillReceiveProps(nextProps: TspComponentListViewProps): void { // 重载 if (nextProps.reloadId !== this.props.reloadId) { this.page = nextProps.pageStart; this.data = []; this.sourceData = []; this.status = 'loading'; this.setState({ render: nextProps.loadingView }); this.request(nextProps.params); } // 刷新当前 if (this.props.refreshId !== nextProps.refreshId) { this.setState({ render: this.dataRender(nextProps) }); } } /** * 滑动事件 */ public onTouchmove(e: any): void { if (this.status !== 'complete' || !this.loadingOver) { return; } if (this.throttle) { this.throttle = false; } else { return; } let offsetTop; let scrollHeight; if (this.props.scrollContainerId) { offsetTop = this.scrollContainerElem.offsetTop; scrollHeight = this.scrollContainerElem.offsetHeight; } else { offsetTop = document.body.scrollTop; scrollHeight = document.body.scrollHeight; } this.throttleTimeoutInstance = setTimeout(() => { this.throttle = true; }, 250); if (scrollHeight - offsetTop > distance) { return; } else { this.request(); } } /** * 发起请求 */ public request(sendParams: TspComponentAjaxPostOption = this.props.params): void { const requestParams = assign({}, sendParams.params, { [this.pageKey]: this.page }); const params = assign({}, sendParams, { params: requestParams, success: this.success, timeoutCallback: this.timeoutCallback, }); this.loadingOver = false; this.props.request(params); this.timeoutInstance = setTimeout(() => { if (!this.loadingOver) { this.status = 'loading'; if (this.data.length) { this.loadingElem.style.display = 'block'; } else { this.setState({ render: this.props.loadingView }); } } }, 300); } /** * 请求成功的函数 */ public success(data: any, status: boolean): void { if (status) { let temp; const firstKey = this.dataKey[0]; const secondKey = this.dataKey[1]; const tempData = firstKey && secondKey && this.sourceData ? this.sourceData[secondKey] : undefined; if (tempData) { this.sourceData = assign({}, this.sourceData, data, { [secondKey]: this.sourceData[secondKey].concat(data[firstKey][secondKey]) }); } else { if (firstKey && secondKey) { this.sourceData = data[firstKey]; } else { this.sourceData = this.sourceData ? this.sourceData.concat(data[firstKey]) : data[firstKey]; } } for (let i = 0; i < this.dataKey.length; i++) { if (i === 0) { temp = data[this.dataKey[0]]; } else if (i === 1) { temp = data[this.dataKey[0]][this.dataKey[1]]; } } this.data = this.data.concat(temp); if (temp.length) { this.status = 'complete'; } else if (this.data.length && temp.length < this.pageSize) { this.status = 'over'; } else if (!this.data.length && !temp.length) { this.status = 'empty'; } else { this.status = 'fail'; } } else { this.status = 'fail'; } this.page++; this.loadingElem.style.display = 'none'; this.loadingOver = true; this.setStateRender(); } /** * 设置state的render */ public setStateRender(): void { const length = this.data.length; let temp = []; if (this.status === 'complete') { temp = this.dataRender(); } else if (this.status === 'empty') { temp = this.props.emptyView; } else if (this.status === 'over') { temp = this.state.render; } else if (!length && this.status === 'fail') { // temp = this.props.failView; temp = this.timeoutView(); } else if (this.status === 'timeout') { temp = this.timeoutView(); } this.setState({ render: temp }); } /** * 超时视图,点击可刷新 */ public timeoutView(): any { return ( this.request()}> {this.props.timeoutView} ); } /** * 数据渲染 */ public dataRender(props: TspComponentListViewProps = this.props): any[] { const length = this.data.length; const temp = []; let i = 0; for (i; i < length; i++) { if (props.filter) { if (props.filter(this.data[i])) { temp.push(props.rowRender(this.data[i], i, this.data)); } } else { temp.push(props.rowRender(this.data[i], i, this.data)); } } return temp; } public timeoutCallback(): void { const length = this.data.length; if (!length) { this.status = 'timeout'; this.setState({ render: this.timeoutView() }); } else { this.loadingOver = true; this.status = 'complete'; } this.loadingElem.style.display = 'none'; } public render(): JSX.Element { const successExtendsViewShowStauts = this.status === 'complete' || this.status === 'over'; const completeExtendsViewShowStauts = this.status !== 'timeout'; return (
{successExtendsViewShowStauts && this.props.successBeforeView ? this.props.successBeforeView(this.sourceData) : null} {completeExtendsViewShowStauts && this.props.completeBeforeView ? this.props.completeBeforeView(this.sourceData) : null} {this.status === 'complete' ? ( this.state.render.map((value) => value) ) : ( this.state.render )}
{successExtendsViewShowStauts && this.props.successAfterView ? this.props.successAfterView(this.sourceData) : null}
); } } export default ListView;