在上一篇文章中我们创建了项目。现在我们在这个项目中完成右键的菜单实现
umi脚手架搭建项目
在项目创建好之后 运行 会自动打开welcome 界面,以及是基于antd的界面了。
sumi项目中 src/pages 下的内容会自动生成路由。 (有很多是这么说的)
但是实际上你创建页面之后在项目 修改地址是会报 404 错误的
要想修改路由需要在 config / config.js 文件中修改哦
我修改了路由中welcome 改成了home 修改了component 的路径 此时 默认路由就是 Home 中的内容了
routes: [
{
path: '/',
component: '../layouts/BasicLayout',
Routes: ['src/pages/Authorized'],
authority: ['admin', 'user'],
routes: [
{
path: '/',
name: 'home',
icon: 'smile',
component: './Home/home',
},
{
component: './404',
}
],
需求是从后台读取列表数据,所以我们需要先用mock 模拟
services/apis.js
import request from '@/utils/request';
export async function getList() {
return request('/api/rightlist');
}
mock/rightlist.js
const getList = (req,res) => {
res.json([
{
id: '1',
icon: 'copy',
title: '添加到自定义',
},
{
id: '2',
icon: 'scissor',
title: '剪切',
},
{
id: '3',
icon: 'snippets',
title: '粘贴',
},
{
id: '4',
icon: 'delete',
title: '删除',
},
{
id: '5',
icon: 'lock',
title: '锁定',
},
{
id: '6',
icon: 'layout',
title: '组合',
children: [
{
id: '61',
icon: 'build',
title: '创建'
},
{
id: '62',
icon: 'appstore',
title: '卡片'
},
{
id: '63',
icon: 'database',
title: '列表'
},
]
},
{
id: '7',
icon: 'vertical-align-top',
title: '顺序',
children: [
{
id: '71',
icon: 'arrow-down',
title: '后移'
},
{
id: '72',
icon: 'arrow-up',
title: '前移'
},
{
id: '73',
icon: 'vertical-align-bottom',
title: '置底'
},
{
id: '74',
icon: 'vertical-align-top',
title: '置顶'
}
]
},
])
}
export default {
'GET /api/rightlist': getList,
};
在models 中应该有请求以及操作
models/rightlist.js
import { getList } from '@/services/api'
export default {
namespace: 'rightlist',
state: {
list: []
},
effects: {
*fetchList({ callback }, { call, put }) {
const response = yield call(getList);
if (!response.error) {
yield put({
type: 'saveList',
payload: response,
});
}
if (callback && response) {
callback(response);
}
},
},
reducers: {
saveList(state, action) {
return {
...state,
list: action.payload,
}
},
},
}
修改路径后我们创建组件 components/rightList 直接上代码了
components/rightList/index.js
import React, { Component } from 'react';
import { connect } from 'dva';
import { Menu, Dropdown, Icon } from 'antd';
import styles from './index.less';
const { SubMenu } = Menu;
@connect(({rightlist, global}) => ({
rightlist, global
}))
class RightMenu extends Component {
state = {
content: '',
visible: false,
list: []
}
componentDidMount() {
const { children } = this.props
this.setState({
content: children
})
this.getList()
document.addEventListener('contextmenu', this.handleContextMenu)
window.addEventListener('resize', this.resize);
document.addEventListener('click', this.handleClick);
document.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
document.removeEventListener('contextmenu', this.handleContextMenu)
window.removeEventListener('resize', this.resize);
document.removeEventListener('click', this.handleClick);
document.removeEventListener('scroll', this.handleScroll);
}
getList() {
const { dispatch } = this.props;
dispatch({
type: 'rightlist/fetchList',
callback: (res) => {
this.setState({
list: res
})
}
});
}
handleClick = (event) => {
const { visible } = this.state;
const wasOutside = !(event.target.contains === this.root);
if (wasOutside && visible) this.setState({ visible: false, });
};
handleScroll = () => {
const { visible } = this.state;
if (visible) this.setState({ visible: false, });
}
runderMenu = () => {
const { visible, list } = this.state
console.log(list)
const menu = list.map(item => {
if (item.children) {
return ( {item.title}} key={item.id}>
{item.children.map(i => {
return ( {i.title} )
})}
)
} else {
return (
{item.title}
)
}
})
console.log(menu)
return (
visible && (
{ this.root = ref }} className={styles.contextMenuWrap}>
)
)
}
handleContextMenu = (event) => {
event.preventDefault()
this.setState({ visible: true })
// clientX/Y 获取到的是触发点相对于浏览器可视区域左上角距离
const clickX = event.clientX
const clickY = event.clientY
// window.innerWidth/innerHeight 获取的是当前浏览器窗口的视口宽度/高度
const screenW = window.innerWidth
const screenH = window.innerHeight
// 获取自定义菜单的宽度/高度
const rootW = this.root.offsetWidth
const rootH = this.root.offsetHeight
// right为true,说明鼠标点击的位置到浏览器的右边界的宽度可以放下菜单。否则,菜单放到左边。
// bottom为true,说明鼠标点击位置到浏览器的下边界的高度可以放下菜单。否则,菜单放到上边。
const right = (screenW - clickX) > rootW
const left = !right
const bottom = (screenH - clickY) > rootH
const top = !bottom
if (right) {
this.root.style.left = `${clickX}px`
}
if (left) {
this.root.style.left = `${clickX - rootW}px`
}
if (bottom) {
this.root.style.top = `${clickY}px`
}
if (top) {
this.root.style.top = `${clickY - rootH}px`
}
};
render() {
return (
demo
{this.state.content}
{this.runderMenu()}
);
}
}
export default RightMenu;
components/rightList/index.less
.rwrap{
width: 100%;
height: 100%;
background: #5b5b5b;
}
.contextMenuWrap{
z-index: 100;
position: fixed;
// background: linear-gradient(to right, #ffffff, rgba(190, 190, 190, 0.65));
box-shadow: 0px 2px 5px #383737;
border-radius: 4px;
padding-top: 5px;
.contextMenuOption{
// padding: 0px 50px 2px 15px;
min-width: 160px;
cursor: default;
font-size: 14px;
&:hover {
background: #9c9c9c;
color: white;
}
&:active {
color: #b5b7be;
background: linear-gradient(to top, #555, #444);
}
}
.contextMenuSeparator{
width: 100%;
height: 1px;
background: rgba(0, 0, 0, 0.65);
margin: 2px 0;
}
}
.hide{
display: none;
}
.contextMenuOption{
// padding: 0px 50px 2px 15px;
min-width: 160px;
cursor: default;
font-size: 14px;
&:hover {
background: #9c9c9c;
color: white;
}
&:active {
color: #b5b7be;
background: linear-gradient(to top, #555, #444);
}
}
这样就可以实现 右键菜单的功能