From 02babf74f7f40e8c299a10b01d15f24d5801d2b1 Mon Sep 17 00:00:00 2001 From: zuiidea Date: Sun, 11 Sep 2016 22:55:25 +0800 Subject: [PATCH] some layout --- src/components/Sidebar/index.js | 166 ++++++++++---------- src/components/Welcome/index.js | 6 +- src/index.js | 1 - src/menu.js | 50 +++++- src/view/index.js | 32 ++-- src/view/index.less | 260 ++++++++++++++++++++++++++++++++ webpack.config.prod.js | 29 ++-- 7 files changed, 433 insertions(+), 111 deletions(-) create mode 100644 src/view/index.less diff --git a/src/components/Sidebar/index.js b/src/components/Sidebar/index.js index 378448d..18d8d75 100644 --- a/src/components/Sidebar/index.js +++ b/src/components/Sidebar/index.js @@ -1,113 +1,119 @@ -import React from 'react'; -import {Link} from 'react-router'; -import {Menu, Icon} from 'antd'; -import Logo from '../Logo'; -import Logger from '../../utils/Logger'; -import items from 'menu.js'; // 由于webpack中的设置, 不用写完整路径 +import React from 'react' +import { Link } from 'react-router' +import { Menu, Icon } from 'antd' +import Logo from '../Logo' +import Logger from '../../utils/Logger' +import items from 'menu.js' -const SubMenu = Menu.SubMenu; -const MenuItem = Menu.Item; +const SubMenu = Menu.SubMenu +const MenuItem = Menu.Item +const logger = Logger.getLogger('Sidebar') -const logger = Logger.getLogger('Sidebar'); - -/** - * 定义sidebar组件 - */ -class Sidebar extends React.Component { - - /** - * 将菜单项配置转换为对应的MenuItem组件 - * - * @param obj sidebar菜单配置项 - * @param paths 父级目录, array - * @returns {XML} - */ +const Sider = React.createClass({ + getInitialState() { + let fixed=false; + let fixedText="固定"; + if(window.localStorage.getItem("mgFixedSider")&&window.localStorage.getItem("mgFixedSider")=="true"){ + fixed=true; + fixedText="取消"; + } + return { + fixed:fixed, + collapse: true, + fixedText:fixedText + } + }, + onCollapseChange() { + this.setState({ + collapse: !this.state.collapse, + }) + }, + switchFixed(){ + window.localStorage.setItem("mgFixedSider",!this.state.fixed); + this.setState({ + fixed: !this.state.fixed + }); + if(this.state.fixedText=="固定"){ + this.setState({ + fixed: !this.state.fixed, + fixedText:"取消" + }); + } else { + this.setState({ + fixed: !this.state.fixed, + fixedText:"固定" + }); + } + }, transFormMenuItem(obj, paths) { - const parentPath = paths.join('/'); // 将各级父目录组合成完整的路径 - logger.debug('transform %o to path %s', obj, parentPath); - - // 这个表达式还是有点恶心的... - // JSX虽然方便, 但是很容易被滥用, ES6也是 + const parentPath = paths.join('/') + logger.debug('transform %o to path %s', obj, parentPath) return ( {obj.child ? obj.name : {obj.name} } - ); - } - - // 在每次组件挂载的时候parse一次菜单, 不用每次render都解析 + ) + }, componentWillMount() { - const paths = []; // 暂存各级路径, 当作stack用 - const defaultOpenKeys = []; - - // 菜单项是从配置中读取的, parse过程还是有点复杂的 - // map函数很好用 + const paths = [] + const defaultOpenKeys = [] const menu = items.map((level1) => { - // parse一级菜单 - paths.push(level1.key); + paths.push(level1.key) if (defaultOpenKeys.length === 0) { - defaultOpenKeys.push(level1.key); // 默认展开第一个菜单 + defaultOpenKeys.push(level1.key) } - - // 是否有子菜单? if (level1.child) { const level2menu = level1.child.map((level2) => { - // parse二级菜单 - paths.push(level2.key); + paths.push(level2.key) if (level2.child) { const level3menu = level2.child.map((level3) => { - // parse三级菜单, 不可能再有子菜单了 - paths.push(level3.key); - const tmp = this.transFormMenuItem(level3, paths); - paths.pop(); - return tmp; - }); - - paths.pop(); + paths.push(level3.key) + const tmp = this.transFormMenuItem(level3, paths) + paths.pop() + return tmp + }) + paths.pop() return ( {level3menu} - ); - + ) } else { - const tmp = this.transFormMenuItem(level2, paths); - paths.pop(); - return tmp; + const tmp = this.transFormMenuItem(level2, paths) + paths.pop() + return tmp } - }); - - paths.pop(); + }) + paths.pop() return ( {level1.name}}> {level2menu} ) } - // 没有子菜单, 直接转换为MenuItem else { - const tmp = this.transFormMenuItem(level1, paths); - paths.pop(); // return之前别忘了pop - return tmp; + const tmp = this.transFormMenuItem(level1, paths) + paths.pop() + return tmp } - }); - - this.menu = menu; - this.defaultOpenKeys = defaultOpenKeys; - } - + }) + this.menu = menu + this.defaultOpenKeys = defaultOpenKeys + }, render() { - // 这些样式其实是在App/index.less中定义的 + const collapse = this.state.collapse return ( - - ); +
+
+ + {this.menu} + +
+ {collapse ? : } +
+
+ ) } +}) -} - -export default Sidebar; +module.exports = Sider diff --git a/src/components/Welcome/index.js b/src/components/Welcome/index.js index a01d2d5..441df94 100644 --- a/src/components/Welcome/index.js +++ b/src/components/Welcome/index.js @@ -9,11 +9,7 @@ class Welcome extends React.Component { render() { return (
-

- Welcome, 这里是欢迎界面, 欢迎访问我的blog. -
- 项目地址: https://github.com/jiangxy/react-antd-admin -

+ Hello,React
); } diff --git a/src/index.js b/src/index.js index ae4d1af..fe8620b 100644 --- a/src/index.js +++ b/src/index.js @@ -8,7 +8,6 @@ import {Router, Route, IndexRoute, hashHistory} from 'react-router'; import './utils/index.js'; // 引入各种prototype辅助方法 // 开始引入各种自定义的组件 -// import App from './components/App'; import App from './view/index'; import SignIn from './view/user/signIn'; import Welcome from './components/Welcome'; diff --git a/src/menu.js b/src/menu.js index 3735884..e340591 100644 --- a/src/menu.js +++ b/src/menu.js @@ -10,9 +10,9 @@ module.exports = [ { - key: 'index', // route时url中的值 - name: '菜单哈哈哈', // 在菜单中显示的名称 - icon: 'smile', // 只有顶层菜单可以带图标 + key: 'index', + name: '菜单一', + icon: 'user', child: [ { key: 'option1', @@ -30,8 +30,8 @@ module.exports = [ }, { key: 'daohang', - name: '导航', - icon: 'appstore', + name: '菜单二', + icon: 'setting', child: [ { key: '555', @@ -63,8 +63,8 @@ module.exports = [ }, { key: 'test', - name: '测试', - icon: 'eye', + name: '菜单三', + icon: 'laptop', child: [ { key: 'aaa', @@ -100,4 +100,40 @@ module.exports = [ }, ], }, + { + key: 'menu4', + name: '菜单四', + icon: 'notification', + child: [ + { + key: 'menu4', + name: '菜单四一', + }, + ], + }, + { + key: 'menu5', + name: '菜单五', + icon: 'folder', + child: [ + { + key: 'menu51', + name: '菜单五一', + }, + { + key: 'menu52', + name: '菜单五二', + }, + { + key: 'menu53', + name: '菜单五三', + child: [ + { + key: 'menu531', + name: '菜单五三一', + }, + ], + }, + ], + }, ]; diff --git a/src/view/index.js b/src/view/index.js index 0e1ae4e..9a5c8ae 100644 --- a/src/view/index.js +++ b/src/view/index.js @@ -5,8 +5,9 @@ import Footer from '../components/Footer' import Sidebar from '../components/Sidebar' import SignIn from './user/SignIn' import Breadcrumb from '../components/Breadcrumb' +import './index.less' import { ajax, config, logger } from '../utils/lib.js' -import { Spin, message } from 'antd' +import { Spin, message, Icon } from 'antd' const App = React.createClass({ getInitialState() { @@ -16,6 +17,7 @@ const App = React.createClass({ loading: false, login: true, userName: '未登录', + collapse: false, } }, componentDidMount() { @@ -63,20 +65,32 @@ const App = React.createClass({ } else if (!this.state.login) { return } + const collapse = this.state.collapse return ( -
+
+ +
+
+
+
-
-
- {this.state.loading ? '' : this.props.children} +
+
+
+
+ {this.props.children}
-
- +
+
+ Ant Design 版权所有 © 2015 由蚂蚁金服体验技术部支持 +
) diff --git a/src/view/index.less b/src/view/index.less new file mode 100644 index 0000000..81ff18d --- /dev/null +++ b/src/view/index.less @@ -0,0 +1,260 @@ +body { + background: #f0f0f0; + height: 100%; + overflow-y: hidden; +} +// .main-wrapper { +// background: #fff; +// width: 100%; +// margin: 0 auto; +// overflow: hidden; +// position: relative; +// // min-width: 1200px; +// .aside { +// float: left; +// // height: e("calc(100vh - 48px)"); +// height: 100vh; +// .ant-menu-item-selected { +// background-color: #333; +// } +// .side-menus { +// // height: e("calc(100vh - 48px)"); +// height: 100vh; +// position: absolute; +// width: 60px; +// overflow: hidden; +// transition: width 0.3s ease; +// left: 0; +// z-index: 9999; +// background: #404040; +// &:hover{ +// width: 200px; +// } +// &.fixed-sider{ +// width: 200px; +// position: relative; +// .ant-menu-item { +// a { +// .text { +// opacity: 1; +// } +// } +// } +// } +// .ant-menu-item { +// height: 48px; +// line-height: 48px; +// text-align: center; +// padding: 0; +// transition: background-color 0.3s ease; +// &:hover { +// background-color: #474747; +// } +// a { +// width: 100%; +// text-align: center; +// .text { +// opacity: 0; +// transition: opacity 0.3s ease; +// width: 60px; +// display: inline-block; +// text-align: left; +// } +// .anticon { +// width: 60px; +// margin-right: 0; +// font-size: 14px; +// } +// } +// } +// &:hover { +// .ant-menu-item { +// a { +// .text { +// opacity: 1; +// } +// } +// } +// } +// } +// } +// .content { +// padding: 24px; +// height: e("calc(100vh - 48px)"); +// margin-left: 60px; +// background: #fff; +// overflow: auto; +// .ant-table-small { +// border: none; +// } +// } +// &.no-aside { +// .content { +// margin-left: 0; +// padding: 24px; +// } +// } +// } +// .fixed-btn { +// position: absolute; +// bottom: 0; +// left: 0; +// width: 200px; +// color: #666; +// height: 48px; +// z-index: 9999; +// line-height: 48px; +// background: #474747; +// transition: all .3s ease; +// cursor: pointer; +// .text { +// width: 60px; +// display: inline-block; +// text-align: left; +// float: left; +// } +// .anticon { +// width: 60px; +// margin-right: 0; +// float: left; +// line-height: 48px; +// } +// &:hover{ +// color: #2db7f5; +// background-color: #333; +// } +// } +.ant-layout-aside { + position: relative; + min-height: 100%; +} + +.ant-layout-aside .ant-layout-logo { + width: 150px; + height: 32px; + background: #333; + border-radius: 6px; + margin: 16px 24px 16px 28px; + transition: all 0.3s ease; +} + +.ant-layout-aside-collapse .ant-layout-logo { + width: 32px; + margin: 16px; + transition: all 0.3s ease; +} + +.ant-layout-aside .ant-layout-sider { + width: 224px; + background: #404040; + position: absolute; + overflow: visible; + padding-bottom: 24px; + height: 100%; + transition: all 0.3s ease; +} + +.ant-layout-aside-collapse .ant-layout-sider { + width: 64px; + transition: all 0.3s ease; +} + +.ant-layout-aside .ant-layout-sider > .ant-menu { + margin-bottom: 20px; +} + +.ant-layout-aside .ant-layout-sider > .ant-menu > .ant-menu-item { + margin: 16px 0; +} + +.ant-layout-aside .ant-layout-sider > .ant-menu > .ant-menu-item .nav-text { + vertical-align: baseline; + display: inline-block; +} + +.ant-layout-aside .ant-layout-sider > .ant-menu > .ant-menu-item > .anticon { + transition: font-size .3s; +} + +.ant-layout-aside-collapse .ant-layout-sider > .ant-menu > .ant-menu-item { + transition: all 0s ease; +} + +.ant-layout-aside-collapse .ant-layout-sider > .ant-menu > .ant-menu-item > .anticon { + font-size: 16px; + display: inline-block; +} + +.ant-layout-aside-collapse .ant-layout-sider > .ant-menu > .ant-menu-item .nav-text { + display: none; +} + +.ant-layout-aside-collapse .ant-layout-sider > .ant-menu > .ant-menu-item:hover { + background: #2db7f5; + color: #fff; + transition: all 0s ease; +} + +.ant-layout-aside-collapse .ant-layout-sider > .ant-menu > .ant-menu-item:hover .nav-text { + display: inline-block; + vertical-align: top; + background: #2db7f5; + color: #fff; + padding-right: 16px; + border-radius: 0 5px 5px 0; +} + +/* 实际使用中需要改成 position: fixed */ +.ant-layout-aside { + .ant-layout-header{ + background: #fff; + height: 64px; + border-bottom: 1px solid #e9e9e9; + } + .ant-layout-breadcrumb{ + margin: 7px 0 -17px 24px; + } + .ant-layout-main{ + margin-left: 224px; + transition: all 0.3s ease; + } + .ant-aside-action { + height: 42px; + width: 224px; + position: fixed; + bottom: 0; + background: #474747; + color: #fff; + text-align: center; + line-height: 42px; + cursor: pointer; + transition: all 0.3s ease; + } + .ant-layout-container{ + margin: 24px 16px; + .ant-layout-content{ + background: #fff; + padding: 24px; + } + } + .ant-layout-footer{ + height: 64px; + line-height: 64px; + text-align: center; + font-size: 12px; + color: #999; + background: #fff; + border-top: 1px solid #e9e9e9; + width: 100%; + } + &.ant-layout-aside-collapse{ + .ant-aside-action{ + width: 64px; + transition: all 0.3s ease; + } + .ant-layout-main{ + margin-left: 64px; + transition: all 0.3s ease; + } + } +} diff --git a/webpack.config.prod.js b/webpack.config.prod.js index 408e739..bf1d92f 100644 --- a/webpack.config.prod.js +++ b/webpack.config.prod.js @@ -1,4 +1,6 @@ -const webpack = require('webpack'); +const webpack = require('webpack') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require("extract-text-webpack-plugin") const babelLoaderConfig = { presets: ['es2015', 'stage-0', 'react'], @@ -13,7 +15,7 @@ module.exports = { output: { path: __dirname + '/dist', filename: 'bundle.js', - // publicPath: 'http://mycdn.com/', // require时用来生成图片的地址 + // publicPath: 'http://mycdn.com/', }, resolve: { @@ -27,15 +29,23 @@ module.exports = { test: /\.jsx?$/, loaders: ['babel-loader?' + JSON.stringify(babelLoaderConfig), 'strip-loader?strip[]=logger.debug,strip[]=console.log,strip[]=console.debug'], exclude: /node_modules/, - }, { - test: /\.css$/, - loader: 'style!css', - }, { - test: /\.less$/, - loader: 'style!css!less', - }, { + }, + // { + // test: /\.css$/, + // loader: 'style!css', + // }, { + // test: /\.less$/, + // loader: 'style!css!less', + // }, + { test: /\.(png|jpg|svg)$/, loader: 'url?limit=25000', + },{ + test: /\.css$/, + loader: ExtractTextPlugin.extract("style-loader", "css-loader") + }, { + test: /\.less$/, + loader: ExtractTextPlugin.extract("style-loader", "css-loader!less-loader") }, ], }, @@ -50,6 +60,7 @@ module.exports = { filename: 'index.html', template: './src/index.html' }), + new ExtractTextPlugin("[name].css"), new webpack.optimize.DedupePlugin(), new webpack.optimize.OccurenceOrderPlugin(), new webpack.optimize.AggressiveMergingPlugin(),