import React, { useEffect, useState } from 'react'; import { message, notification, Tooltip, Modal, Input, Button, Spin, Empty } from 'antd'; import { FileAddTwoTone, FolderAddTwoTone, FolderFilled, FileOutlined, DeleteTwoTone, DeliveredProcedureOutlined, } from '@ant-design/icons'; import classnames from 'classnames'; import { miniAppSiteTree, getMiniAppModuleTree, addMenu, getMenuInfo, deleteMenu, editMenu, getRoleListPlain, dragMenu, } from '@/services/mobileConfig/api'; import { setIn } from 'immutable'; import { appConnector } from '@/containers/App/store'; import Tree from '@/components/ExpendableTree'; import AddForm from './AddForm'; import EditForm from './editForm'; import ImportOrExport from './ImportOrExport'; import CheckList from './checkBox'; import styles from './miniMenu.less'; const MiniMenu = props => { const { userMode, clientName, parentKey } = props; const [flag, setFlag] = useState(1); // 刷新标志 const [treeFlag, setTreeFlag] = useState(true); const [treeData, setTreeData] = useState([]); // 树的数据 const [loading, setLoading] = useState(false); // 加载 const [menuID, setMenuID] = useState(''); // 选中的树ID // const [saveID, setSaveID] = useState(''); // 保存选择的树id const [nodeType, setNodeType] = useState(''); // 选中的节点类型 const [info, setInfo] = useState({}); const [addVisible, setAddVisible] = useState(false); // 新增弹窗 const [addTwoVisible, setAddTwoVisible] = useState(false); // 编辑弹窗 const [delVisible, setDelVisible] = useState(false); // 删除弹窗 const [nodeObj, setNodeObj] = useState({}); const [addType, setAddType] = useState(''); // 添加下级类型 const [modalTitle, setModalTitle] = useState(''); const [roleList, setRoleList] = useState([]); // 复选框选中的值 const [modalLoading, setModalLoading] = useState(false); const [submitLoading, setSubmitLoading] = useState(false); const [addList, setAddList] = useState([]); const [newTreeList, setNewTreeList] = useState([]); const [expendKey, setExpendKey] = useState(''); // 保存默认展开项 const [keepId, setKeepId] = useState(''); const [mapDataList, setMapDataList] = useState(new Set()); const [keepType, setKeepType] = useState(''); const [inf, setInf] = useState({}); const [checkList, setCheckList] = useState([]); const [importOrExportVisible, setImportOrExportVisible] = useState(false); // 导入导出弹窗 // 获取菜单树 useEffect(() => { console.log(props); updateTrees(); }, []); // 获取角色列表 useEffect(() => { getRoleListPlain({ subSystemValue: clientName || 'miniapp', _dc: Date.now(), _version: 9999, }).then(res => { if (res.code === 0) { setAddList(res.list); } }); }, [flag, clientName]); // 获取菜单信息 useEffect(() => { console.log(flag, 'flag'); if (flag === 1) { setExpendKey(menuID); } getInfo(); }, [menuID]); // 更新树 const updateTrees = e => { getMiniAppModuleTree({ userMode: userMode || 'super', }) .then(res => { // console.log(res, 'res'); // const result1 = res[0].children[0].children[2].children; // setNewTreeList(JSON.parse(JSON.stringify(result1))); // setTreeData(result1); // setLoading(false); if (res.code === 0) { const { data } = res; let obj = data[0].children.find(item => item.id === parentKey); const result = obj.children[1].children; console.log(result, 'result'); setTreeData(result); if (result.length > 0) { if (e) { result.map(item => mapData(item, e)); let aa = [...mapDataList].find(i => i.menuID == e); console.log(aa); setMenuID(aa.menuID); setExpendKey(aa.menuID); switch (aa.menuType) { case 'MiniAppMenuGroup': setNodeType(1); setAddType(1); break; case 'MiniAppMenuGroupTwo': setNodeType(2); setAddType(2); break; case 'MiniAppMenu': setNodeType(3); setAddType(3); break; case 'MiniAppMenuThree': setNodeType(4); setAddType(4); break; default: break; } } if (flag === 1) { setMenuID(result[0].menuID); switch (result[0].menuType) { case 'MiniAppMenuGroup': setNodeType(1); setAddType(1); break; case 'MiniAppMenuGroupTwo': setNodeType(2); setAddType(2); break; case 'MiniAppMenu': setNodeType(3); setAddType(3); break; case 'MiniAppMenuThree': setNodeType(4); setAddType(4); break; default: break; } setFlag(flag + 1); } } setLoading(false); setKeepId(result[0]); // 第一次加载,默认选择第一个组织 // if (treeFlag) { // handleSelect([result[0].menuID], false); // setTreeFlag(false); // } } else { notification({ message: '提示', duration: 10, description: '获取菜单失败', }); } }) .catch(err => { setLoading(false); message.error(err); }); }; // 处理数据 const mapTree = val => { const obj = JSON.parse(JSON.stringify(val)); const hasChild = obj.children.length > 0; return { title: ( <div className={styles.title}> <div>{obj.text}</div> <div className={styles.tip}> {obj.menuType === 'MiniAppMenuGroup' && ( <Tooltip title="导入导出菜单组" className={styles.fs}> <DeliveredProcedureOutlined style={{ color: '#178BF7' }} onClick={e => importAndExport(obj, e)} /> </Tooltip> )} {obj.menuType === 'MiniAppMenuGroupTwo' && ( <Tooltip title="导入导出菜单组" className={styles.fs}> <DeliveredProcedureOutlined style={{ color: '#178BF7' }} onClick={e => importAndExport(obj, e)} /> </Tooltip> )} {obj.menuType === 'MiniAppMenuGroup' && ( <Tooltip title="新增菜单组" className={styles.fsize}> <FolderAddTwoTone onClick={e => addMenuGroupTip(obj, e)} /> </Tooltip> )} {obj.menuType === 'MiniAppMenuGroupTwo' && ( <Tooltip title="新增功能菜单" className={styles.fsize}> <FileAddTwoTone onClick={e => addMenuTip(obj, e)} /> </Tooltip> )} <Tooltip title="删除菜单" className={styles.fsize}> <DeleteTwoTone onClick={e => deleteMenuTip(obj, e)} /> </Tooltip> </div> </div> ), key: obj.menuID, icon: obj.menuType !== 'MiniAppMenuThree' && obj.menuType !== 'MiniAppMenu' ? ( <FolderFilled /> ) : ( <FileOutlined /> ), menuType: obj.menuType, children: hasChild ? obj.children.map(i => mapTree(i)) : [], }; }; const mapData = (val, e) => { const obj = { ...val }; const hasChild = obj.children.length > 0; if (hasChild) { obj.children.map(i => mapData(i)); } mapDataList.add(obj); }; // 树的点击事件 const handleSelect = (prop, treeNode) => { console.log(prop, treeNode); if (treeNode) { const { node, node: { menuType }, } = treeNode; console.log(menuType); switch (menuType) { // 最上级菜单组 case 'MiniAppMenuGroup': setNodeType(1); setAddType(1); break; // 第二级菜单组 case 'MiniAppMenuGroupTwo': setNodeType(2); setAddType(2); break; // 最上级菜单 case 'MiniAppMenu': setNodeType(3); setAddType(3); break; // 第三季功能菜单 case 'MiniAppMenuThree': setNodeType(4); setAddType(4); break; default: break; } } if (prop[0]) { setMenuID(prop[0]); // setSaveID(prop[0]); } }; const handleExpand = (keys, { expanded, node }) => { console.log(keys); }; const getInfo = id => { if (!menuID) { return; } setLoading(true); getMenuInfo({ menuID, }) .then(res => { setLoading(false); if (res.code === 0) { let infoList = res.data.relatedRoleList.map(item => ({ groupName: item.groupName, isChecked: item.isChecked, list: item.list, checkedList: new Set([]), })); setInf(infoList); setInfo({ ...res.data }); let data = new Set(); infoList.map(item => { if (item.isChecked == true) { data.add(item.groupName); } item.list.map(i => { if (i.related) { item.checkedList.add(i.relatedRoleCode); } }); }); setCheckList(data); } else { notification.error({ message: '提示', duration: 10, description: res.msg || '获取失败', }); } console.log(res, 'resss'); }) .catch(err => { setLoading(false); console.error(err); }); }; // 删除的回调 const deleteMenuTip = (val, e) => { e.stopPropagation(); console.log(val, 'val'); setModalTitle(val.text); setNodeObj(val); setDelVisible(true); }; const delMenu = () => { setModalLoading(true); deleteMenu({ menuID: nodeObj.menuID, _dc: Date.now(), _version: 9999, }) .then(res => { setModalLoading(false); if (res.code === 0) { setDelVisible(false); setFlag(flag + 1); if (nodeObj.menuID == menuID) { updateTrees(); setMenuID(keepId.menuID); setNodeType(1); setNodeObj(keepId); } else { updateTrees(); } notification.success({ message: '提示', duration: 3, description: '删除成功', }); } else { notification.error({ message: '提示', duration: 10, description: res.msg || '删除失败', }); } }) .catch(err => { setModalLoading(false); console.log(err); }); }; // 导入导出菜单组 const importAndExport = (val, e) => { e.stopPropagation(); setNodeObj(val); setImportOrExportVisible(true); }; // 新增菜单组 const addMenuGroupTip = (val, e) => { e.stopPropagation(); console.log(val, 'addgroup'); setModalTitle(`在${val.text}下新增菜单组`); setNodeObj(val); setAddType(2); setAddVisible(true); setKeepType('MiniAppMenuGroupTwo'); }; const rootAddGroup = () => { setNodeObj(''); // setNodeType(1); setModalTitle('新增最上级菜单组'); setAddType(1); setAddVisible(true); setKeepType('MiniAppMenuGroup'); }; // 新增功能菜单 const addMenuTip = (val, e) => { e.stopPropagation(); console.log(val, 'add'); setNodeObj(val); setModalTitle(`在${val.text}下新增功能菜单`); setAddType(4); setAddTwoVisible(true); setKeepType('MiniAppMenuThree'); }; const rootAdd = () => { setModalTitle('新增最上级功能菜单'); setNodeObj(''); // setNodeType(3); setAddType(3); setAddTwoVisible(true); setKeepType('MiniAppMenu'); }; // 新增提交的回调 const submitCallback = (prop, item, e) => { setSubmitLoading(true); let obj = { ...prop }; // if (addType === 3 || addType === 4) { // obj.relatedRoleList = String(roleList) || ''; // } console.log(obj); console.log(e); const parentID = item.menuID ? item.menuID : '-1'; addMenu({ _dc: Date.now(), parentID, subSystemValue: clientName || 'miniapp', _version: 9999, ...obj, }) .then(res => { setSubmitLoading(false); if (res.code === 0) { setAddVisible(false); setAddTwoVisible(false); setFlag(flag + 1); console.log(prop); console.log(item); updateTrees(res.data); notification.success({ message: '提示', description: '新增成功', duration: 3, }); } else { notification.error({ message: '提示', description: res.msg || '新增失败', duration: 10, }); } }) .catch(err => { setSubmitLoading(false); console.error(err); }); }; // 编辑的回调 const editSubmitCallback = prop => { setLoading(true); console.log(prop); let obj = { ...prop }; // if (nodeType === 3 || nodeType === 4) { // obj.relatedRoleList = String(roleList) || ''; // } editMenu({ _dc: Date.now(), menuID, subSystemValue: clientName || 'miniapp', _version: 9999, ...obj, }) .then(res => { setLoading(false); if (res.code === 0) { setFlag(flag + 1); updateTrees(); notification.success({ message: '提示', duration: 3, description: '编辑成功', }); } else { notification.error({ message: '提示', duration: 3, description: res.msg || '编辑失败', }); } console.log(res, 'resres'); }) .catch(err => { console.error(err); setLoading(false); }); }; // 复选框选中值 const valueCallback = val => { console.log(val, 'val'); // relatedRoleCode let arr = [...val]; let arr2 = []; arr.map(item => { if (item.related) { arr2.push(item.relatedRoleCode); } }); setRoleList(arr2); }; // 遍历数组,找出key值与之相同的对象(节点),执行callback函数 // 若key值不匹配且含有children,则循环遍历 // 否则不执行任何操作 const loop = (data, key, parentID, callback) => { for (let i = 0; i < data.length; i++) { if (data[i].menuID === key) { return callback(data[i], i, data, parentID); } if (data[i].children) { loop(data[i].children, key, data[i].menuID, callback); } } }; // 树的拖动 const handleDrop = infos => { const dropKey = infos.node.key; const dragKey = infos.dragNode.key; const dropPos = infos.node.pos.split('-'); const dropPosition = infos.dropPosition - Number(dropPos[dropPos.length - 1]); const data = JSON.parse(JSON.stringify(treeData)); // 找到拖拽的元素 let dragObj; let dropObj; let id; let dragList; // 保存拖拽到的节点信息 loop(data, dropKey, -1, item => { dropObj = item; }); // 保存节点信息并删除节点 loop(data, dragKey, -1, (item, index, arr) => { arr.splice(index, 1); dragObj = item; }); console.log(dragObj.menuType, 'dragObj'); console.log(dropObj.menuType, 'dropObj'); // console.log(dropPos, 'dropPosition'); // 拖拽到的位置不能在菜单下面 if ( (dropObj.menuType === 'MiniAppMenuThree' || dropObj.menuType === 'MiniAppMenu') && !infos.dropToGap ) { return; } // 将节点插入到正确位置 if (!infos.dropToGap) { console.log('111111'); // 菜单不能拖拽到跟一级菜单组下 if ( dropObj.menuType === 'MiniAppMenuGroup' && (dragObj.menuType === 'MiniAppMenuThree' || dragObj.menuType === 'MiniAppMenu') ) { return; } // 一级菜单组只能在一级菜单组平级拖拽 if ( dragObj.menuType === 'MiniAppMenuGroup' && (dropObj.menuType === 'MiniAppMenuGroup' || dropObj.menuType === 'MiniAppMenuGroupTwo') ) { return; } // 二级菜单组只能在一级的菜单组下面 if ( dragObj.menuType === 'MiniAppMenuGroupTwo' && (dropObj.menuType === 'MiniAppMenuGroupTwo' || dropObj.menuType === 'MiniAppMenu') ) { return; } dropObj.children = dropObj.children || []; // 在哪里插入,示例添加到头部,可以是随意位置 dropObj.children.unshift(dragObj); id = dropObj.menuID; dragList = dropObj.children.map(val => val.menuID); } else { // 菜单不能拖拽到跟一级菜单组下 if ( dropObj.menuType === 'MiniAppMenuGroupTwo' && (dragObj.menuType === 'MiniAppMenuThree' || dragObj.menuType === 'MiniAppMenu') ) { return; } // 一级菜单组只能在一级菜单组平级拖拽 if ( dragObj.menuType === 'MiniAppMenuGroup' && (dropObj.menuType === 'MiniAppMenuGroupTwo' || dropObj.menuType === 'MiniAppMenuThree') ) { return; } // 二级菜单组只能在一级的菜单组下面 if ( dragObj.menuType === 'MiniAppMenuGroupTwo' && (dropObj.menuType === 'MiniAppMenuGroup' || dropObj.menuType === 'MiniAppMenuThree' || dropObj.menuType === 'MiniAppMenu') ) { return; } let ar; let i; loop(data, dropKey, -1, (item, index, arr, parentID) => { ar = arr; i = index; id = parentID; }); if (dropPosition === -1) { ar.splice(i, 0, dragObj); } else { ar.splice(i + 1, 0, dragObj); } dragList = ar.map(val => val.menuID); } console.log(dragList, 'dragList'); // 如果拖拽到菜单下不做任何处理 if (!dragList) { return; } dragMenu({ menuID: dragKey, newParentID: id.toString() || '-1', menuList: String(dragList) || '', _version: 9999, _dc: Date.now(), }).then(res => { if (res.code === 0) { updateTrees(); } else { notification.error({ message: '提示', duration: 3, description: res.msg || '操作失败', }); } }); }; const findNum = (array, id, callback, parentId = '') => { let ptId = parentId; let arrFlag = true; array.map((item, index) => { if (item.menuID === id) { callback(array, id, parentId, index); arrFlag = false; return; } if (arrFlag && item.children && item.children.length > 0) { ptId = item.menuID; findNum(item.children, id, callback, ptId); } }); }; const getArrList = (arr, id, parentId, index) => { console.log(arr, 'arr', id, 'id', parentId, 'parentId', index, 'index'); return { arr, id, parentId, index }; }; return ( <Spin spinning={loading} tip="loading..."> <div className={classnames({ [styles.box]: true, })} > <div className={classnames({ [styles.left]: true, })} > <div className={classnames({ [styles.leftTop]: true, })} > 菜单列表 <div className={classnames({ [styles.tRight]: true, })} > <Tooltip title="导入导出菜单组" className={styles.fs}> <DeliveredProcedureOutlined style={{ color: '#178BF7', marginRight: '10px' }} onClick={() => { setNodeObj(''); setImportOrExportVisible(true); }} /> </Tooltip> <Tooltip title="新增最上级菜单组"> <FolderAddTwoTone className={`${styles.icon1} ${styles.icon}`} onClick={() => rootAddGroup()} /> </Tooltip> <Tooltip title="新增最上级功能菜单"> <FileAddTwoTone onClick={() => rootAdd()} className={`${styles.icon2} ${styles.icon}`} /> </Tooltip> </div> </div> {treeData.length > 0 ? ( <div style={{ height: 'calc(100% - 35px)', overflowY: 'scroll' }}> <Tree showIcon onSelect={handleSelect} treeData={treeData.map(item => mapTree(item))} blockNode draggable autoExpandParent onDrop={handleDrop} onExpand={handleExpand} selectedKeys={[menuID]} expandedKeys={expendKey} /> </div> ) : ( <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无菜单" /> )} </div> <Modal visible={addVisible} title={modalTitle} bodyStyle={{ width: '100%', minHeight: '100px' }} style={{ top: 80 }} width="600px" destroyOnClose footer={null} cancelText="取消" okText="确认" maskClosable={false} onCancel={() => setAddVisible(false)} > <AddForm nodeType={nodeType} nodeObj={nodeObj} keepType={keepType} addType={addType} submitCallback={submitCallback} submitLoading={submitLoading} valueCallback={valueCallback} addList={addList} /> </Modal> <Modal visible={addTwoVisible} title={modalTitle} bodyStyle={{ width: '100%', minHeight: '100px', maxHeight: '700px', overflowY: 'scroll', }} style={{ top: 80 }} width="600px" destroyOnClose footer={null} cancelText="取消" okText="确认" maskClosable={false} onCancel={() => setAddTwoVisible(false)} > <AddForm submitLoading={submitLoading} nodeType={nodeType} nodeObj={nodeObj} keepType={keepType} addType={addType} addList={addList} submitCallback={submitCallback} valueCallback={valueCallback} /> </Modal> <Modal visible={delVisible} title="请确认" bodyStyle={{ width: '100%', minHeight: '100px' }} style={{ top: 80 }} width="400px" destroyOnClose cancelText="取消" okText="确认" maskClosable={false} onCancel={() => setDelVisible(false)} onOk={() => delMenu()} confirmLoading={modalLoading} > {nodeObj.children && nodeObj.children.length > 0 ? ( <span> 是否删除<span style={{ color: 'red' }}>{modalTitle}</span>及其子菜单? </span> ) : ( <span> 是否删除<span style={{ color: 'red' }}>{modalTitle}</span>? </span> )} </Modal> <div className={classnames({ [styles.middle]: true, })} > 菜单配置 {nodeType ? ( <div style={{ marginTop: '10px' }}> <EditForm nodeType={nodeType} info={info} inf={inf} checkList={checkList} submitCallback={editSubmitCallback} valueCallback={valueCallback} /> {/* <div className={classnames({ [styles.rightBox]: true, })} > 关联角色 <CheckList info={info} nodeType={nodeType} valueCallback={valueCallback} /> </div> */} </div> ) : ( <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="当前未选中菜单" /> )} </div> <ImportOrExport visible={importOrExportVisible} onCancel={() => { setImportOrExportVisible(false); updateTrees(); }} subSystemValue={clientName} nodeObj={nodeObj} /> </div> </Spin> ); }; export default MiniMenu;