import { Popover, Button, Checkbox, Empty, Icon } from 'antd'; import { is, fromJS, Map } from 'immutable'; import classname from 'classnames' import * as React from 'react'; import * as Tool from 'jad-tool' import { Move } from 'jad-tool' import { overstepToTooltip, RenderToolTip } from '../Tips/Tips' const Tools = Tool.extend({ setCacheData(key: string, value: any[]) { window.localStorage.setItem('JG_FilterColumns_Cache_' + key, JSON.stringify(value)) }, getCacheData(key: string) { var value = window.localStorage.getItem('JG_FilterColumns_Cache_' + key) return value ? JSON.parse(value) : false }, }, Tool) // 权重 hasNoRight > required > checked type ColumnItem = { title: string | Function, //标题 dataIndex: string, //index group?: string, //分类组名 hasNoRight?: boolean, // 显示和查看权限, 默认为fasle - 有权限 required?: boolean, //是否必选 checked?: boolean, //是否选择 } type FilterColumnsMenuType = { title?: string | React.ReactNode; 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 FilterColumnsMenu extends React.Component { state = { visible: false, renderGroupBuff: {}, checkedBuff: [], groupLength: 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'), group: val.get('group'), hasNoRight: val.get('hasNoRight'), required: val.get('required'), })); // 只有这些才是自定义table真正需要比较的 const copyCacheData = fromJS(cacheData); const newCopyCacheData = copyCacheData.map(val => Map({ dataIndex: val.get('dataIndex'), group: val.get('group'), hasNoRight: val.get('hasNoRight'), required: val.get('required'), })); return is(newCopyColumns, newCopyCacheData) } UNSAFE_componentWillReceiveProps(nextProps) { if (!this.state.init) return; const { receivePropsUpdate } = this.props; const { groupLength: oldGroupLength } = this.state; if (oldGroupLength === 0 && receivePropsUpdate) { console.warn('FilterColumnsMenu->receivePropsUpdate在更新columns的时候,检测到origin columns为空,请确保初始columns在初始化的时候被赋值') } } private init(columnsBuff: any[]) { const { initCallback } = this.props; let renderGroupBuff = {} const groupLength = columnsBuff.length; let checkedBuff = columnsBuff.map(e => { let item = Object.assign({}, e) //防止引用类型穿透 let groupName = item.group || '其他' item.required = item.hasNoRight ? false : item.required; // 无权限则非必须 item.checked = item.hasNoRight ? false : (Tool.isExist(item.checked) ? item.checked : true); // 无权限则不选中,item.checked 未主动赋值时默认值为 true item.group = groupName; let group = renderGroupBuff[groupName] || [] renderGroupBuff[groupName] = group group.push(item) return item }).filter(item => { if (item.required || item.checked) { return item } }) this.setState({ renderGroupBuff, checkedBuff, groupLength, init: true }, () => { if (initCallback) { this.onHandleOk() } }) } private onHandleClear(allClear, target) { let { renderGroupBuff, checkedBuff } = this.state if (allClear) { for (let key in renderGroupBuff) { renderGroupBuff[key] = renderGroupBuff[key].map(item => { if (!item.required) { // 必选的不能操作 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.required) { // 必选的不能操作 item.checked = false; } return item }) checkedBuff = checkedBuff.filter(e => e.dataIndex !== dataIndex) } this.setState({ renderGroupBuff, checkedBuff }, () => { this.onHandleOk() }) } private onHandleChecked(checkData, target) { let { renderGroupBuff } = this.state let checked = target.target.checked // all Check if (Tools.isArray(checkData)) { var groupName = checkData[0].group renderGroupBuff[groupName] = renderGroupBuff[groupName].map(item => { if (!item.required) { // 必选的不能操作 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 } = 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.required || item.checked) && !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 = 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('FilterColumnsMenu->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 let checkedAll = false; //是否全选中 checkedAll = groupData.every((obj) => obj.required || obj.checked || obj.hasNoRight); return (
this.archorKeyRefsMap[archorKey + index] = ref} className='jad-filtercolumsmenu-group-container' key={groupName}>

this.onHandleChecked(groupData, event)}>全选

{ groupData.map((item, index) => { 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 if (Tool.isEmptyObject(renderGroupBuff)) { return } //这里之前存在问题keys是无序列的 return
this.archorKeyRefsMap[archorKey] = ref} className={classname('jad-filtercolumsmenu-container-content', 'group')} onScroll={() => { if (!moveFlag && activeKey !== '') { this.setState({ activeKey: '' }) } }} > { Object.keys(renderGroupBuff).map((key, index) => { var group = renderGroupBuff[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-filtercolumsmenu-container-content', 'anchor', 'jad-filtercolumsmenu-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}> {overstepToTooltip(key, 6)} }) }
} private renderCheck() { const { checkedBuff } = this.state; return ; } private renderContent() { const { checkedBuff } = this.state return (

可添加的列

{this.renderAnchor()} {this.renderGroup()}

已选{checkedBuff.length}列 this.onHandleClear(true, null)}>清空全部

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

{title ? title : '自定义列'}

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