react项目开发-权限布局(前三篇续)

基于create-react-app官方脚手架搭建dva模式的项目-权限布局的开发,之前的项目骨架已经可以按照你自己的业务和项目逻辑去组装汽车了。

这里依然以上项目骨架为基础,增加权限布局的开发。

权限设计思路:

1 用户一旦登录成功,后台会返回一个token令牌,此令牌的形式有很多种,传统的就是session-cookie机制了,也可以采用比较流行的一种令牌机制JWT(有兴趣的同学可自行学习),个人推荐已此种方式做令牌。

2 前端会把token存在浏览器端,比如sessionStorge存储机制,之后每次请求均会在请求的中带上这个token传给服务端,服务端校验此令牌,若合法则通过,视为正常用户,若不合法则返回无权限。

3 用户点击退出,或者后台返回不合法,前端则请求退出接口,并清空临时数据,返回登录界面。

4 涉及登录安全,在开发环境也增加https访问模式,修改package.json文件中scripts配置项:

"start": "cross-env PORT=9999 HTTPS=true node scripts/start.js",

下面我们在之前基础上进行开发

1 routes目录下新建login目录,里面新建index.js和index.less文件作为登录页面:

(1)不要忘了接入store数据

(2)登录页面,需要表单Form组件支持

(3)用到了样式,本项目用到classnames (可自行学习),安装:cnpm i classnames --save

(4)要用到css-module,这里有个点:直接开启module会导致和antd冲突,故此开启方式,修改webpack.config.dev.js和webpack.config.prod.js,在本文件中复制一份css-loader配置部分,改为开启less的module,这份配置排在前面

代码如下:

{
            test: /\.less$/,
            use: [
              require.resolve('style-loader'),
              {
                loader: require.resolve('css-loader'),
                options: {
                  importLoaders: 1,
                  modules: true,
                  localIdentName:"[name]__[local]___[hash:base64:5]"
                },
              },
              {
                loader: require.resolve('postcss-loader'),
                options: {
                  // Necessary for external CSS imports to work
                  // https://github.com/facebookincubator/create-react-app/issues/2677
                  ident: 'postcss',
                  plugins: () => [
                    require('postcss-flexbugs-fixes'),
                    autoprefixer({
                      browsers: [
                        '>1%',
                        'last 4 versions',
                        'Firefox ESR',
                        'not ie < 9', // React doesn't support IE8 anyway
                      ],
                      flexbox: 'no-2009',
                    }),
                  ],
                },
              },
              {
                loader:require.resolve('less-loader'),
                options: { javascriptEnabled: true }
              }
            ],
          },
          {
            test: /\.css$/,
            use: [
              require.resolve('style-loader'),
              {
                loader: require.resolve('css-loader'),
                options: {
                  importLoaders: 1,
                },
              },
              {
                loader: require.resolve('postcss-loader'),
                options: {
                  // Necessary for external CSS imports to work
                  // https://github.com/facebookincubator/create-react-app/issues/2677
                  ident: 'postcss',
                  plugins: () => [
                    require('postcss-flexbugs-fixes'),
                    autoprefixer({
                      browsers: [
                        '>1%',
                        'last 4 versions',
                        'Firefox ESR',
                        'not ie < 9', // React doesn't support IE8 anyway
                      ],
                      flexbox: 'no-2009',
                    }),
                  ],
                },
              }
            ],
          },


关与开启module和antd冲突解决方法网上找了下,大家可参考:

https://segmentfault.com/a/1190000011225917

https://segmentfault.com/q/1010000011965218

https://www.jianshu.com/p/51ff1c8be301

https://blog.csdn.net/nongweiyilady/article/details/79939761

登录页面代码如下:

login/index.js

import React, {Component} from 'react';
import {connect} from 'dva'
import {injectIntl} from 'react-intl'
import {Row, Col, Form, Icon, Input, Button} from 'antd'
import classnames from 'classnames';
import styles from './index.less';

const FormItem = Form.Item

class Login extends Component{

    loginSubmit=(e)=>{
        e.preventDefault();
        const {form} = this.props;
        form.validateFields((err, values) => {
            if (!err) {
              console.log(values);
            }
        });
    }

    render(){
        const {form} = this.props;
        const {getFieldDecorator} = form;
        return(
            <Row>
                <Col className={classnames(styles.loginFormCol)}>
                    <Form onSubmit={this.loginSubmit} className={classnames(styles.loginForm)}>
                        <h3>登录</h3>
                        <FormItem>
                        {getFieldDecorator('username', {
                            rules: [{ 
                                required: true, 
                                message: '请输入用户名' 
                            }],
                        })(
                            <Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="用户名" />
                        )}
                        </FormItem>
                        <FormItem>
                        {getFieldDecorator('password', {
                            rules: [{ 
                                required: true, 
                                message: '请输入密码' 
                            }],
                        })(
                            <Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="密码" />
                        )}
                        </FormItem>
                        <FormItem>
                            <Button type="primary" htmlType="submit" className={classnames(styles.loginBtn)}>
                                登录
                            </Button>
                        </FormItem>
                    </Form>
                </Col>
            </Row>
        )
    }
}

export default connect(({

})=>({

}))(injectIntl(Form.create()(Login)))

login/index.less

.loginFormCol{
    height:100vh;
    display:flex;
    justify-content:center;
    flex-direction:column;
    .loginForm{
        width:300px;
        height:190px;
        margin:0 auto;
        .loginBtn{
            width:100%
        }
    }
}

最终效果:


2 修改models/app.js文件,增加login.js的model

(1)增加token存储字段,用于将来存储令牌的字段,值取自sessionStorage.getItem('token'),对应增加对其的实时更新effects和reducers:updateToken和updateStore

(2)增加locationPathname字段,用于存储当前url的pathname,对应增加对其的实时更新effects和reducers:updateLocation

代码如下:

import {Map, fromJS} from 'immutable';
import {routerRedux} from 'dva/router';
const initState = Map({
i18n: 'zh_CN',
token:null,
locationPathname:null,
})
export default {
namespace: 'app',
state:initState,
subscriptions: {
setup({ dispatch, history }) {
},
setupHistory ({ dispatch, history }) {
history.listen((location) => {
dispatch({
type: 'updateLocation',
payload: {
locationPathname: location.pathname
},
});
dispatch({
type: 'updateToken',
payload: {
token: window.sessionStorage.getItem('token')
},
})
})
},
},
effects: {
* changeLang ({
payload: {value},
}, { put }) {
yield put({ type: 'updateLang', payload: {value}});
},
* updateLocation ({
payload
}, {put, select}) {
yield put({type: 'updateStore', payload});
},
* updateToken ({
payload
}, {put, select}) {
yield put({type: 'updateStore', payload});
},
* logout ({
payload
}, {put, select}) {
window.sessionStorage.removeItem('token');
window.location.href='/login';
},
},
reducers: {
updateLang (state,{payload:{value}}) {
return state.set('i18n',value);
},
updateStore (state, { payload }) {
return payload?state.mergeDeep(fromJS(payload)):initState
},
},
};

(3)models/login.js代码:

export default {

    namespace: 'login',
  
    state: {
      name:'这是login的model'
    },
  
    subscriptions: {
      
    },
  
    effects: {
      
    },
  
    reducers: {
      
    },
  
  };
  

3 src目录下增加目录layout,用于存放布局组件

(1)layout下创建auth.js,权限组件,用户包裹全局组件:

import {connect} from 'dva';
import React from 'react';

const Auth=({ children,dispatch,token,locationPathname })=>{

  if(!token&&locationPathname!='/login'){
    dispatch({
      type:'app/logout'
    })
  }

  return (
    <React.Fragment>
      {children}
    </React.Fragment>
  );
}

export default connect(({
  app
})=>({
  token:app.get('token'),
  locationPathname:app.get('locationPathname'),
}))(Auth)

(2)把locale.js国际化组件,移到layout目录下,目录调整如下:


4 src/route.js修改,包裹auth组件:

import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import dynamic from 'dva/dynamic'
import Locale from './layout/locale'
import Auth from './layout/auth'
import {config} from './utils'
const { menuGlobal } = config

function RouterConfig({ history, app }) {

  return (
    <Auth>
      <Locale>
        <Router history={history}>
          <Switch>
            {
              menuGlobal.map(({path,...dynamics},index)=>(
                <Route
                  key={index} 
                  path={path} 
                  exact 
                  component={dynamic({
                    app,
                    ...dynamics
                  })} 
                />
              ))
            }
          </Switch>
        </Router>
      </Locale>
    </Auth>
  );
}

export default RouterConfig;

下面验证一下效果:

直接访问:https://localhost:9999/aaa , 你会发现已经跳转到了https://localhost:9999/login页面,当我们再sessionStorage中模拟一个token=123456,再次访问/aaa即可进入,当清除掉token则退出到/login,效果如下:


5 routes目录下,新建home,作为登录后的首页,目录如下:


index.js

import React, {Component} from 'react';
import {connect} from 'dva'
import {Link} from 'dva/router'
import {injectIntl} from 'react-intl'
import {Row, Col, Form, Button} from 'antd'
import classnames from 'classnames';
import styles from './index.less';

class Home extends Component{

    render(){
        return(
            <Row>
                <Col className={classnames(styles.home)}>
                    欢迎您,来到首页
                </Col>
                <Col>
                    <Link to={'/aaa'}><Button>去AAA页面</Button></Link>
                </Col>
            </Row>
        )
    }
}

export default connect(({

})=>({

}))(injectIntl(Form.create()(Home)))

index.less

.home{
    padding:20px;
}

6 models目录下新建home.js

export default {

    namespace: 'home',
  
    state: {
      name:'这是home的model'
    },
  
    subscriptions: {
      
    },
  
    effects: {
      
    },
  
    reducers: {
      
    },
  
  };
  

7 修改utils/config.js 代码:

const menuGlobal=[
    {
        id:'login',
        pid:'0',
        name:'登录',
        icon:'user',
        path: '/login',
        models: () => [import('../models/login')], //models可多个
        component: () => import('../routes/login'),
    }, 
    {
        id:'home',
        pid:'0',
        name:'首页',
        icon:'user',
        path: '/',
        models: () => [import('../models/home')], //models可多个
        component: () => import('../routes/home'),
    }, 
    {
        id:'aaa',
        pid:'0',
        name:'aaa页',
        icon:'user',
        path: '/aaa',
        models: () => [import('../models/aaa')], //models可多个
        component: () => import('../routes/AAA'),
    }, 
    {
        id:'bbb',
        pid:'0',
        name:'bbb页',
        icon:'user',
        path: '/aaa/bbb',
        models: () => [import('../models/bbb')], //models可多个
        component: () => import('../routes/BBB'),
    }, 
    {
        id:'ccc',
        pid:'0',
        name:'ccc页',
        icon:'user',
        path: '/ccc',
        models: () => [import('../models/ccc')], //models可多个
        component: () => import('../routes/CCC'),
    }, 
  ];
  
export default {
    menuGlobal
}

8 我们暂时约定账号为admin,密码为123456

(1) 修改/routes/login/index.js

import React, {Component} from 'react';
import {connect} from 'dva'
import {injectIntl} from 'react-intl'
import {Row, Col, Form, Icon, Input, Button} from 'antd'
import classnames from 'classnames';
import styles from './index.less';

const FormItem = Form.Item

class Login extends Component{

    loginSubmit=(e)=>{
        e.preventDefault();
        const {form,dispatch} = this.props;
        form.validateFields((err, values) => {
            if (!err) {
              dispatch({
                  type:'login/login',
                  payload:{
                      values
                  }
              })
            }
        });
    }

    render(){
        const {form} = this.props;
        const {getFieldDecorator} = form;
        return(
            <Row>
                <Col className={classnames(styles.loginFormCol)}>
                    <Form onSubmit={this.loginSubmit} className={classnames(styles.loginForm)}>
                        <h3>登录</h3>
                        <FormItem>
                        {getFieldDecorator('username', {
                            rules: [{ 
                                required: true, 
                                message: '请输入用户名' 
                            }],
                        })(
                            <Input autoComplete={'off'} prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="用户名" />
                        )}
                        </FormItem>
                        <FormItem>
                        {getFieldDecorator('password', {
                            rules: [{ 
                                required: true, 
                                message: '请输入密码' 
                            }],
                        })(
                            <Input autoComplete={'off'} prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="密码" />
                        )}
                        </FormItem>
                        <FormItem>
                            <Button type="primary" htmlType="submit" className={classnames(styles.loginBtn)}>
                                登录
                            </Button>
                        </FormItem>
                    </Form>
                </Col>
            </Row>
        )
    }
}

export default connect(({

})=>({

}))(injectIntl(Form.create()(Login)))

(2) 修改models/login.js

import {message} from 'antd';

export default {

    namespace: 'login',
  
    state: {
      name:'这是login的model'
    },
  
    subscriptions: {
      
    },
  
    effects: {
      * login ({
          payload
      }, {put, select}) {
          if(payload.values.username=='admin'&&payload.values.password=='123456'){
            //登录成功
            yield put({
              type:'app/loginOk',
              payload:{
                token:'123abc'
              }
            });
          }else{
            message.warning('用户名或密码不正确')
          }
      },

    },
  
    reducers: {
      
    },
  
  };
  

(3) 修改models/app.js

import {Map, fromJS} from 'immutable';
import {routerRedux} from 'dva/router';

const initState = Map({
    i18n: 'zh_CN',
    token:null,
    locationPathname:null,
})

export default {

    namespace: 'app',
  
    state:initState,
  
    subscriptions: {
        setup({ dispatch, history }) {
        
        },
        setupHistory ({ dispatch, history }) {
            history.listen((location) => {
                dispatch({
                    type: 'updateLocation',
                    payload: {
                      locationPathname: location.pathname
                    },
                });
                dispatch({
                    type: 'updateToken',
                    payload: {
                      token: window.sessionStorage.getItem('token')
                    },
                })
            })
        },
    },
  
    effects: {

        * changeLang ({
            payload: {value},
        }, { put }) {
            yield put({ type: 'updateLang', payload: {value}});
        },

        * updateLocation ({
            payload
        }, {put, select}) {
            yield put({type: 'updateStore', payload});
        },

        * updateToken ({
            payload
        }, {put, select}) {
            yield put({type: 'updateStore', payload});
        },

        * loginOk ({
            payload
        }, {put, select}) {
            window.sessionStorage.setItem('token',payload.token);
            yield put(routerRedux.push({
                pathname: '/'
            }));
        },

        * logout ({
            payload
        }, {put, select}) {
            window.sessionStorage.removeItem('token');
            window.location.href='/login';
        },
        
    },
  
    reducers: {
        updateLang (state,{payload:{value}}) {
            return state.set('i18n',value);
        },
        updateStore (state, { payload }) {
            return payload?state.mergeDeep(fromJS(payload)):initState
        },
        
    },
  
  };
  

(4) 修改layout/auth.js

import {connect} from 'dva';
import React from 'react';

const Auth=({ children,dispatch,token,locationPathname })=>{

  if(!token&&locationPathname!='/login'){
    dispatch({
      type:'app/logout'
    })
  }else if(token&&locationPathname=='/login'){
    dispatch({
      type:'app/loginOk',
      payload:{
        token:token
      }
    })
  }

  return (
    <React.Fragment>
      {children}
    </React.Fragment>
  );
}

export default connect(({
  app
})=>({
  token:app.get('token'),
  locationPathname:app.get('locationPathname'),
}))(Auth)


至此,我们看下效果:




  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Java Web上开发一个网上蛋糕商城的台,主要分为用户界面设计、交互功能实现和数据展示等几个关键部分。以下是开发的一般实验思路: 1. **需求分析**: - 确定商城的主要功能,如商品浏览、搜索、购物车管理、订单流程(添加、确认、支付)和用户登录注册等。 - 明确界面风格,如响应式设计以适应不同设备。 2. **界面设计**: - 使用HTML/CSS构建静态页面布局,包括导航栏、轮播图、商品列表页、详情页和用户个人中心等。 - 利用Bootstrap或其他端框架进行快速样式搭建。 3. **JavaScript或jQuery**: - 实现动态效果,如图片轮播、下拉菜单、按钮点击事件处理等。 - 利用AJAX异步加载数据,提升用户体验,避免页面刷新。 4. **UI组件化**: - 使用端框架提供的组件库,比如React、Vue或Angular,提高开发效率并保证代码复用。 5. **用户认证与权限管理**: - 使用session或JWT进行用户身份验证,确保用户只能访问自己的数据。 - 设计用户权限系统,如管理员能查看和修改所有数据,普通用户只能查看和购买。 6. **数据绑定和模型驱动**: - 如果使用MVVM架构,如Angular,利用双向数据绑定连接视图和模型。 7. **端状态管理**: - 使用Redux或Vuex管理应用状态,保证状态的一致性和可预测性。 8. **SEO优化**: - 为搜索引擎优化考虑,确保页面元素有正确的元信息和结构。 9. **性能优化**: - 压缩和合并CSS/JS文件,减少HTTP请求。 - 利用CDN加速资源加载。 10. **测试**: - 编写单元测试和集成测试,确保各个功能正常工作。 - 进行用户验收测试,收集用户反馈。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值