update 菜单搜索
This commit is contained in:
@@ -1,11 +1,13 @@
|
|||||||
import { combineReducers } from 'redux'
|
import { combineReducers } from 'redux'
|
||||||
import user from './user'
|
import user from './user'
|
||||||
import layout from './layout'
|
import layout from './layout'
|
||||||
|
import nav from './nav'
|
||||||
import dictData from './dict-data'
|
import dictData from './dict-data'
|
||||||
|
|
||||||
const combine = combineReducers({
|
const combine = combineReducers({
|
||||||
user,
|
user,
|
||||||
layout,
|
layout,
|
||||||
|
nav,
|
||||||
dictData
|
dictData
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
12
web-react/src/store/reducer/nav.js
Normal file
12
web-react/src/store/reducer/nav.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const nav = (state = { nav: [] }, action) => {
|
||||||
|
// 写入各种action对应的操作
|
||||||
|
switch (action.type) {
|
||||||
|
case 'SET_ANV':
|
||||||
|
const _state = { ...state, nav: action.nav }
|
||||||
|
return _state
|
||||||
|
default:
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default nav
|
||||||
0
web-react/src/store/reducer/pane.js
Normal file
0
web-react/src/store/reducer/pane.js
Normal file
@@ -3,6 +3,7 @@ import { Layout, Badge } from 'antd'
|
|||||||
import { AntIcon, Container } from 'components'
|
import { AntIcon, Container } from 'components'
|
||||||
import Logo from '../logo'
|
import Logo from '../logo'
|
||||||
import User from './user'
|
import User from './user'
|
||||||
|
import Search from './search'
|
||||||
import store from 'store'
|
import store from 'store'
|
||||||
|
|
||||||
const { getState, subscribe, dispatch } = store
|
const { getState, subscribe, dispatch } = store
|
||||||
@@ -41,7 +42,7 @@ export default class index extends Component {
|
|||||||
<AntIcon type="menu" />
|
<AntIcon type="menu" />
|
||||||
</span>
|
</span>
|
||||||
<Logo />
|
<Logo />
|
||||||
{/* search */}
|
<Search />
|
||||||
</div>
|
</div>
|
||||||
<div className="header-actions">
|
<div className="header-actions">
|
||||||
<span className="header-action" onClick={() => window.realodContentWindow()}>
|
<span className="header-action" onClick={() => window.realodContentWindow()}>
|
||||||
|
|||||||
144
web-react/src/views/main/_layout/header/search.jsx
Normal file
144
web-react/src/views/main/_layout/header/search.jsx
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import React, { Component } from 'react'
|
||||||
|
import { AutoComplete, Input } from 'antd'
|
||||||
|
import { AntIcon } from 'components'
|
||||||
|
import store from 'store'
|
||||||
|
import { concat } from 'lodash'
|
||||||
|
|
||||||
|
const { getState, subscribe } = store
|
||||||
|
|
||||||
|
const unZip = (menus) => {
|
||||||
|
const result = []
|
||||||
|
menus.forEach(item => {
|
||||||
|
if (item.children) {
|
||||||
|
result.push({
|
||||||
|
parent: item.meta.title,
|
||||||
|
children: item.children
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
result.push({
|
||||||
|
parent: item.meta.title,
|
||||||
|
children: [item]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class search extends Component {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
options: [],
|
||||||
|
|
||||||
|
searchValue: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
nav = getState('nav').nav
|
||||||
|
|
||||||
|
autoComplete = React.createRef()
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.unsubscribe = subscribe('nav', state => {
|
||||||
|
this.getOptions(state.nav)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getOptions(this.nav)
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.unsubscribe()
|
||||||
|
}
|
||||||
|
|
||||||
|
onSearch(searchValue, option) {
|
||||||
|
searchValue = searchValue.toLowerCase()
|
||||||
|
return (option.value && option.value.toLowerCase().indexOf(searchValue) > -1)
|
||||||
|
|| (option.component && option.component.toLowerCase().indexOf(searchValue) > -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelect(value, option) {
|
||||||
|
const { id, meta, component } = option.menu
|
||||||
|
|
||||||
|
// 选中时清空输入框内容,并失去焦点
|
||||||
|
this.setState({ searchValue: '' })
|
||||||
|
this.autoComplete.current.blur()
|
||||||
|
|
||||||
|
window.openContentWindow({
|
||||||
|
key: id,
|
||||||
|
title: meta.title,
|
||||||
|
icon: meta.icon,
|
||||||
|
path: component,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getOptions(nav) {
|
||||||
|
|
||||||
|
const menus = unZip(concat.apply(this, nav.map(p => p.menu)))
|
||||||
|
|
||||||
|
const options = menus.map(item => {
|
||||||
|
if (item.parent) {
|
||||||
|
return {
|
||||||
|
label: this.renderTitle(item.parent),
|
||||||
|
options: item.children.map(menu => {
|
||||||
|
return this.renderItem(menu.meta.title, menu)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
options: item.children.map(menu => {
|
||||||
|
return this.renderItem(menu.meta.title, menu)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.setState({ options })
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTitle(title) {
|
||||||
|
return <span>
|
||||||
|
{title}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
renderItem(title, menu) {
|
||||||
|
return {
|
||||||
|
value: title,
|
||||||
|
component: menu.component,
|
||||||
|
menu,
|
||||||
|
label: (
|
||||||
|
<>
|
||||||
|
{title}
|
||||||
|
<div className="text-normal">{menu.component}</div>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const { options, searchValue } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AutoComplete
|
||||||
|
ref={this.autoComplete}
|
||||||
|
value={searchValue}
|
||||||
|
options={options}
|
||||||
|
dropdownMatchSelectWidth={false}
|
||||||
|
dropdownStyle={{ width: '300px' }}
|
||||||
|
dropdownClassName="certain-category-search-dropdown"
|
||||||
|
optionLabelProp="value"
|
||||||
|
filterOption={(searchValue, option) => this.onSearch(searchValue, option)}
|
||||||
|
onSelect={(value, option) => this.onSelect(value, option)}
|
||||||
|
onChange={(value) => this.setState({ searchValue: value })}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
allowClear
|
||||||
|
placeholder="请输入检索关键字"
|
||||||
|
suffix={<AntIcon type="search" />}
|
||||||
|
/>
|
||||||
|
</AutoComplete>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -78,7 +78,7 @@ export default class index extends Component {
|
|||||||
<div className="swiper-container" id="layout--swiper-container">
|
<div className="swiper-container" id="layout--swiper-container">
|
||||||
<div className="swiper-wrapper">
|
<div className="swiper-wrapper">
|
||||||
<div className="swiper-slide">
|
<div className="swiper-slide">
|
||||||
<Menu parent={this} menuStyle={{ height: '100%', borderRight: 0 }} nav={this.props.nav} />
|
<Menu parent={this} menuStyle={{ height: '100%', borderRight: 0 }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="swiper-scrollbar" id="layout--swiper-scrollbar"></div>
|
<div className="swiper-scrollbar" id="layout--swiper-scrollbar"></div>
|
||||||
|
|||||||
@@ -1,9 +1,28 @@
|
|||||||
import React, { Component } from 'react'
|
import React, { Component } from 'react'
|
||||||
import { Menu } from 'antd'
|
import { Menu } from 'antd'
|
||||||
import AntIcon from 'components/ant-icon'
|
import AntIcon from 'components/ant-icon'
|
||||||
|
import store from 'store'
|
||||||
|
|
||||||
|
const { getState, subscribe } = store
|
||||||
|
|
||||||
export default class index extends Component {
|
export default class index extends Component {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
...getState('nav')
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.unsubscribe = subscribe('nav', state => {
|
||||||
|
this.setState(state)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.unsubscribe()
|
||||||
|
}
|
||||||
|
|
||||||
renderMenu = (menu) => {
|
renderMenu = (menu) => {
|
||||||
return menu.map((p) => {
|
return menu.map((p) => {
|
||||||
return p.children ? this.renderSubMenu(p) : this.renderMenuItem(p)
|
return p.children ? this.renderSubMenu(p) : this.renderMenuItem(p)
|
||||||
@@ -56,7 +75,7 @@ export default class index extends Component {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{
|
{
|
||||||
this.props.nav.map((item, i) => {
|
this.state.nav.map((item, i) => {
|
||||||
return (
|
return (
|
||||||
<section key={i}>
|
<section key={i}>
|
||||||
<div className="yo-sider-nav--app">{item.app.name}</div>
|
<div className="yo-sider-nav--app">{item.app.name}</div>
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ const setNav = async (nav) => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const menus = await api
|
const menus = await Promise.all(getNav.map(p => api.sysMenuChange({ application: p.app.code })))
|
||||||
.$queue(getNav.map((p) => api.sysMenuChangeAwait({ application: p.app.code })))
|
|
||||||
menus.forEach((menu, i) => {
|
menus.forEach((menu, i) => {
|
||||||
getNav[i].menu = serializeMenu(menu.data)
|
getNav[i].menu = serializeMenu(menu.data)
|
||||||
})
|
})
|
||||||
@@ -52,7 +51,6 @@ export default class index extends Component {
|
|||||||
|
|
||||||
state = {
|
state = {
|
||||||
loading: true,
|
loading: true,
|
||||||
nav: [],
|
|
||||||
panes: [],
|
panes: [],
|
||||||
actived: '',
|
actived: '',
|
||||||
test: 0
|
test: 0
|
||||||
@@ -73,7 +71,12 @@ export default class index extends Component {
|
|||||||
user: data
|
user: data
|
||||||
})
|
})
|
||||||
|
|
||||||
this.setState({ loading: false, nav }, () => {
|
dispatch({
|
||||||
|
type: 'SET_ANV',
|
||||||
|
nav
|
||||||
|
})
|
||||||
|
|
||||||
|
this.setState({ loading: false }, () => {
|
||||||
this.onOpenContentWindow({
|
this.onOpenContentWindow({
|
||||||
title: '工作台',
|
title: '工作台',
|
||||||
path: '/home',
|
path: '/home',
|
||||||
@@ -233,7 +236,7 @@ export default class index extends Component {
|
|||||||
<Layout>
|
<Layout>
|
||||||
<Header />
|
<Header />
|
||||||
<Layout className="yo-nav-theme--light">
|
<Layout className="yo-nav-theme--light">
|
||||||
<Sider nav={this.state.nav} />
|
<Sider />
|
||||||
<Content parent={this} panes={this.state.panes} actived={this.state.actived} />
|
<Content parent={this} panes={this.state.panes} actived={this.state.actived} />
|
||||||
</Layout>
|
</Layout>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
Reference in New Issue
Block a user