Commit c557eab0 authored by 张烨's avatar 张烨

feat: web菜单展示

parent d2923656
......@@ -21,7 +21,7 @@ export default props => {
closable
onClose={onClose}
visible={visible}
maskClosable={false}
maskClosable
>
<WebConfigForm
hasIntegerate={hasIntegerate}
......
......@@ -10,10 +10,12 @@ import {
} from '@/services/webConfig/api';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import Modal from 'antd/lib/modal/Modal';
import ProCard from '@ant-design/pro-card';
import styles from './index.less';
import SiteConfig from './components/siteConfigDrawer';
import { appConnector } from '@/containers/App/store';
import { defaultWebConfigObj, webMode } from './utils';
import MenuConfig from './menuconfig/MenuConfig';
const { TabPane } = Tabs;
const WebConfigPage = props => {
......@@ -196,6 +198,10 @@ const WebConfigPage = props => {
});
};
const handleUpdateOnMenuChange = () => {
updateModuleTree(userMode || 'super');
};
const renderTabPane = tabPaneItem => (
<TabPane key={tabPaneItem.id} tab={tabPaneItem.text}>
<>
......@@ -209,6 +215,12 @@ const WebConfigPage = props => {
>
查看/编辑网站配置
</span>
<ProCard>
<MenuConfig
menu={curWeb?.children.find(w => w.menuType === 'Web4MenuRoot')}
onUpdate={handleUpdateOnMenuChange}
/>
</ProCard>
</>
</TabPane>
);
......
import React, { useState } from 'react';
import { Form, Input, Button, Row, Col } from 'antd';
import styles from './addForm.less';
import PicturesWall from '@/components/Upload/index';
const { Item } = Form;
const AddForm = props => {
const { submitCallback, nodeType, nodeObj, addType, submitLoading } = props;
const [form] = Form.useForm();
const [otherForm] = Form.useForm();
console.log(nodeObj, 'nodeObj');
const layout = {
layout: 'horizontal',
labelCol: { span: 4, offset: 1 },
wrapperCol: { span: 16 },
};
const submit = () => {
if (addType === 1) {
let obj = form.getFieldsValue();
submitCallback(obj, nodeObj);
}
if (addType === 2) {
// 分组
let obj = otherForm.getFieldsValue();
submitCallback(obj, nodeObj);
}
};
const finish = () => {
submit();
};
return (
<div>
{addType === 1 && (
<Form
form={form}
name="groupform"
{...layout}
className={styles.formStyle}
onFinish={finish}
>
<Item
label="菜单名称"
name="menuName"
rules={[
{
required: true,
message: '请输入分组名称',
},
]}
>
<Input />
</Item>
<Item label="菜单别名" name="shortName">
<Input />
</Item>
<Item
label="菜单图标"
name="imageUrl"
rules={[
{
required: true,
message: '请选择在线图标',
},
]}
>
<PicturesWall />
</Item>
<Item label="功能参数" name="funParam">
<Input />
</Item>
<Item wrapperCol={{ offset: 5 }} style={{ marginTop: '40px' }}>
<Button type="primary" htmlType="submit" loading={submitLoading}>
提交
</Button>
</Item>
</Form>
)}
{addType === 2 && (
<Form
form={otherForm}
name="menuform"
{...layout}
onFinish={finish}
className={styles.formStyle}
>
<Item
label="菜单名称"
name="menuName"
rules={[
{
required: true,
message: '请输入菜单名称',
},
]}
>
<Input />
</Item>
<Item label="菜单别名" name="shortName">
<Input />
</Item>
<Item
label="菜单图标"
name="imageUrl"
rules={[
{
required: true,
message: '请选择菜单图标',
},
]}
>
<PicturesWall />
</Item>
<Item wrapperCol={{ offset: 5 }} style={{ marginTop: '40px' }}>
<Button type="primary" htmlType="submit" loading={submitLoading}>
提交
</Button>
</Item>
</Form>
)}
</div>
);
};
export default AddForm;
import React, { useState, useEffect } from 'react';
import ProCard from '@ant-design/pro-card';
import WebMenu from './webMenu';
const MenuConfig = props => {
const { menu } = props;
return (
<>
{menu && (
<div split="vertical">
<WebMenu {...props} />
</div>
)}
</>
);
};
export default MenuConfig;
.formStyle{
margin-bottom: 40px;
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import { Checkbox, Empty } from 'antd';
import styles from './checkBox.less';
const CheckList = props => {
const { info, valueCallback, nodeType } = props;
console.log(info, 'info');
const [list, setList] = useState([]);
const [checkList, setCheckList] = useState([]);
const [flag, setFlag] = useState(false);
const a = 'a';
useEffect(() => {
if (info.pageUrl) {
let arr = [...info.relatedRoleList];
let arr2 = [];
arr.map(item => {
if (item.related) {
arr2.push(item.relatedRoleCode);
}
});
console.log(arr2, 'arr2');
setCheckList(arr2);
setList(arr);
setFlag(true);
}
return () => {
setFlag(false);
setCheckList([]);
};
}, [info]);
const handleSelect = (e, val) => {
console.log(e.target, 'e', val);
let arr = [];
list.forEach(item => {
if (item.relatedRoleCode === val) {
item.related = e.target.checked;
}
});
setList([...list]);
valueCallback(list);
};
return (
<div>
{nodeType === 3 || nodeType === 4 ? (
<div className={styles.box}>
{/* <Checkbox>全选/反选</Checkbox> */}
{list &&
list.length > 0 &&
list.map(item => (
<div key={item.relatedRoleCode} className={styles.check}>
<Checkbox
checked={item.related}
onChange={e => {
handleSelect(e, item.relatedRoleCode);
}}
>
{item.relatedRoleName}
</Checkbox>
</div>
))}
</div>
) : (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
)}
</div>
);
};
export default CheckList;
.box{
display: flex;
padding: 10px;
margin-top: 10px;
flex-wrap: wrap;
}
.check{
flex-shrink: 0;
flex-grow: 0;
min-width: 150px;
padding-bottom: 10px;
}
\ No newline at end of file
import React, { useState, useEffect } from 'react';
import { Form, Input, Button, Row, Col } from 'antd';
import styles from './addForm.less';
import PicturesWall from '@/components/Upload/index';
const { Item } = Form;
const EditForm = props => {
const { submitCallback, nodeType, info } = props;
const [form] = Form.useForm();
const [otherForm] = Form.useForm();
console.log(info);
const layout = {
layout: 'horizontal',
labelCol: { span: 4, offset: 1 },
wrapperCol: { span: 16 },
};
// 回显表单
useEffect(() => {
form.resetFields();
otherForm.resetFields();
}, [info]);
useEffect(() => {
const targetForm = nodeType === 1 ? form : otherForm;
let arr = Object.keys(targetForm.getFieldsValue());
let obj = {};
arr.map(i => {
obj[i] = info[i];
});
targetForm.setFieldsValue({ ...obj, shortName: info.menuShortName });
}, [info]);
const submit = () => {
const targetForm = nodeType === 1 ? form : otherForm;
let obj = targetForm.getFieldsValue();
submitCallback(obj);
};
const onFinish = () => {
submit();
};
return (
<div>
{nodeType === 1 && (
<Form
form={form}
name="editGroup"
{...layout}
className={styles.formStyle}
onFinish={onFinish}
>
<Item
label="菜单名称"
name="menuName"
rules={[
{
required: true,
message: '请输入菜单名称',
},
]}
>
<Input />
</Item>
<Item label="菜单别名" name="shortName">
<Input />
</Item>
<Item
label="菜单图标"
name="imageUrl"
rules={[
{
required: true,
message: '请选择在线图标',
},
]}
>
<PicturesWall />
</Item>
<Item label="功能路径" name="funParam">
<Input />
</Item>
<Item wrapperCol={{ offset: 5 }} style={{ marginTop: '40px' }}>
<Button type="primary" htmlType="submit">
提交
</Button>
</Item>
</Form>
)}
{nodeType === 2 && (
<Form
form={otherForm}
name="editMenu"
{...layout}
onFinish={onFinish}
className={styles.formStyle}
>
<Item
label="菜单名称"
name="menuName"
rules={[
{
required: true,
message: '请输入菜单名称',
},
]}
>
<Input />
</Item>
<Item label="菜单别名" name="shortName">
<Input />
</Item>
<Item
label="菜单图标"
name="imageUrl"
rules={[
{
required: true,
message: '请选择菜单图标',
},
]}
>
<PicturesWall />
</Item>
<Item wrapperCol={{ offset: 5 }} style={{ marginTop: '40px' }}>
<Button type="primary" htmlType="submit">
提交
</Button>
</Item>
</Form>
)}
</div>
);
};
export default EditForm;
.box{
min-height: calc( 100vh - 172px);
display: flex;
}
.left{
min-width: 300px;
width: 350px;
min-height: 100%;
border: 1px solid #eee;
padding: 10px;
.ant-tree-node-content-wrapper{
display: flex;
align-items: center;
.ant-tree-iconEle{
display: flex;
align-items: center;
}
.ant-tree-title{
width: 100%;
}
}
.leftTop{
display: flex;
align-items: center;
margin-bottom: 10px;
justify-content: space-between;
.tRight{
display: flex;
align-items: center;
.icon1{
font-size: 20px;
margin-right: 10px;
}
.icon2{
font-size: 18px;
margin-right: 5px;
}
.icon:hover{
cursor: pointer;
}
}
}
}
.middle{
min-width: 500px;
width: 500px;
min-height: 100%;
border: 1px solid #eee;
padding: 10px;
margin: 0 10px;
}
.title{
display: flex;
align-items: center;
width: 100%;
}
.tip{
display: none;
}
.title:hover{
.tip{
display: flex;
align-items: center;
justify-content: flex-end;
width: 100%;
}
}
.fs{
font-size: 18px;
margin-left: 10px;
}
.rightBox{
min-width: 200px;
// width: 600px;
width: 100%;
min-height: 100%;
border: 1px solid #eee;
padding: 10px;
}
\ No newline at end of file
import React, { useEffect, useState } from 'react';
import {
message,
notification,
Popconfirm,
Form,
Tree,
Tooltip,
Modal,
Input,
Button,
Spin,
} from 'antd';
import {
FileAddTwoTone,
FolderAddTwoTone,
FolderFilled,
FileOutlined,
DeleteTwoTone,
} from '@ant-design/icons';
import PicturesWall from '@/components/Upload/index';
import classnames from 'classnames';
import {
miniAppSiteTree,
getMiniAppModuleTree,
addMenu,
getMenuInfo,
deleteMenu,
editMenu,
} from '@/services/mobileConfig/api';
import styles from './miniMenu.less';
import AddForm from './AddForm';
import EditForm from './editForm';
import CheckList from './checkBox';
const MiniMenu = props => {
const { menu } = props;
const [flag, setFlag] = useState(1); // 刷新标志
const [treeFlage, setTreeFlag] = useState(true);
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 [curMenuType, setCurMenuType] = useState('');
// 获取菜单信息
useEffect(() => {
getInfo();
}, [menuID]);
// 处理数据
const mapTree = val => {
const obj = { ...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}>
<FolderAddTwoTone onClick={() => addMenuGroupTip(obj)} />
</Tooltip>
)}
{obj.menuType === 'MiniAppMenuGroupTwo' && (
<Tooltip title="新增功能菜单" className={styles.fs}>
<FileAddTwoTone onClick={() => addMenuTip(obj)} />
</Tooltip>
)}
<Tooltip title="删除菜单" className={styles.fs}>
<DeleteTwoTone onClick={() => deleteMenuTip(obj)} />
</Tooltip>
</div>
</div>
),
key: obj.menuID,
icon:
obj.menuType === 'Web4MenuGroup' ? <FolderFilled /> : <FileOutlined />,
menuType: obj.menuType,
children: hasChild ? obj.children.map(i => mapTree(i)) : [],
};
};
// 树的点击事件
const handleSelect = (prop, treeNode) => {
console.log(prop, treeNode);
if (treeNode) {
const {
node,
node: { menuType },
} = treeNode;
console.log(menuType);
setCurMenuType(menuType);
switch (menuType) {
case 'Web4MenuGroup':
setNodeType(2);
setAddType(2);
break;
default:
setNodeType(1);
setAddType(1);
break;
}
}
if (prop[0]) {
setMenuID(prop[0]);
setSaveID(prop[0]);
} else {
setMenuID(saveID);
}
};
const getInfo = id => {
if (!menuID) {
return;
}
setLoading(true);
getMenuInfo({
menuID,
_version: 9999,
_dc: Date.now(),
})
.then(res => {
setLoading(false);
if (res.success) {
setInfo({ ...res });
} else {
notification.error({
message: '提示',
duration: 10,
description: res.message || '获取失败',
});
}
console.log(res, 'resss');
})
.catch(err => {
setLoading(false);
console.error(err);
});
};
// 删除的回调
const deleteMenuTip = val => {
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.success) {
setDelVisible(false);
setFlag(flag + 1);
setNodeType('');
setNodeObj('');
notification.success({
message: '提示',
duration: 3,
description: '删除成功',
});
} else {
notification.error({
message: '提示',
duration: 10,
description: res.message || '删除失败',
});
}
})
.catch(err => {
setModalLoading(false);
console.log(err);
});
};
// 新增菜单组
const addMenuGroupTip = val => {
console.log(val, 'addgroup');
setModalTitle(val.text);
setNodeObj(val);
setAddVisible(true);
};
const rootAddGroup = () => {
setNodeObj('');
setNodeType(1);
setModalTitle('最上级列表');
setAddType(1);
setAddVisible(true);
};
// 新增功能菜单
const addMenuTip = val => {
console.log(val, 'add');
setNodeObj(val);
setModalTitle(val.text);
setAddTwoVisible(true);
};
const rootAdd = () => {
setModalTitle('最上级列表');
setNodeObj('');
setNodeType(3);
setAddType(3);
setAddTwoVisible(true);
};
// 新增提交的回调
const submitCallback = (prop, item) => {
setSubmitLoading(true);
console.log(prop, item);
let obj = { ...prop };
const parentID = item.menuID ? item.menuID : -1;
addMenu({
_dc: Date.now(),
parentID,
subSystemValue: 'miniapp',
_version: 9999,
...obj,
})
.then(res => {
setSubmitLoading(false);
if (res.success) {
setAddVisible(false);
setAddTwoVisible(false);
setFlag(flag + 1);
notification.success({
message: '提示',
description: '新增成功',
duration: 3,
});
} else {
notification.error({
message: '提示',
description: res.message || '新增失败',
duration: 10,
});
}
console.log(res, 'resadd');
})
.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: 'miniapp',
_version: 9999,
...obj,
})
.then(res => {
setLoading(false);
if (res.success) {
setFlag(flag + 1);
notification.success({
message: '提示',
duration: 3,
description: '编辑成功',
});
} else {
notification.error({
message: '提示',
duration: 3,
description: res.message || '编辑失败',
});
}
console.log(res, 'resres');
})
.catch(err => {
console.error(err);
setLoading(false);
});
};
const valueCallback = val => {
let arr = [...val];
let arr2 = [];
arr.map(item => {
if (item.related) {
arr2.push(item.relatedRoleCode);
}
});
setRoleList(arr2);
};
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="新增菜单组">
<FolderAddTwoTone
className={`${styles.icon1} ${styles.icon}`}
onClick={() => rootAddGroup()}
/>
</Tooltip>
<Tooltip title="新增功能菜单">
<FileAddTwoTone
onClick={() => rootAdd()}
className={`${styles.icon2} ${styles.icon}`}
/>
</Tooltip>
</div>
</div>
{menu.children.length > 0 && (
<Tree
showIcon
onSelect={handleSelect}
treeData={menu.children.map(item => mapTree(item))}
blockNode
autoExpandParent
selectedKeys={[menuID]}
expandedKeys={[menuID]}
/>
)}
</div>
<Modal
visible={addVisible}
title={`在${modalTitle}下新增菜单组`}
bodyStyle={{ width: '100%', minHeight: '100px' }}
style={{ top: 80 }}
width="600px"
destroyOnClose
footer={null}
cancelText="取消"
okText="确认"
onCancel={() => setAddVisible(false)}
onConfirm={() => {
submitCallback();
}}
>
<AddForm
nodeType={nodeType}
nodeObj={nodeObj}
addType={addType}
submitCallback={submitCallback}
submitLoading={submitLoading}
/>
</Modal>
<Modal
visible={addTwoVisible}
title={`在${modalTitle}下新增功能菜单`}
bodyStyle={{ width: '100%', minHeight: '100px' }}
style={{ top: 80 }}
width="600px"
destroyOnClose
footer={null}
cancelText="取消"
okText="确认"
onCancel={() => setAddTwoVisible(false)}
>
<AddForm
submitLoading={submitLoading}
nodeType={nodeType}
nodeObj={nodeObj}
addType={addType}
submitCallback={submitCallback}
/>
</Modal>
<Modal
visible={delVisible}
title="请确认"
bodyStyle={{ width: '100%', minHeight: '100px' }}
style={{ top: 80 }}
width="400px"
destroyOnClose
cancelText="取消"
okText="确认"
onCancel={() => setDelVisible(false)}
onOk={() => delMenu()}
confirmLoading={modalLoading}
>
是否删除<span style={{ color: 'red' }}>{modalTitle}</span>?
</Modal>
<div
className={classnames({
[styles.middle]: true,
})}
>
{nodeType && (
<EditForm
nodeType={nodeType}
info={info}
submitCallback={editSubmitCallback}
/>
)}
</div>
<div
className={classnames({
[styles.rightBox]: true,
})}
>
关联角色
<CheckList
info={info}
nodeType={nodeType}
valueCallback={valueCallback}
/>
</div>
</div>
</Spin>
);
};
export default MiniMenu;
......@@ -56,6 +56,7 @@ export const defaultWebConfigObj = {
menu: 'banner-left',
mdi: 'MDI',
hideMap: true,
loginTemplate: 'Default.html',
};
export const getDefaultGetWebconfig = ({
......
......@@ -45,18 +45,25 @@ export const getWebconfig = title =>
* @param {*} config 网站配置
* @param {*} isAdd true:添加 false:编辑
*/
export const postEditWebConfig = (config, isAdd = false) =>
export const postEditWebConfig = (config, isAdd = false) => {
const obj = { ...config };
Object.keys(obj).forEach(k => {
if (typeof obj[k] === 'string') {
obj[k] = obj[k].trim();
}
});
post(
`${CITY_SERVICE}/OMS.svc/${
isAdd ? 'W4_AddWebsite' : 'W4_EditWebsite'
}?_version=9999`,
qs.stringify({ config: JSON.stringify(config) }),
qs.stringify({ config: JSON.stringify(obj) }),
{
headers: {
'content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
},
},
);
};
export const postAddWebSite = config => postEditWebConfig(config, true);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment