// 核心库 import React, { Component } from 'react' import { connect } from 'dva' // 组件库 import { Tree, Icon, Tabs, Input, message } from 'antd' import { Scrollbars } from 'react-custom-scrollbars' import { PictureThumb } from '@woowalker/feui' import ListPage from '../../components/Table/ListPage' import Edit from '../../components/Edit/Edit' import CombinationPage from '../../components/Combination/CombinationPage' import FormPage from '../../components/FormPage' // 工具库 import { cloneDeep, isEqual } from 'lodash' import { getDataFieldValue, permissionUtils, getCustomParams, flatTreeData } from '../../utils/common' // 样式 import classNames from 'classnames' import styles from '../Component.css' import customTabStyles from '../../components/Combination/combinationPage.css' class VPage extends Component { constructor (props) { super(props) this.state = { activeKey: '', // 是否多选 multiCheck: props.treeConfig.multiCheck, multiCheckCount: props.treeConfig.multiCheckCount, // 多选选中的节点 selectedKeys: this.getDefaultSelectedKeys(props), // 拍平后的树数据 flatedTreeData: [], searchTreeData: [], // 是否收起左侧树 collapse: false } } componentDidMount () { const { treeData } = this.props this.checkFlatTreeData(treeData) } UNSAFE_componentWillReceiveProps (nextProps) { if (!isEqual(nextProps.treeData, this.props.treeData)) { this.checkFlatTreeData(nextProps.treeData) } } getThumbData = () => { const { treePicFilter } = this.props const thumbCodes = [] const thumbConfigs = [] if (Array.isArray(treePicFilter) && treePicFilter.length) { treePicFilter.forEach(({ Nav_PicFilterDetail = [], Nav_Picture }) => { thumbCodes.push(Nav_Picture.CODE) thumbConfigs.push({ target: Nav_Picture.CODE, rules: Nav_PicFilterDetail.map(npd => ({ field: npd.FIELD_TYPE === 1 ? `Node.${npd.NAME}` : npd.NAME, operate: Number(npd.OPERATE), value: typeof npd.VALUE === 'string' ? npd.VALUE.toLowerCase() : npd.VALUE })) }) }) } return { thumbCodes, thumbConfigs } } getDefaultSelectedKeys = (props) => { const { multiCheck, multiCheckCount } = props.treeConfig const defaultSelectedKeys = [] if (props.treeData && Array.isArray(props.treeData)) { props.treeData.forEach(({ node = { id: '' } }) => { node.id && defaultSelectedKeys.push(node.id) }) } return multiCheck ? (multiCheckCount ? defaultSelectedKeys.slice(0, multiCheckCount) : defaultSelectedKeys) : [defaultSelectedKeys[0]] } getDefaultExpandedKeys = () => { const { treeData, treeConfig } = this.props const { expandLevel } = treeConfig if (expandLevel) { const keys = [] function checkTreeDataLevel (data) { data.forEach(item => { if (item.level < expandLevel) { keys.push(item.node.id) if (Array.isArray(item.children) && item.children.length) { checkTreeDataLevel(item.children) } } }) } checkTreeDataLevel(treeData) return keys } return [] } checkShowBtn = (btn, nodeData) => { let isShow = !btn.btn_condition if (btn.btn_condition) { // 菜单参数 const { currActivatedMenu } = this.props.app || {} const menuFormParameter = currActivatedMenu?.MENU_FORM_PARAMS // 按钮配置条件 const conditionsOr = btn.btn_condition.split('|') const conditionsAnd = btn.btn_condition.split('&') if (conditionsOr.length === 1 && conditionsAnd.length === 1) { // 只配置了一个条件 const fields = btn.btn_condition.split(',') const val = getDataFieldValue(nodeData, fields[0].toLowerCase()) // 条件直接命中菜单参数,则直接显示 if (btn.btn_condition === menuFormParameter) { isShow = true } // 1 等于 else if (parseInt(fields[1], 10) === 1) { if (String(val) === String(fields[2].toLowerCase())) { isShow = true } } // 2 不等于 else if (parseInt(fields[1], 10) === 2) { if (String(val) !== String(fields[2].toLowerCase())) { isShow = true } } } else if (conditionsOr.length > 1) { // 或条件 for (let conditionOr of conditionsOr) { const fields = conditionOr.split(',') const val = getDataFieldValue(nodeData, fields[0].toLowerCase()) // 条件直接命中菜单参数,则直接显示 if (conditionOr === menuFormParameter) { isShow = true break } // 1 等于 else if (parseInt(fields[1], 10) === 1) { if (String(val) === String(fields[2].toLowerCase())) { isShow = true break } } // 2 不等于 else if (parseInt(fields[1], 10) === 2) { if (String(val) !== String(fields[2].toLowerCase())) { isShow = true break } } } } else if (conditionsAnd.length > 1) { // 与条件 const showResults = [] for (let conditionAnd of conditionsAnd) { const fields = conditionAnd.split(',') const val = getDataFieldValue(nodeData, fields[0].toLowerCase()) // 条件直接命中菜单参数,则直接显示 if (conditionAnd === menuFormParameter) { showResults.push(true) } // 1 等于 else if (parseInt(fields[1], 10) === 1) { if (String(val) === String(fields[2].toLowerCase())) { showResults.push(true) } } // 2 不等于 else if (parseInt(fields[1], 10) === 2) { if (String(val) !== String(fields[2].toLowerCase())) { showResults.push(true) } } } isShow = showResults.length === conditionsAnd.length } } return isShow } checkMultiShowBtn = (btn) => { const { currActivatedMenu } = this.props.app || {} const menuFormParameter = currActivatedMenu?.MENU_FORM_PARAMS let isShow = !btn.btn_condition || !menuFormParameter if (btn.btn_condition && menuFormParameter) { const conditionsOr = btn.btn_condition.split('|') const conditionsAnd = btn.btn_condition.split('&') if (conditionsOr.length === 1 && conditionsAnd.length === 1) { // 只配置了一个条件 if (btn.btn_condition === menuFormParameter) { isShow = true } } else if (conditionsOr.length > 1) { // 或条件 for (let conditionOr of conditionsOr) { if (conditionOr === menuFormParameter) { isShow = true break } } } else if (conditionsAnd.length > 1) { // 与条件 const showResults = [] for (let conditionAnd of conditionsAnd) { if (conditionAnd === menuFormParameter) { showResults.push(true) } } isShow = showResults.length === conditionsAnd.length } } return isShow } checkRenderBtn = (nodeData, treeConfig) => { const { btns = [] } = treeConfig const btnsTmp = [] btns.forEach(btn => { if (!btn.isRule || permissionUtils(this.props.login).checkBtn(treeConfig.formId, btn.pId || btn.id)) { // 如果配置了树多选,那么直接显示所配置的所有页面 if (treeConfig.multiCheck ? this.checkMultiShowBtn(btn) : this.checkShowBtn(btn, nodeData)) { btn.btnType === 3 && btn.isSameLevel && (btn.btnType = -1) // 新增同级 btnsTmp.push(btn) } } }) return btnsTmp } checkFlatTreeData = (treeData) => { // 拍平树数据 const flatedTreeData = [] flatTreeData(treeData, flatedTreeData) this.setState({ flatedTreeData: cloneDeep(flatedTreeData) }, () => { const { flatedTreeData, selectedKeys } = this.state const allKeys = flatedTreeData.map(({ node }) => node.id) const validKeys = selectedKeys.filter(item => allKeys.indexOf(item) !== -1) this.handleTreeNodesSelect(validKeys.length ? validKeys : this.getDefaultSelectedKeys(this.props)) }) } checkFlatTreeNodes = (treeData, treeConfig, selectedKeys) => { const selectedNodes = this.state.flatedTreeData.filter(item => selectedKeys.indexOf(item.node.id) !== -1) treeData.forEach(item => { const showBtns = this.checkRenderBtn(item, treeConfig) item.btns = showBtns.map(btnConfig => { // false 以按钮形式展示 true 以页面形式展示 let typePage = btnConfig.btnType === 5 || btnConfig.btnType === 12 || btnConfig.btnType === 14 || btnConfig.btnType === 0 // component 的赋值逻辑请参考 TreeBaseComponent -> Hindex -> getRenderBtn (取的是 modal 里面的表单) const { getRenderBtn, onSave } = this.props let component = getRenderBtn({ record: item.node, btnConfig }) // 表单编辑 if (btnConfig.btnType === 5) { const params = { id: item.node.id, parentId: item.node.id, formCode: treeConfig.formCode, data: { record: item.node }, onSave: (params) => { onSave instanceof Function && onSave(params) } } component = } // 列表查看 else if (btnConfig.btnType === 12) { const params = { formCode: btnConfig.formCode, formParam: this.props.formParam, data: { // 当前选中的节点 selectedNodes, // 传递给页面选中的节点参数 TreeSelected: selectedKeys, TreeSelectId: btnConfig.customParams, // 页面操作取消树节点选中 setTreeNodeUnCheck: this.handleTreeNodesUnCheck, rules: [ { field: 'TreeSelected', operator: 1, value: selectedKeys, isCustom: true } ] }, isQuery: true } if (btnConfig.customParams) { params.data.rules.push({ field: btnConfig.customParams, operator: 1, value: item.node.id }) } component = } // 组合表单 else if (btnConfig.btnType === 14) { const params = { formCode: btnConfig.formCode, formParam: this.props.formParam, data: { // 当前选中的节点 selectedNodes, // 传递给页面选中的节点参数 TreeSelected: selectedKeys, // 页面操作取消树节点选中 setTreeNodeUnCheck: this.handleTreeNodesUnCheck, rules: [ { field: btnConfig.customParams, operator: 1, value: item.node.id, isCustom: true } ] } } component = } // 自定义弹窗 else if (btnConfig.btnType === 0) { const record = item.node const custParams = getCustomParams(btnConfig.customParams) const params = { id: record.id, parentId: record.id, formCode: btnConfig.formCode, formParam: this.props.formParam, data: { ...record, ...custParams, customParams: btnConfig.customParams, // 当前选中的节点 selectedNodes, // 传递给页面选中的节点参数 TreeSelected: selectedKeys, // 页面操作取消树节点选中 setTreeNodeUnCheck: this.handleTreeNodesUnCheck } } component = } return { typePage, component, btnConfig } }) }) } renderTreeNodes = (treeData, thumbData) => { return treeData.map(item => { const { children, node } = item const { name, is_exist_alarm, child_exist_alarm, appliance_id, nav_appliance } = node const alert = `${name}${is_exist_alarm ? '(节点异常)' : child_exist_alarm ? '(存在子节点异常)' : (appliance_id != null && nav_appliance?.is_report == false) ? '(器具未采集数据)' : ''}` const color = is_exist_alarm ? 'red' : child_exist_alarm ? 'orange' : (appliance_id != null && nav_appliance?.is_report == false) ? 'rgba(170, 170, 170)' : 'inhert' const title = ( {name} { is_exist_alarm || child_exist_alarm || (appliance_id != null && nav_appliance?.is_report == false) ? ( ) : '' } ) const style = { width: 24, height: 24, position: 'relative', top: '-1px' } if (children) { return ( } > {this.renderTreeNodes(children, thumbData)} ) } return ( } /> ) }) } handleTreeNodesSelect = (selectedKeys) => { const { treeConfig } = this.props const { flatedTreeData, activeKey } = this.state // 获取选中节点所配置的按钮页面,并直接附在 flatedTreeData 数据之上 this.checkFlatTreeNodes(flatedTreeData, treeConfig, selectedKeys) // 查找被激活 tab 的 key 值 const findNode = flatedTreeData.find(item => item.node.id === selectedKeys[0]) const pageBtns = findNode && findNode.btns ? findNode.btns.filter(btn => btn.typePage) : [] const currActived = pageBtns.find(item => item.btnConfig.id === activeKey) this.setState({ selectedKeys, activeKey: !currActived ? (pageBtns.length > 1 ? pageBtns[0].btnConfig.id : '') : activeKey }) } handleTreeNodesCheck = ({ checked: selectedKeys }) => { const { multiCheckCount } = this.state if (multiCheckCount && selectedKeys.length > multiCheckCount) { message.error(`最多可选${multiCheckCount}个节点`) return } this.handleTreeNodesSelect(selectedKeys) } handleTreeNodesUnCheck = (applianceId) => { const { flatedTreeData, selectedKeys } = this.state const find = flatedTreeData.find(item => item.node?.appliance_id === applianceId) if (find) { const copyKeys = cloneDeep(selectedKeys) const findIndex = copyKeys.findIndex(item => item === find.node?.id) findIndex !== -1 && copyKeys.splice(findIndex, 1) this.handleTreeNodesCheck({ checked: copyKeys }) } } renderTabBar = (pageBtns) => { const { activeKey } = this.state return (
{ pageBtns.map(item => { return (
this.setState({ activeKey: item.btnConfig.id })} className={classNames(customTabStyles.tabBar__tab, { [customTabStyles.activated]: item.btnConfig.id === activeKey })}> {item.btnConfig.label}
) }) }
) } handleSearch = (evt) => { const { value } = evt.target const searchTreeData = this.state.flatedTreeData.filter(({ node = {} }) => (node.name || '').indexOf(value) !== -1) this.setState({ searchTreeData: value ? searchTreeData : [] }) } render () { const { treeData, treeConfig } = this.props const thumbData = this.getThumbData() const defaultExpandedKeys = this.getDefaultExpandedKeys() const expandProps = defaultExpandedKeys.length ? { defaultExpandedKeys } : { defaultExpandAll: true } const { activeKey, multiCheck, selectedKeys, flatedTreeData, searchTreeData, collapse } = this.state // 多选时,所有节点都会展示全部配置的页面,单选时,就一个 selectedKeys[0],所以直接取 selectedKeys[0] 来获取配置的页面 const targetNodes = flatedTreeData.find(item => item.node.id === selectedKeys[0]) || { node: {} } const toolBtns = targetNodes.btns ? targetNodes.btns.filter(btn => !btn.typePage) : [] const pageBtns = targetNodes.btns ? targetNodes.btns.filter(btn => btn.typePage) : [] const selectedTitle = multiCheck && selectedKeys.length > 1 ? `${selectedKeys.length}个` : targetNodes.node.name const selectedEvtKey = multiCheck && selectedKeys.length > 1 ? selectedKeys.join(';') : targetNodes.node.id return (
this.setState({ collapse: !collapse })} className={styles.treeVFold} />
collapse && this.setState({ collapse: false })} className={styles.treeVWrap}> {treeConfig.labelName}
当前选中:{selectedTitle || '无'}
{ !multiCheck || selectedKeys.length === 1 ? toolBtns.map(tb => tb.component) : null }
} onSelect={this.handleTreeNodesSelect} onCheck={this.handleTreeNodesCheck} > {this.renderTreeNodes(searchTreeData.length ? searchTreeData : treeData, thumbData)}
{ pageBtns.length ? ( pageBtns.length > 1 ? ( this.renderTabBar(pageBtns)} animated={false} className='combination-page__mainTab' > { pageBtns.map(pb => ( {pb.component} )) } ) : pageBtns[0].component ) : null }
) } } export default connect(({ login, app }) => ({ login, app }))(VPage)