import * as React from 'react'; import { Popover, Checkbox, Empty, Icon, message } from 'antd'; import { is, fromJS, Map } from 'immutable'; import classname from 'classnames' import * as Tool from 'jad-tool' import { Move } from 'jad-tool' import { RenderToolTip } from '../Tips/Tips' const Tools = Tool.extend({ setCacheData(key: string, value: any[]) { window.localStorage.setItem('JG_FilterConfigMenu_Cache_' + key, JSON.stringify(value)) }, getCacheData(key: string) { var value = window.localStorage.getItem('JG_FilterConfigMenu_Cache_' + key) return value ? JSON.parse(value) : false }, }, Tool) type ColumnItem = { title: string | Function, //标题 dataIndex: string, //index checked?: boolean, //是否选择 required?: boolean, //是否必选 group?: string, //分类组名 hasNoRight?: boolean, // 显示和查看权限, 默认为fasle - 有权限 } type FilterConfigMenuType = { title?: string | React.ReactNode; leftTitle?: string | React.ReactNode; minLength?: number; maxLength?: number; noGroup?: boolean; style?: any; children: React.ReactNode; columns: ColumnItem[]; receivePropsUpdate?: boolean //接收props是否更新 onChangeKey?: (key: any[])=> void; //返回自定义后的dataIndex[] initCallback?: boolean; //初始化后是否需要回调一次,恢复缓存考虑 cacheKey?: string; } const archorKey = 'archor' //react component export class FilterConfigMenu extends React.Component { state = { visible: false, renderGroupBuff: {}, checkedBuff: [], requiredBuff: [], columnsLength: 0, init: false, activeKey: '', moveFlag: false, } doNotDisplayGroup = {} archorKeyRefsMap = {} componentDidMount() { const { columns, cacheKey, receivePropsUpdate } = this.props; const cacheData = this.replaceColumnsCacheTitle(Tools.getCacheData(cacheKey), columns); // 页面首载或重载时,要对比前后this.props传入的columns是否有变化,若有,应该取columns if (!cacheKey || !cacheData || (!this.compareColumnsCacheData(columns, cacheData) && receivePropsUpdate)) { this.init(columns); } else { this.init(cacheData); } } private replaceColumnsCacheTitle(cacheData, columns) { if (!cacheData) return cacheData return cacheData.map(obj => { /** * 1. 如果缓存的title不存在(函数时) * 2. 或者缓存的title是个对象(reactNode等)时可能与columns的title不全等 * 3. 或者因为需求改动,原来的title字段内容更新了 * 以上等原因,需要将缓存的title替换为props columns的title */ let item = columns.find(item => obj['dataIndex'] === item['dataIndex']) obj['title'] = item ? item['title'] : obj['title'] return obj }) } private compareColumnsCacheData(columns, cacheData) { const copyColumns = fromJS(columns); const newCopyColumns = copyColumns.map(val => Map({ dataIndex: val.get('dataIndex'), required: val.get('required'), group: val.get('group'), hasNoRight: val.get('hasNoRight'), })); // 只有这些才是自定义table真正需要比较的 const copyCacheData = fromJS(cacheData); const newCopyCacheData = copyCacheData.map(val => Map({ dataIndex: val.get('dataIndex'), required: val.get('required'), group: val.get('group'), hasNoRight: val.get('hasNoRight'), })); return is(newCopyColumns, newCopyCacheData) } UNSAFE_componentWillReceiveProps(nextProps) { if (!this.state.init) return; const { receivePropsUpdate } = this.props; const { columnsLength: oldColumnsLength } = this.state; if (oldColumnsLength === 0 && receivePropsUpdate) { console.warn('FilterConfigMenu->receivePropsUpdate在更新columns的时候,检测到origin columns为空,请确保初始columns在初始化的时候被赋值') } } private init(columnsBuff: any[]) { const { initCallback } = this.props; let renderGroupBuff = {} const columnsLength = columnsBuff.length; let requiredBuff = [] columnsBuff.map(e => { let item = Object.assign({}, e) //防止引用类型穿透 let groupName = item.group || '其他' item.checked = item.hasNoRight ? false : (Tool.isExist(item.checked) ? item.checked : true); // 无权限则不选中 item.group = groupName; if (item.required) { requiredBuff.push(item) } else { let group = renderGroupBuff[groupName] || [] renderGroupBuff[groupName] = group group.push(item) } return item }) let checkedBuff = columnsBuff.filter(item => { if (item.checked && !item.required) { return item } }) this.setState({ renderGroupBuff, requiredBuff, checkedBuff, columnsLength, init: true }, () => { if (initCallback) { this.onHandleOk() } }) } private onHandleClear(allClear, target) { let { renderGroupBuff, checkedBuff } = this.state; let { minLength } = this.props; let checked = target.checked; // minLength存在 && 已选长度小雨等于minLength && 准备减少选中 (理论上不应该少于minLength) if (minLength && checkedBuff.length <= minLength && checked) { message.warn(`注意:最少须选中${minLength}个指标!`); return } if (allClear) { for (let key in renderGroupBuff) { renderGroupBuff[key] = renderGroupBuff[key].map(item => { item.checked = false; return item }) } checkedBuff = []; } else { var groupName = target.group || '其他' var dataIndex = target.dataIndex renderGroupBuff[groupName] = renderGroupBuff[groupName].map(item => { if (item.dataIndex === dataIndex) { item.checked = false; } return item }) checkedBuff = checkedBuff.filter(e => e.dataIndex !== dataIndex) } this.setState({ renderGroupBuff, checkedBuff }, () => { this.onHandleOk() }) } private onHandleChecked(checkData, target) { let { renderGroupBuff, checkedBuff } = this.state; let { minLength, maxLength } = this.props; let checked = target.target.checked; // maxLength存在 && 已选长度大于等于maxLength && 准备增加选中 if (maxLength && checkedBuff.length >= maxLength && checked) { message.warn(`注意:最多可选中${maxLength}个指标!`); return } // minLength存在 && 已选长度小雨等于minLength&& 准备减少选中 (理论上不应该少于minLength) if (minLength && checkedBuff.length <= minLength && !checked) { message.warn(`注意:最少须选中${minLength}个指标!`); return } // all Check if (Tools.isArray(checkData)) { var groupName = checkData[0].group renderGroupBuff[groupName] = renderGroupBuff[groupName].map(item => { item.checked = checked; return item }) } else { var groupName = checkData.group; var dataIndex = checkData.dataIndex; renderGroupBuff[groupName] = renderGroupBuff[groupName].map(item => { if (item.dataIndex === dataIndex) { item.checked = checked; } return item }) } this.setState({ renderGroupBuff }) this.onHandleOk() } private onHandleOk() { const { onChangeKey, cacheKey, columns } = this.props; const { renderGroupBuff, requiredBuff } = this.state; var filterBuff = [] for (let key in renderGroupBuff) { renderGroupBuff[key].map(item => { filterBuff[item.dataIndex] = item; return item }) } let newColumsCache = columns.map((e) => { let item = Object.assign({}, e)//防止引用类型穿透 item.checked = filterBuff[item.dataIndex] ? filterBuff[item.dataIndex].checked : item.checked return item }) let checkedBuff = newColumsCache.filter(item => { if (item.checked && !item.required && !item.hasNoRight) { return item } }) this.setState({ checkedBuff, }) //update cache if (!Tool.isEmptyArray(newColumsCache)) { Tools.setCacheData(cacheKey, newColumsCache) } //return [dataIndex(string),...] if (onChangeKey && Tools.isFunction(onChangeKey)) { var resultKey = requiredBuff.map(e => !e.hasNoRight && e.dataIndex) resultKey = resultKey.concat(checkedBuff.map(e => e.dataIndex)) onChangeKey(resultKey) } return false; } private onHandleVisibleChange(visible) { this.setState({ visible }) } private onHandleAnchorScroll(targetKey) { const { moveFlag } = this.state if (moveFlag) { return false; } if (!this.archorKeyRefsMap[archorKey] || !this.archorKeyRefsMap[targetKey]) { console.error('FilterConfigMenu->archor 暂未渲染完毕') return false; } const container: any = this.archorKeyRefsMap[archorKey] const target: any = this.archorKeyRefsMap[targetKey] var distance = 40 var minScroll = 50; var maxScroll = container.scrollHeight - container.clientHeight; var offsetTop = target.offsetTop var scroll = offsetTop - distance if (scroll < minScroll) { scroll = 0 } if (scroll > maxScroll) { scroll = maxScroll } this.setState({ moveFlag: true }) var m = new Move() m.ease([container.scrollTop, scroll], 500, function (v) { container.scrollTo(0, v) }, () => { setTimeout(() => { this.setState({ moveFlag: false }) }, 100) }) } private renderGroupItem(groupName, groupData, index) { const { activeKey } = this.state const { noGroup, maxLength } = this.props; let checkedAll = false; //是否全选中 checkedAll = groupData.every((obj) => obj.checked || obj.hasNoRight); return (
this.archorKeyRefsMap[archorKey + index] = ref} className={'jad-filterconfig-group-container'} key={groupName}> {!noGroup &&

{!maxLength && this.onHandleChecked(groupData, event)}>全选 }

}
{ groupData.map((item, index) => { if (item.required) { return null } if (item.hasNoRight) { // 无权限则不展示 return null } return ( this.onHandleChecked(item, event)} > {Tools.isFunction(item.title) ? item.title() : RenderToolTip(item.title, 6)} ) }) }
) } private renderGroup() { const { renderGroupBuff, moveFlag, activeKey } = this.state; const { noGroup } = this.props if (Tool.isEmptyObject(renderGroupBuff)) { return } //这里之前存在问题keys是无序列的 const otherGroup = Tool.filter(renderGroupBuff, (e, key) => key !== 'required') return
this.archorKeyRefsMap[archorKey] = ref} className={classname('jad-filterconfig-container-content', !noGroup && 'group')} onScroll={() => { if (!moveFlag && activeKey !== '') { this.setState({ activeKey: '' }) } }} > { Object.keys(otherGroup).map((key, index) => { var group = otherGroup[key]; let doNotDisplay = false; // 是否不展示 doNotDisplay = group.every((obj) => obj.hasNoRight); if (doNotDisplay) { const { doNotDisplayGroup } = this; this.doNotDisplayGroup = { ...doNotDisplayGroup, [key]: key } return } return this.renderGroupItem(key, group, index) }) }
} private renderAnchor() { const { renderGroupBuff, activeKey } = this.state const classStyle = classname( 'jad-filterconfig-container-content', 'anchor', 'jad-filterconfig-anchor-container' ) return
{ Object.keys(renderGroupBuff).map((key, index) => { if (this.doNotDisplayGroup[key]) return; return { this.setState({ activeKey: key }) this.onHandleAnchorScroll(archorKey + index) }} key={key}> {key} }) }
} private renderCheck() { const { checkedBuff } = this.state; return ; } private renderContent() { const { checkedBuff } = this.state; const { noGroup, leftTitle, minLength } = this.props; return (

{leftTitle || '可选择的列'}

{!noGroup && this.renderAnchor()} {this.renderGroup()}

已选{checkedBuff.length}个 {!minLength && this.onHandleClear(true, null)}>清空全部}

{this.renderCheck()}
) } private renderTitle() { const { title, } = this.props return (

{title || '自定义列'}

) } //render filter component groud public render() { const { children, style } = this.props; const { visible, columnsLength } = this.state; return ( {children} ) } }