update 响应式处理
This commit is contained in:
@@ -26,7 +26,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
>.ant-pagination {
|
||||
.ant-pagination {
|
||||
margin: @padding-md 0;
|
||||
}
|
||||
.ant-descriptions {
|
||||
@@ -44,6 +44,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
&--scroll {
|
||||
position: relative;
|
||||
|
||||
overflow-x: auto;
|
||||
}
|
||||
.ant-list-items {
|
||||
min-width: 1000px;
|
||||
}
|
||||
.ant-list-item {
|
||||
transition: @animation-duration-slow;
|
||||
transition-property: background, border-bottom-color;
|
||||
@@ -52,4 +60,36 @@
|
||||
background: linear-gradient(90deg, transparent 10%, @background-color-light 70%, transparent);
|
||||
}
|
||||
}
|
||||
&-container {
|
||||
position: relative;
|
||||
&::before,
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 3;
|
||||
|
||||
width: 30px;
|
||||
|
||||
content: '';
|
||||
transition: box-shadow @animation-duration-slow;
|
||||
pointer-events: none;
|
||||
}
|
||||
&::before {
|
||||
left: 0;
|
||||
}
|
||||
&::after {
|
||||
right: 0;
|
||||
}
|
||||
&.yo-list--ping-left {
|
||||
&::before {
|
||||
box-shadow: inset 10px 0 8px -8px fade(@black, 15%);
|
||||
}
|
||||
}
|
||||
&.yo-list--ping-right {
|
||||
&::after {
|
||||
box-shadow: inset -10px 0 8px -8px fade(@black, 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-table {
|
||||
.ant-table-container {
|
||||
&::before,
|
||||
&::after {
|
||||
z-index: 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-table-thead {
|
||||
th.ant-table-column-has-sorters {
|
||||
&:hover {
|
||||
|
||||
@@ -19,6 +19,20 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
&--collapsed {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 4;
|
||||
|
||||
transform: translateX(-100%);
|
||||
&.open {
|
||||
transform: translateX(0);
|
||||
|
||||
box-shadow: 2px 0 8px fade(@black , 20%);
|
||||
}
|
||||
}
|
||||
&--bar {
|
||||
line-height: 20px;
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ export default class QueryList extends Component {
|
||||
state = {
|
||||
loading: false,
|
||||
dataSource: [],
|
||||
|
||||
ping: [],
|
||||
}
|
||||
|
||||
// 查询表单实例
|
||||
@@ -91,6 +93,12 @@ export default class QueryList extends Component {
|
||||
if (this.autoLoad) {
|
||||
this.onLoadData()
|
||||
}
|
||||
|
||||
window.addEventListener('resize', this.onResizeScroll)
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.onResizeScroll)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,9 +116,14 @@ export default class QueryList extends Component {
|
||||
this.query
|
||||
)
|
||||
|
||||
this.setState({
|
||||
dataSource: res.rows || res.data || res.items,
|
||||
})
|
||||
this.setState(
|
||||
{
|
||||
dataSource: res.rows || res.data || res.items,
|
||||
},
|
||||
() => {
|
||||
this.onResizeScroll()
|
||||
}
|
||||
)
|
||||
|
||||
this.pagination.total = res.totalCount
|
||||
|
||||
@@ -174,7 +187,26 @@ export default class QueryList extends Component {
|
||||
this.onLoadData()
|
||||
}
|
||||
|
||||
onResizeScroll = () => {
|
||||
this.onListScrollX({ target: this.refs['scroll-x'] })
|
||||
}
|
||||
|
||||
onListScrollX(e) {
|
||||
const { offsetWidth, scrollWidth, scrollLeft } = e.target
|
||||
const ping = []
|
||||
if (offsetWidth + scrollLeft < scrollWidth && scrollLeft > 0) {
|
||||
ping.push(...['left', 'right'])
|
||||
} else if (offsetWidth + scrollLeft < scrollWidth) {
|
||||
ping.push('right')
|
||||
} else if (offsetWidth + scrollLeft >= scrollWidth && offsetWidth < scrollWidth) {
|
||||
ping.push('left')
|
||||
}
|
||||
this.setState({ ping })
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading, dataSource, ping } = this.state
|
||||
|
||||
const attrs = {}
|
||||
Object.keys(this.props).forEach(key => {
|
||||
if (!propsMap.includes(key)) {
|
||||
@@ -206,9 +238,21 @@ export default class QueryList extends Component {
|
||||
</div>
|
||||
</div>
|
||||
<div className="yo-list">
|
||||
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />}>
|
||||
<List {...props} />
|
||||
{!!this.state.dataSource && !!this.state.dataSource.length && (
|
||||
<Spin spinning={loading} indicator={<AntIcon type="loading" />}>
|
||||
<div
|
||||
className={['yo-list-container', ...ping.map(p => `yo-list--ping-${p}`)]
|
||||
.filter(p => p)
|
||||
.join(' ')}
|
||||
>
|
||||
<div
|
||||
className="yo-list--scroll"
|
||||
ref="scroll-x"
|
||||
onScroll={e => this.onListScrollX(e)}
|
||||
>
|
||||
<List {...props} />
|
||||
</div>
|
||||
</div>
|
||||
{!!dataSource && !!dataSource.length && (
|
||||
<Pagination
|
||||
size="small"
|
||||
{...this.pagination}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Breadcrumb, Empty, Input, Layout, Spin, Tooltip, Tree } from 'antd'
|
||||
import { Breadcrumb, Button, Col, Empty, Input, Layout, Row, Spin, Tooltip, Tree } from 'antd'
|
||||
import { AntIcon, Container } from 'components'
|
||||
import { SIDER_BREAK_POINT } from 'util/global'
|
||||
|
||||
function generateKey(data, level) {
|
||||
const n = level || [0]
|
||||
@@ -23,7 +24,7 @@ function generateList(data) {
|
||||
this.list.push({
|
||||
key,
|
||||
[this.replaceFields.value]: data[i][this.replaceFields.value],
|
||||
[this.replaceFields.title]: data[i][this.replaceFields.title]
|
||||
[this.replaceFields.title]: data[i][this.replaceFields.title],
|
||||
})
|
||||
if (data[i][this.replaceFields.children]) {
|
||||
generateList.call(this, data[i][this.replaceFields.children])
|
||||
@@ -32,7 +33,7 @@ function generateList(data) {
|
||||
}
|
||||
|
||||
function getParentKey(key, tree) {
|
||||
let parentKey;
|
||||
let parentKey
|
||||
for (let i = 0; i < tree.length; i++) {
|
||||
const node = tree[i]
|
||||
if (node[this.replaceFields.children]) {
|
||||
@@ -43,23 +44,26 @@ function getParentKey(key, tree) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return parentKey;
|
||||
return parentKey
|
||||
}
|
||||
|
||||
function renderTitle(nodeData) {
|
||||
const title = nodeData[this.replaceFields.title]
|
||||
if (this.state.searchValue && title.indexOf(this.state.searchValue) > -1) {
|
||||
return <>
|
||||
{title.substr(0, title.indexOf(this.state.searchValue))}
|
||||
<span style={{ color: '#f50' }}>{this.state.searchValue}</span>
|
||||
{title.substr(title.indexOf(this.state.searchValue) + this.state.searchValue.length)}
|
||||
</>
|
||||
return (
|
||||
<>
|
||||
{title.substr(0, title.indexOf(this.state.searchValue))}
|
||||
<span style={{ color: '#f50' }}>{this.state.searchValue}</span>
|
||||
{title.substr(
|
||||
title.indexOf(this.state.searchValue) + this.state.searchValue.length
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
return <>{title}</>
|
||||
}
|
||||
|
||||
function renderBreadcrumbItem() {
|
||||
|
||||
const path = ['顶级']
|
||||
|
||||
const findPath = (data, level) => {
|
||||
@@ -91,14 +95,16 @@ function renderBreadcrumbItem() {
|
||||
}
|
||||
|
||||
export default class QueryTreeLayout extends Component {
|
||||
|
||||
state = {
|
||||
loading: false,
|
||||
dataSource: [],
|
||||
expandedKeys: [],
|
||||
selectedKey: '',
|
||||
autoExpandParent: true,
|
||||
searchValue: ''
|
||||
searchValue: '',
|
||||
|
||||
collapsed: false,
|
||||
showSider: false,
|
||||
}
|
||||
|
||||
list = []
|
||||
@@ -108,16 +114,18 @@ export default class QueryTreeLayout extends Component {
|
||||
replaceFields = {
|
||||
value: 'id',
|
||||
title: 'title',
|
||||
children: 'children'
|
||||
children: 'children',
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.autoLoad = typeof this.props.autoLoad === 'boolean' ? this.props.autoLoad : true
|
||||
this.loadData = typeof this.props.loadData === 'function' ? this.props.loadData : async () => { }
|
||||
this.loadData =
|
||||
typeof this.props.loadData === 'function' ? this.props.loadData : async () => {}
|
||||
|
||||
this.defaultExpanded = typeof this.props.defaultExpanded === 'boolean' ? this.props.defaultExpanded : false
|
||||
this.defaultExpanded =
|
||||
typeof this.props.defaultExpanded === 'boolean' ? this.props.defaultExpanded : false
|
||||
|
||||
if (this.props.replaceFields) {
|
||||
this.replaceFields = this.props.replaceFields
|
||||
@@ -131,6 +139,13 @@ export default class QueryTreeLayout extends Component {
|
||||
if (this.autoLoad) {
|
||||
this.onLoadData()
|
||||
}
|
||||
|
||||
window.addEventListener('resize', this.onResizeLayout)
|
||||
this.onResizeLayout()
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.onResizeLayout)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,7 +166,7 @@ export default class QueryTreeLayout extends Component {
|
||||
}
|
||||
this.setState({
|
||||
dataSource: data,
|
||||
expandedKeys
|
||||
expandedKeys,
|
||||
})
|
||||
this.onLoaded()
|
||||
}
|
||||
@@ -162,8 +177,8 @@ export default class QueryTreeLayout extends Component {
|
||||
onLoading = () => {
|
||||
this.setState({
|
||||
loading: {
|
||||
indicator: <AntIcon type="loading" />
|
||||
}
|
||||
indicator: <AntIcon type="loading" />,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -178,28 +193,28 @@ export default class QueryTreeLayout extends Component {
|
||||
this.onLoadData()
|
||||
}
|
||||
|
||||
onExpand = (expandedKeys) => {
|
||||
onExpand = expandedKeys => {
|
||||
this.setState({
|
||||
expandedKeys,
|
||||
autoExpandParent: false
|
||||
autoExpandParent: false,
|
||||
})
|
||||
}
|
||||
|
||||
onSelect = (selectedKeys) => {
|
||||
onSelect = selectedKeys => {
|
||||
const selectedIds = []
|
||||
selectedKeys.forEach(p => {
|
||||
const data = this.list.find(m => m.key === p)
|
||||
selectedIds.push(data[this.replaceFields.value])
|
||||
})
|
||||
this.setState({
|
||||
selectedKey: selectedIds[0]
|
||||
selectedKey: selectedIds[0],
|
||||
})
|
||||
if (this.props.onSelect) {
|
||||
this.props.onSelect(selectedIds[0])
|
||||
}
|
||||
}
|
||||
|
||||
onSearch = (value) => {
|
||||
onSearch = value => {
|
||||
const expandedKeys = this.list
|
||||
.map(p => {
|
||||
if (p[this.replaceFields.title].indexOf(value) > -1) {
|
||||
@@ -212,34 +227,49 @@ export default class QueryTreeLayout extends Component {
|
||||
this.setState({
|
||||
expandedKeys,
|
||||
autoExpandParent: !!value,
|
||||
searchValue: value
|
||||
searchValue: value,
|
||||
})
|
||||
}
|
||||
|
||||
onResizeLayout = () => {
|
||||
this.setState({
|
||||
collapsed: window.innerWidth <= SIDER_BREAK_POINT,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
const { dataSource, expandedKeys, autoExpandParent } = this.state
|
||||
const { loading, dataSource, expandedKeys, autoExpandParent, collapsed, showSider } =
|
||||
this.state
|
||||
|
||||
const props = {
|
||||
treeData: dataSource,
|
||||
expandedKeys,
|
||||
autoExpandParent
|
||||
autoExpandParent,
|
||||
}
|
||||
|
||||
const on = {
|
||||
onExpand: (expandedKeys) => this.onExpand(expandedKeys),
|
||||
onSelect: (selectedKeys) => this.onSelect(selectedKeys)
|
||||
onExpand: expandedKeys => this.onExpand(expandedKeys),
|
||||
onSelect: selectedKeys => this.onSelect(selectedKeys),
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout className="yo-tree-layout">
|
||||
<Layout.Sider width="240px">
|
||||
<Layout.Sider
|
||||
width="240px"
|
||||
className={[
|
||||
collapsed ? 'yo-tree-layout--collapsed' : '',
|
||||
showSider ? 'open' : '',
|
||||
]
|
||||
.filter(p => p)
|
||||
.join(' ')}
|
||||
onMouseLeave={() => this.setState({ showSider: false })}
|
||||
>
|
||||
<Layout.Header>
|
||||
<div className="header-actions">
|
||||
<Input.Search
|
||||
allowClear={true}
|
||||
placeholder="请输入检索关键字"
|
||||
onSearch={(value) => this.onSearch(value)}
|
||||
onSearch={value => this.onSearch(value)}
|
||||
/>
|
||||
</div>
|
||||
</Layout.Header>
|
||||
@@ -248,37 +278,56 @@ export default class QueryTreeLayout extends Component {
|
||||
<AntIcon type="undo" onClick={() => this.onReloadData()} />
|
||||
</Tooltip>
|
||||
<Tooltip placement="bottom" title="折叠全部">
|
||||
<AntIcon type="switcher" onClick={() => this.setState({ expandedKeys: [] })} />
|
||||
<AntIcon
|
||||
type="switcher"
|
||||
onClick={() => this.setState({ expandedKeys: [] })}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="yo-tree-layout--content">
|
||||
<Spin spinning={this.state.loading} indicator={<AntIcon type="loading" />} wrapperClassName="h-100-p">
|
||||
{
|
||||
!this.state.loading && !this.state.dataSource.length ?
|
||||
<Empty
|
||||
image={
|
||||
<div className="text-center pt-md">
|
||||
<AntIcon className="h3 mt-xl mb-md" type="smile" />
|
||||
<p>暂无数据</p>
|
||||
</div>
|
||||
}
|
||||
description={false}
|
||||
/>
|
||||
:
|
||||
<Tree
|
||||
{...props}
|
||||
{...on}
|
||||
titleRender={(nodeData) => renderTitle.call(this, nodeData)}
|
||||
/>
|
||||
}
|
||||
<Spin
|
||||
spinning={loading}
|
||||
indicator={<AntIcon type="loading" />}
|
||||
wrapperClassName="h-100-p"
|
||||
>
|
||||
{!loading && !dataSource.length ? (
|
||||
<Empty
|
||||
image={
|
||||
<div className="text-center pt-md">
|
||||
<AntIcon className="h3 mt-xl mb-md" type="smile" />
|
||||
<p>暂无数据</p>
|
||||
</div>
|
||||
}
|
||||
description={false}
|
||||
/>
|
||||
) : (
|
||||
<Tree
|
||||
{...props}
|
||||
{...on}
|
||||
titleRender={nodeData => renderTitle.call(this, nodeData)}
|
||||
/>
|
||||
)}
|
||||
</Spin>
|
||||
</div>
|
||||
</Layout.Sider>
|
||||
<Layout.Content>
|
||||
<Container mode="fluid">
|
||||
<Breadcrumb className="mt-md mb-md">
|
||||
{renderBreadcrumbItem.call(this)}
|
||||
</Breadcrumb>
|
||||
<Row gutter={16} align="middle">
|
||||
{collapsed && (
|
||||
<Col>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<AntIcon type="menu" />}
|
||||
onClick={() => this.setState({ showSider: true })}
|
||||
/>
|
||||
</Col>
|
||||
)}
|
||||
<Col flex="1">
|
||||
<Breadcrumb className="mt-md mb-md">
|
||||
{renderBreadcrumbItem.call(this)}
|
||||
</Breadcrumb>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
{this.props.children}
|
||||
</Layout.Content>
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
const layout = (state = {
|
||||
siderCollapsed: false
|
||||
}, action) => {
|
||||
import { SETTING_KEY } from "common/storage"
|
||||
import { SIDER_BREAK_POINT } from "util/global"
|
||||
|
||||
const defaultState = {
|
||||
siderCollapsed: false,
|
||||
allowSiderCollapsed: true
|
||||
}
|
||||
|
||||
const localStorageState = () => {
|
||||
return JSON.parse(window.localStorage.getItem(SETTING_KEY))
|
||||
}
|
||||
|
||||
const mergeState = {
|
||||
...defaultState,
|
||||
...localStorageState()
|
||||
}
|
||||
|
||||
const layout = (state = mergeState, action) => {
|
||||
switch (action.type) {
|
||||
// 打开窗口
|
||||
case 'OPEN_WINDOW':
|
||||
@@ -13,8 +28,23 @@ const layout = (state = {
|
||||
return state
|
||||
// 侧边收起状态
|
||||
case 'TOGGLE_COLLAPSED':
|
||||
const _state = { ...state, siderCollapsed: action.siderCollapsed }
|
||||
return _state
|
||||
{
|
||||
if (window.innerWidth <= SIDER_BREAK_POINT) {
|
||||
return state
|
||||
}
|
||||
const _state = { ...state, siderCollapsed: action.siderCollapsed }
|
||||
window.localStorage.setItem(SETTING_KEY, JSON.stringify(_state))
|
||||
return _state
|
||||
}
|
||||
case 'AUTO_TOGGLE_COLLAPSED':
|
||||
{
|
||||
const _state = {
|
||||
...state,
|
||||
siderCollapsed: localStorageState().siderCollapsed || action.siderCollapsed,
|
||||
allowSiderCollapsed: !action.siderCollapsed
|
||||
}
|
||||
return _state
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
|
||||
@@ -36,4 +36,6 @@ export const RSA_PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQU
|
||||
/**
|
||||
* 城市名称
|
||||
*/
|
||||
export const CITY = '黄石市'
|
||||
export const CITY = '黄石市'
|
||||
|
||||
export const SIDER_BREAK_POINT = 1366
|
||||
@@ -33,13 +33,20 @@ export default class index extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { allowSiderCollapsed } = this.state
|
||||
|
||||
return (
|
||||
<Layout.Header>
|
||||
<Container mode="fluid">
|
||||
<div className="header-actions">
|
||||
<span className="header-action mr-md" onClick={() => this.onCollapsed()}>
|
||||
<AntIcon type="menu" />
|
||||
</span>
|
||||
{allowSiderCollapsed && (
|
||||
<span
|
||||
className="header-action mr-md"
|
||||
onClick={() => this.onCollapsed()}
|
||||
>
|
||||
<AntIcon type="menu" />
|
||||
</span>
|
||||
)}
|
||||
<Logo />
|
||||
<Search />
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import React, { Component } from 'react'
|
||||
import { Spin, Layout } from 'antd'
|
||||
import { LoadingOutlined } from '@ant-design/icons'
|
||||
import { api } from 'common/api'
|
||||
import { cloneDeep, groupBy, findIndex, last } from 'lodash'
|
||||
import store from 'store'
|
||||
import { EMPTY_ID } from 'util/global'
|
||||
import { EMPTY_ID, SIDER_BREAK_POINT } from 'util/global'
|
||||
|
||||
import Header from './_layout/header'
|
||||
import Sider from './_layout/sider'
|
||||
@@ -83,6 +82,9 @@ export default class index extends Component {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
window.addEventListener('resize', this.onResizeSider)
|
||||
this.onResizeSider()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,6 +233,13 @@ export default class index extends Component {
|
||||
})
|
||||
}
|
||||
|
||||
onResizeSider = () => {
|
||||
dispatch({
|
||||
type: 'AUTO_TOGGLE_COLLAPSED',
|
||||
siderCollapsed: window.innerWidth <= SIDER_BREAK_POINT,
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading, panes, actived } = this.state
|
||||
|
||||
|
||||
Reference in New Issue
Block a user