vue-ele-node-实战-demo-笔记①

打开别人项目:安装依赖,有两个依赖环境,同时运行前后端两个服务

npm install
cd client
npm install
npm run dev

项目开始:
在项目中创建文件夹node-demo\node-app
在node-app 中使用cmd: npm init, 操作如下

package name: (app)
version: (1.0.0)
description: restful api
entry point: (index.js) server.js
test command:
git repository:
keywords:
author: yyz
license: (ISC)
About to write to H:\web\vue-ele-demo\node-demo\node-app\package.json:

{
  "name": "app",
  "version": "1.0.0",
  "description": "restful api",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "yyz",
  "license": "ISC"
}


Is this OK? (yes)

拥有package.js 在cmd 输入 code . 进入VScode
使用VSCODE的终端,新建server.js,安装express

npm install express

出现node model 文件夹说明装好了

server.js配置

// 引入express
const express = require("express")
// 实例化app
const app = express()
// 设置端口号
const port = process.env.PORT || 5000

app.listen(port, () =>{
    console.log(`Server is runing on port ${port}`)
})

// 设置路由, 找到根路径
app.get("/", (req, res) =>{
    res.send('Hello world')
})

node server.js 运行nodejs

npm install nodemon -g 安装nodemon
如果安装了好了 使用 nodemon server.js 启动
作用是热启动,修改文件会自动重启node

修改package.json

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js",
    "server": "nodemon server.js"
  }

npm run start 线上用的
npm run server 启动nodemon本地测试用

localhost:5000 可以打开node本地服务器

远程连接mongoDB
官网
注册账号
847007730@qq.com
密码是长的那个

创建project 创建cluster
cluster创建好后 添加user
root 密码短的
最后connect
拷贝connect String

回到项目终端 安装mongodb
npm install mongoose
npm install mongoose -save

回到server.js 引入mongoose,连接mgdb

const mongoose = require("mongoose")

// mongoose 连接
mongoose
  .connect(
    "mongodb+srv://root:as123456@cluster0-sibgs.mongodb.net/test?retryWrites=true&w=majority",
    {
      useNewUrlParser: true,
      useCreateIndex: true,
      useUnifiedTopology: true,
      useFindAndModify: false
    }
  )
  .then(() => {
    console.log("mongoDB has been connected");
  })
  .catch(err => console.log(err));

为便于管理,创建config文件夹,里面新建key.js

// 让外部能正常引入
// 让外部能正常引入
module.exports = {
  mongoURL:
    "mongodb+srv://root:as123456@cluster0-sibgs.mongodb.net/test?retryWrites=true&w=majority"
};

回到server.js

// 引入配置文件
const dbURL = require("./config/key").mongoURL

URL string改成dbURL

// mongoose 连接
mongoose
  .connect(
    // 可以简化了
    dbURL,
    {
      useNewUrlParser: true,
      useCreateIndex: true,
      useUnifiedTopology: true,
      useFindAndModify: false
    }
  )
  .then(() => {
    console.log("mongoDB has been connected");
  })
  .catch(err => console.log(err));

在mongodb cluster 里的 collection能看到你存储的数据

实现接口,新建两个文件夹 routers/api api下创建users.js用于login和register

user.js hello world , 注意module.exports = router;要放到最后

// login and register
const express = require("express");
const router = express.Router();

/* test 接口
 * $route GET api/user/test
 * @desc 返回请求的数据
 * @access public
 */
rou
ter.get('/test', (req, res) =>{
    res.json({
        msg: 'it works'
    })
})

module.exports = router;

server.js 引入users

// 引入users.js
const users = require('./routers/api/users')

// 使用router
app.use("/api/users", users)

访问localhost:5000/api/users就会调用users
localhost:5000/api/users/test 就会返回{“msg”:“it works”}

创建models文件,里面创建User.js

// 引入mongoose 因为要存进mongoose里去
const mongoose = require("mongoose");

const Schema = mongoose.Schema;

// Create Schema
const UserSchema = new Schema({
  // 需要什么字段就写什么
  name: {
    type: String,
    required: true
  },
  password: {
    type: String,
    required: true
  },
  email: {
    type: String,
    required: true
  },
  avatar: {
    type: String,
  },
  date:{
      type: Date,
      // 默认时间就是当前时间,不用取处理时间了
      default:Date.now
  }
});
// 创建模型 继承UserSchema
module.exports = User = mongoose.model("users", UserSchema)

搭建注册接口,为了得到post结果,安装npm install body-parser

在server.js引入body-parser和加入两行添加中间件

const bodyParser = require("body-parser")

加入两行,注意要放在router前面!!

// 使用body-parser中间件
app.use(bodyParser.urlencoded({extended:false}))
app.use(bodyParser.json())

返回api/users.js,引入model

// 引入model
const Users = require("../../models/User")

写注册接口

/* 注册 接口
 * $route Post api/users/register
 * @desc 用户注册
 * @access public
 */
router.post('/register', (req, res) => {
    // 查询数据库中是否拥有邮箱
    Users.findOne({ email: req.body.email }).then(user => {
        // 如果能查到就返回一个user
        // 如果邮箱已经注册,返回错误信息
        if (user) {
            return res.status(400).json({
                msg: '邮箱已被注册'
            })
        }else{
            // 创建一个User
            const newUser = new User({
                name: req.body.name,
                email: req.body.email,
                avatar,
                password: req.body.password
            })
            
        }
    })
})

密码要加密,安装npm install bcrypt-nodejs
在users.js引入

const bcrypt = require('bcrypt-nodejs')

写完注册接口

router.post('/register', (req, res) => {
    // 查询数据库中是否拥有邮箱
    Users.findOne({ email: req.body.email }).then(user => {
        // 如果能查到就返回一个user
        // 如果邮箱已经注册,返回错误信息
        if (user) {
            return res.status(400).json({
                msg: '邮箱已被注册'
            })
        } else {
            // 创建一个User
            const newUser = new User({
                name: req.body.name,
                email: req.body.email,
            
                password: req.body.password
            })

            // bcrypt加密
            bcrypt.genSalt(10, function(err, salt) {
                bcrypt.hash(newUser.password, salt, null, (err, hash) => {
                    if (err) throw err
                    newUser.password = hash
                    // 保存user,成功返回user json,失败catch
                    newUser.save().then(user => {
                        res.json(user).catch(err => {
                            console.log.err
                        })
                    })
                })
            })
        }
    })
})

查API点这里,bcrypt的hash新版变成4个参数了

去postman那里测试,测试成功

去mongodb查看collection保存的数据,官网

第三方库 gravatar 安装npm i gravatar

在要使用的users.js里面 引入gravatar


// 引入gravatar
var gravatar = require('gravatar');

const avatar = gravatar.url 添加到注册接口中

router.post('/register', (req, res) => {
    // 查询数据库中是否拥有邮箱
    Users.findOne({ email: req.body.email }).then(user => {
        // 如果能查到就返回一个user
        // 如果邮箱已经注册,返回错误信息
        if (user) {
            return res
                .status(400)
                .json({
                    msg: '邮箱已被注册'
                })
                .catch(err => {
                    console.log(err)
                })
        } else {
            // 创建一个User
            // mm是有一个头像
            const avatar = gravatar.url(req.body.email, {s: '200', r: 'pg', d: 'mm'});

            const newUser = new User({
                name: req.body.name,
                email: req.body.email,
                avatar,
                password: req.body.password
            })

            // bcrypt加密
            bcrypt.genSalt(10, function(err, salt) {
                bcrypt.hash(newUser.password, salt, null, (err, hash) => {
                    if (err) throw err
                    newUser.password = hash
                    // 保存user,成功返回user json,失败catch
                    newUser.save().then(user => {
                        res.json(user).catch(err => {
                            console.log(err)
                        })
                    })
                })
            })
        }
    })
})

去postman测试,会返回一个avatar会返回一个url,效果图
gravatar官网,去注册一个账号 邮箱 yyz159756@163.com 用户名 yangyizhou 密码 长的,修改gravatar网站的头像可以返回你的上传的头像,但是我们只是要个他们的默认头像

写登录接口,

/* 登录 接口
 * $route Post api/users/login
 * @desc 返回token jwt passport
 * @access public
 */
router.post('/login', (req, res) => {
    const email = req.body.email
    const password = req.body.password
    // 查询数据库,会返回user
    Users.findOne({ email }).then(user => {
        // 如果user不存在
        if (!user) {
            return res.status(404).json({
                code: 404,
                msg: '用户不存在'
            })
        }

        // bcrypt密码匹配
        bcrypt.compare(password, user.password, function(err, isMatch) {
            if (isMatch) {
                return res.json({
                    code: 200,
                    msg: 'success'
                })
            } else {
                return res.status(400).json({
                    code: 901,
                    msg: '密码错误'
                })
            }
        })
    })
})

因为bcrypt用的是bcryptnodejs的,所以要用function这种写法,注意function的参数不要用res,会覆盖返回json的res

实现token,使用jsonwebtoken, npm install jsonwebtoken

在要用的地方引入依赖

const jwt = require('jsonwebtoken')

在key.js中加入secret

module.exports = {
    mongoURL:
        'mongodb+srv://root:as123456@cluster0-sibgs.mongodb.net/test?retryWrites=true&w=majority',
    secretOrKey: 'secret'
}

再要用的地方加入引入

const keys = require("../../config/key");

token的获取方式

if (isMatch) {
                // 获取token
                // 定义规则
                const rule = { id: user.id, name: user.name }
                jwt.sign(
                    rule,
                    keys.secretOrKey,
                    { expiresIn: 3600 },
                    (err, token) => {
                        res.json({
                            code: 200,
                            token: token
                        })
                    }
                )
            }

完整登录接口

/* 登录 接口
 * $route Post api/users/login
 * @desc 返回token jwt passport
 * @access public
 */
router.post('/login', (req, res) => {
    const email = req.body.email
    const password = req.body.password
    // 查询数据库,会返回user
    Users.findOne({ email }).then(user => {
        // 如果user不存在
        if (!user) {
            return res.status(404).json({
                code: 404,
                msg: '用户不存在'
            })
        }

        // bcrypt密码匹配
        bcrypt.compare(password, user.password, function(err, isMatch) {
            if (isMatch) {
                // 获取token
                // 定义规则
                const rule = { id: user.id, name: user.name }
                jwt.sign(
                    rule,
                    keys.secretOrKey,
                    { expiresIn: 3600 },
                    (err, token) => {
                        res.json({
                            code: 200,
                            token: token
                        })
                    }
                )
            } else {
                return res.status(901).json({
                    code: 901,
                    msg: '密码错误'
                })
            }
        })
    })
})

获取用户信息,安装passport,
npm install passport-jwt,
npm install passport,
npm install passport-jwt passport

在server.js中引入passport

const passport = require('passport')
// passport 初始化
app.use(passport.initialize())

在config文件夹下新建passport.js
server.js代码在初始化下面写,把passport作为参数传递给/config/passport.js

require("./config/passport")(passport)

config/passport.js配置信息

const JwtStrategy = require('passport-jwt').Strategy
const ExtractJwt = require('passport-jwt').ExtractJwt

const mongoose = require('mongoose')
// 要用User里面的users
const User = mongoose.model('users')
const keys = require('../config/key').secretOrKey

const opts = {}
// 配置信息 调用jwtFromRequest验证
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken()
opts.secretOrKey = keys

// 函数引进
module.exports = passport => {
    //这个passport是server.js那里传进来的
    passport.use(
        new JwtStrategy(opts, (jwt_payload, done) => {
            console.log(jwt_payload)
        })
    )
}

在api/user.js引入passport

const passport = require("passport")

将current接口改写成,加入验证方式

router.get(
    '/current',
    passport.authenticate('jwt', { session: false }),
    (req, res) => {
        
    }
)

用postman测试,发现不能通过验证的,
回到获取token那里,token前面上Bearer 字符串,才能验证通过

jwt.sign(
                    rule,
                    keys.secretOrKey,
                    { expiresIn: 3600 },
                    (err, token) => {
                        res.json({
                            code: 200,
                            token: "Bearer " + token
                        })
                    }
                )

在这里插入图片描述
get,注意参数Authorization在Header加。

如果能终端能获取到信息,那么说明成功了
回到passport.js 修改JwtStrategy

module.exports = passport => {
    //这个passport是server.js那里传进来的
    passport.use(
        new JwtStrategy(opts, (jwt_payload, done) => {
            // 能显示出来就行了
            // console.log(jwt_payload)
            User.findById(jwt_payload.id)
                .then(user => {
                    // 如果用户存在,把user返回去
                    if (user) {
                        return done(null, user)
                    } else {
                        return done(null, false)
                    }
                })
                .catch(err => {
                    console.log(err)
                })
        })
    )
}

获取用户信息接口,调成返回json

/* 获取用户信息 接口
 * 验证token
 * $route GET api/users/current
 * @desc return current user info
 * @access Private
 */
router.get(
    '/current',
    passport.authenticate('jwt', { session: false }),
    (req, res) => {
        // 通过验证返回用户信息
        res.json({
            id: req.user.id,
            name: req.user.name,
            email: req.user.email
        })
    }
)

添加身份字段,修改User.js的Schema添加identity

// Create Schema
const UserSchema = new Schema({
    // 需要什么字段就写什么
    name: {
        type: String,
        required: true
    },
    password: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true
    },
    avatar: {
        type: String
    },
    date: {
        type: Date,
        // 默认时间就是当前时间,不用取处理时间了
        defa,ult: Date.now
    },
    identity:{
        type: String
    }
})

回到user.js, 在newUser的时候要多添加一个字段

const newUser = new User({
                name: req.body.name,
                email: req.body.email,
                avatar,
                password: req.body.password,
                identity: req.body.identity
            })

我们还希望token能存储多一点数据,修改规则

const rule = {
                    id: user.id,
                    name: user.name,
                    avatar: user.avatar,
                    identity: user.identity 
                    
                }

最后获取用户信息加上 identity

// 通过验证返回用户信息
        res.json({
            code: 200,
            msg: {
                id: req.user.id,
                name: req.user.name,
                email: req.user.email,
                identity: req.user.identity
            }
        })

然后重新注册,postman进行验证

登陆注册接口搞定~!

接下来是数据的CRUD
model文件夹下新建Profiles.js

// 引入mongoose 因为要存进mongoose里去
const mongoose = require('mongoose')

const Schema = mongoose.Schema

// Create Schema
const ProfileSchema = new Schema({
    // 需要什么字段就写什么
    type: {
        type: String
    },
    describe: {
        type: String
    },
    incode: {
        type: String,
        required: true
    },
    expend: {
        type: String,
        required: true
    },
    cash: {
        type: String,
        required: true
    },
    remark: {
        type: String
    },
    date: {
        type: Date,
        default: Date.now
    }
})
// 创建模型 继承UserSchema
module.exports = Profile = mongoose.model('profile', ProfileSchema)

api下面创建profiles.js,写上一行,不然在server.js用不了

module.exports = router

回到server.js进行配置

// 引入profiles.js
const profiles = require('./routers/api/profiles')
// 使用router
app.use('/api/profiles', profiles)

回到profies.js 设置要用到的引入

// login and register
const express = require('express')
const router = express.Router()
const passport = require('passport')
const Profile = require('../../models/Profiles')
 
/* test 接口
 * $route GET api/users/test
 * @desc 返回请求的数据
 * @access public
 */
router.get('/test', (req, res) => {
    res.json({
        msg: 'it works'
    })
})

module.exports = router

创建信息接口

/* 创建信息 接口
 * $route POST api/profiles/add
 * @desc 创建信息
 * @access Private
 */
router.post(
    '/add',
    passport.authenticate('jwt', { session: false }),
    (req, res) => {
        const profileFields = {}
        if (req.body.type) profileFields.type = req.body.type
        if (req.body.describe) profileFields.describe = req.body.describe
        if (req.body.income) profileFields.income = req.body.income
        if (req.body.expend) profileFields.expend = req.body.expend
        if (req.body.cash) profileFields.cash = req.body.cash
        if (req.body.remark) profileFields.remark = req.body.remark
        // 新建profile对象保存
        new Profile(profileFields).save()
        .then(profile =>{
            // 如果存成功返回json
            res.json(profile)
        })
    }
)

获取所有信息接口

/* 获取所有信息 接口
 * $route GET api/profiles
 * @desc 获取所有信息
 * @access Private
 */
router.get(
    '/',
    passport.authenticate('jwt', { session: false }),
    (req, res) => {
        Profile.find()
            .then(profiles => {
                if (!profiles) {
                    return res.status(902).json({
                        msg: '没有profile内容'
                    })
                } else {
                    res.json(profiles)
                }
            })
            .catch(err => {
                console.log(err)
                res.status(404).json(err)
            })
    }
)

获取单个信息接口

/* 获取单个信息 接口
 * $route GET api/profiles/:id
 * @desc 获取单个信息
 * @access Private
 */
router.get(
    '/:id',
    passport.authenticate('jwt', { session: false }),
    (req, res) => {
        Profile.findOne({_id: req.params.id})
            .then(profiles => {
                if (!profiles) {
                    return res.status(902).json({
                        msg: '没有profile内容'
                    })
                } else {
                    res.json(profiles)
                }
            })
            .catch(err => {
                console.log(err)
                res.status(404).json(err)
            })
    }
)

编辑接口

/* 编辑信息 接口
 * $route POST api/profiles/edit
 * @desc 编辑信息
 * @access Private
 */
router.post(
    '/edit/:id',
    passport.authenticate('jwt', { session: false }),
    (req, res) => {
        const profileFields = {}
        if (req.body.type) profileFields.type = req.body.type
        if (req.body.describe) profileFields.describe = req.body.describe
        if (req.body.income) profileFields.income = req.body.income
        if (req.body.expend) profileFields.expend = req.body.expend
        if (req.body.cash) profileFields.cash = req.body.cash
        if (req.body.remark) profileFields.remark = req.body.remark
        // 查找id
        Profile.findOneAndUpdate(
            {
                _id: req.params.id
            },
            {
                $set: profileFields
            },
            {
                new: true
            }
        ).then(profile => {
            res.json(profile)
        })
    }
)

删除接口


/* 删除信息 接口
 * $route POST api/profiles/delete/:id
 * @desc 删除信息
 * @access Private
 */
router.delete(
    '/delete/:id',
    passport.authenticate('jwt', { session: false }),
    (req, res) => {
        Profile.findOneAndRemove({
            _id: req.params.id
        })
            .then(profile => {
                res.status(200).json({
                    msg: '删除成功',
                    body: profile
                })
            })
            .catch(err => res.status(404).json('删除失败'))
    }
)

搭建前端
回到项目总根目录(就是之前操作的根目录) 打开终端,vue create client, 安装Router,vuex
前后端连载, 在根目录安装npm install concurrently, 用于将多个不同的vue项目启动绑定到一个终端 ,首先修改client的package.js 加入start的启动 主要是启动serve

"scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "start": "npm run serve"
  }

修改根项目的package.js,加入三个

"scripts": {
        "client-install": "npm install --prefix client",
        "client": "npm start --prefix client",
        "start": "node server.js",
        "server": "nodemon server.js""dev": "concurrently \"npm run server\"  \"npm run client\""
    }

client-install表示的是依赖模块
"client"表示启动client项目的start
npm ru ndev 就可以开启绑定的指令

正式进入client项目,删除assert,components,views的东西
在views文件夹下面创建index.vue

<template>
    <div class="index">
        初始化
    </div>
</template>

<script>
export default {
    name: 'index',
    components: {}
};
</script>

<style scoped></style>

router下的index.js引入

import Index from './views/Index.vue'

在配置router

routes = [
  {
    path: '/',
    redirect: '/index',
  },
  {
    path: '/index',
    name: 'index',
    components: Index
  }
]

然后写好404.vue,Register.vue,配置router

404.vue

<template>
    <div class="notfound">
        <img src="../assets/404.gif" alt="">
    </div>
</template>

<script>
export default {
    name: 'not-found',
    component: {}
};
</script>

<style scoped>
.notfound{
    width: 100%;
    height: 100%;
    overflow: hidden;
}
.notfound img{
    width: 100%;
    height: 100%;
}
</style>

Register.vue

<template>
    <div class="register">
        <!-- <section> 标签定义文档中的节(section、区段)。
            比如章节、页眉、页脚或文档中的其他部分。 -->
        <section class="form_container">
            <div class="manage_tip">
                <span class="title" style="">demo</span>
            </div>
        </section>
    </div>
</template>

<script>
export default {
    name: 'register',
    component: {}
};
</script>

<style scoped>
.register {
    position: relative;
    width: 100%;
    height: 100%;
    background: url(../assets/bg.jpg) no-repeat center center;
    background-size: 100% 100%;
}
.form_container {
    width: 370px;
    height: 210px;
    position: relative;
    top: 10%;
    margin:0 auto;
    padding: 25px;
    border-radius: 5px;
    text-align: center;
}
.form_container .manage_tip .title {
    font-family: 'Microsoft YaHei';
    font-weight: bold;
    font-size: 26px;
    text-align: center;
    color: #fff;
}
.registerForm {
    margin-top: 20px;
    background-color: #fff;
    padding: 20px 40px 20px 20px;
    border-radius: 5px;
    box-shadow: 0px 5px 10px #cccc;
}

.submit_btn {
    width: 100%;
}
</style>

配置Router

import Vue from 'vue'
import Router from 'vue-router'
import Index from '../views/Index.vue'
import Register from '../views/Register'
import NotFound from '../views/404'


Vue.use(Router)

const router = new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    { path: '/', redirect: '/index' },
    { path: '/index', component: Index},
    { path: '/register', name: 'register', component: Register },
    { path: '*', name: '/404', component: NotFound}
  ]
})


export default router;```

应用elementUI,[官网](https://element.eleme.cn/#/)
感兴趣可以去看一下布局

比如我们要用这个注册自定义验证
先把HTML贴过来

```html
				<el-form
                    :model="ruleForm"
                    status-icon
                    :rules="rules"
                    ref="ruleForm"
                    label-width="100px"
                    class="demo-ruleForm"
                >
                    <el-form-item label="密码" prop="pass">
                        <el-input
                            type="password"
                            v-model="ruleForm.pass"
                            autocomplete="off"
                        ></el-input>
                    </el-form-item>

                    <el-form-item>
                        <el-button @click="resetForm('ruleForm')"
                            >注册</el-button
                        >
                    </el-form-item>
                </el-form>

修改form表单里的:model :rules等属性

:model="registerUser"

在改其他组件里面的属性如 label prop

修改好后form表单

				<el-form
                    :model="registerUser"
                    :rules="rules"
                    ref="registerForm"
                    label-width="80px"
                    class="registerForm"
                >
                    <el-form-item label="用户名" prop="name">
                        <el-input
                            v-model="registerUser.name"
                            placeholder="请输入用户名"
                        ></el-input>
                    </el-form-item>

                    <el-form-item label="邮箱" prop="email">
                        <el-input
                            v-model="registerUser.email"
                            placeholder="请输入email"
                        ></el-input>
                    </el-form-item>

                    <el-form-item label="密码" prop="password">
                        <el-input
                            type="password"
                            v-model="registerUser.password"
                            placeholder="请输入密码"
                        ></el-input>
                    </el-form-item>

                    <el-form-item label="确认密码" prop="password2">
                        <el-input
                            type="password"
                            v-model="registerUser.password2"
                            placeholder="请确认密码"
                        ></el-input>
                    </el-form-item>

                    <el-form-item label="选择身份">
                        <el-select
                            v-model="registerUser.identity"
                            aria-placeholder="请选择身份"
                        >

                            <el-option label="管理员" value="manage" />
                            <el-option label="员工" value="employee" />

                        </el-select>
                    </el-form-item>

                    <el-form-item>
                        <el-button 
                            type="primary" class="submit_btn"
                            @click="submitForm('registerForm')"
                            >注册</el-button
                        >
                    </el-form-item>
                </el-form>

现在是显示不了的,因为Property or method no defined,

写上属性

export default {
    name: 'register',
    component: {},
    data() {
        return {
            registerUser: {
                name: '',
                email: '',
                password: '',
                password2: '',
                identity: ''
            }
        };
    },
    methods: {}
};

还差methods和rules没写

写rules

	data() {
        return {
            registerUser: {
                name: '',
                email: '',
                password: '',
                password2: '',
                identity: ''
            },
            rules: {
                name: [
                    {
                        required: true,
                        message: '用户名不能为空',
                        trigger: 'blur'
                    },
                    {
                        min: 2,
                        max: 30,
                        message: '长度在2到30之间',
                        trigger: 'blur'
                    }
                ],
                email: [
                    {
                        type: "email",
                        required: true,
                        message: '邮箱格式不正确',
                        trigger: 'blur'
                    }
                ],
                password: [
                    {
                        required: true,
                        message: '密码不能为空',
                        trigger: 'blur'
                    }
                ],
                password2: [
                    {
                        required: true,
                        message: '密码不能为空',
                        trigger: 'blur'
                    }
                ]
            }
        };
    },

在data里return之上自定义验证函数

		var validatePass2 = (rule, value, callback) => {
            if (value !== this.registerUser.password) {
                callback(new Error('两次输入密码不一致!'));
            } else {
                callback();
            }
        };

在return下面的rules里的password2里加上validator

			password2: [
                    {
                        required: true,
                        message: '确认密码不能为空',
                        trigger: 'blur'
                    },
                    {
                        validator:validatePass2
                    }
                ]
            }

验证写完了,写加载效果
去ele官网查 loading加载

点击注册还要有axios请求,所以在client下面 npm i aixos

在src下面新建http.js文件,在里面引入axios

import axios from 'axios'


// 请求拦截


// 响应拦截

export default axios;

main.js中也要引入axios

import axios from './http'

同时引入全局

Vue.prototype.$axios = axios;

Loading 还可以以服务的方式调用。回到http.js 引入 Loading 服务

import { Loading } from 'element-ui';

写loading方法

let loading;
function startLoading(){
    loading = Loading.service({
        lock: true,
        text: '拼命加载中',
        background: 'rgba(0,0,0,7)'
    });
}

function endLoading(){
    loading.close();
}

引入消息提示

import { Message } from 'element-ui';

http.js

import axios from 'axios'
import { Loading } from 'element-ui';
import { Message } from 'element-ui';

let loading;
function startLoading(){
    loading = Loading.service({
        lock: true,
        text: '拼命加载中',
        background: 'rgba(0,0,0,7)'
    });
}

function endLoading(){
    loading.close();
}

// 请求拦截
axios.interceptors.request.use(config => {
    //加载动画
    startLoading();
    return config;
}, error => {
    return Promise.reject(error)
})

// 响应拦截

export default axios;  
axios.interceptors.response.use(response => {
    //结束加载动画
    endLoading();
    return response;

}, error => {
    endLoading();
    Message.error(error.response.data);
    return Promise.reject(error)
})

前端配置跨域请求,在client文件下面创建vue.config.js
配置跨域请求

const path = require('path')
const debug = process.env.NODE_ENV !== 'production'

module.exports = {
    publicPath: '/', // 根域上下文目录
    outputDir: 'dist', // 构建输出目录
    assetsDir: 'assets', // 静态资源目录 (js, css, img, fonts)
    lintOnSave: false, // 是否开启eslint保存检测,有效值:ture | false | 'error'
    runtimeCompiler: true, // 运行时版本是否需要编译
    transpileDependencies: [], // 默认babel-loader忽略mode_modules,这里可增加例外的依赖包名
    productionSourceMap: true, // 是否在构建生产包时生成 sourceMap 文件,false将提高构建速度
    configureWebpack: config => { // webpack配置,值位对象时会合并配置,为方法时会改写配置
        if (debug) { // 开发环境配置
            config.devtool = 'cheap-module-eval-source-map'
        } else { // 生产环境配置
        }
        // Object.assign(config, { // 开发生产共同配置
        //     resolve: {
        //         alias: {
        //             '@': path.resolve(__dirname, './src'),
        //             '@c': path.resolve(__dirname, './src/components'),
        //             'vue$': 'vue/dist/vue.esm.js'
        //         }
        //     }
        // })
    },
    chainWebpack: config => { // webpack链接API,用于生成和修改webapck配置,https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
        if (debug) {
            // 本地开发配置
        } else {
            // 生产开发配置
        }
    },
    parallel: require('os').cpus().length > 1, // 构建时开启多进程处理babel编译
    pluginOptions: { // 第三方插件配置
    },
    pwa: { // 单页插件相关配置 https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-pwa
    },
    devServer: {
        open: true,
        host: 'localhost',
        port: 8081,
        https: false,
        hotOnly: false,
        proxy: { // 配置跨域
            '/api': {
                target: 'http://localhost:5000/api/',
                ws: true,
                changOrigin: true,
                pathRewrite: {
                    '^/api': ''
                }
            }
        },
        before: app => { }
    }
}

target那里改请求地址,记得要重启

回到Register.vue
写axios请求

		submitForm(formName) {
            this.$refs[formName].validate(valid => {
                if (valid) {
                    this.$axios
                        .post('/api/users/register', this.registerUser)
                        .then(res => {
                            // 注册成功
                            this.$message({
                                message: '注册成功!',
                                type: 'success'
                            });
                            this.$router.push('/login');
                        });
                } else {
                    console.log('error submit!!');
                    return false;
                }
            });
        }

测试,成功!

写登录页面 Login.vue,配置router

<template>
    <div class="login">
        <!-- <section> 标签定义文档中的节(section、区段)。
            比如章节、页眉、页脚或文档中的其他部分。 -->
        <section class="form_container">
            <div class="manage_tip">
                <span class="title" style="">demo</span>
                <el-form
                    :model="loginUser"
                    :rules="rules"
                    ref="loginForm"
                    label-width="60px"
                    class="loginForm"
                >
                    <el-form-item label="邮箱" prop="email">
                        <el-input
                            type="email"
                            v-model="loginUser.email"
                            placeholder="请输入email"
                        ></el-input>
                    </el-form-item>

                    <el-form-item label="密码" prop="password">
                        <el-input
                            type="password"
                            v-model="loginUser.password"
                            placeholder="请输入密码"
                        ></el-input>
                    </el-form-item>

                    <el-form-item>
                        <el-button
                            type="primary"
                            class="submit_btn"
                            @click="submitForm('loginForm')"
                            >登陆</el-button
                        >
                    </el-form-item>

                    <div class="tiparea">
                        <p>
                            还有没有账号?现在<router-link to="/register"
                                >注册</router-link
                            >
                        </p>
                    </div>
                </el-form>
            </div>
        </section>
    </div>
</template>

<script>
export default {
    name: 'login',
    component: {},
    data() {
        return {
            loginUser: {
                email: '',
                password: ''
            },
            rules: {
                email: [
                    {
                        type: 'email',
                        required: true,
                        message: '邮箱格式不正确',
                        trigger: 'blur'
                    }
                ],
                password: [
                    {
                        required: true,
                        message: '密码不能为空',
                        trigger: 'blur'
                    }
                ]
            }
        };
    },
    methods: {
        submitForm(formName) {
            this.$refs[formName].validate(valid => {
                if (valid) {
                    this.$axios
                        .post('/api/users/login', this.loginUser)
                        .then(res => {
                            // 登录成功
                            this.$message({
                                message: '登录成功!',
                                type: 'success'
                            });
                            //获取token
                            const { token } = res.data;
                            // 存储到ls
                            localStorage.setItem('eleToken', token);

                            this.$router.push('/login');
                        })
                        .catch(error => {
                            // 如果错误 打印处错误信息
                            console.log(error.response);
                        });
                } else {
                    console.log('error submit!!');
                    return false;
                }
            });
        }
    }
};
</script>

<style scoped>
.login {
    position: relative;
    width: 100%;
    height: 100%;
    background: url(../assets/bg.jpg) no-repeat center center;
    background-size: 100% 100%;
}
.form_container {
    width: 370px;
    height: 210px;
    position: relative;
    top: 10%;
    margin: 0 auto;
    padding: 25px;
    border-radius: 5px;
    text-align: center;
}
.form_container .manage_tip .title {
    font-family: 'Microsoft YaHei';
    font-weight: bold;
    font-size: 26px;
    text-align: center;
    color: #fff;
}
.loginForm {
    margin-top: 20px;
    background-color: #fff;
    padding: 20px 40px 20px 20px;
    border-radius: 5px;
    box-shadow: 0px 5px 10px #cccc;
}

.submit_btn {
    width: 100%;
}

.tiparea {
  text-align: right;
  font-size: 12px;
  color: #333;
}
.tiparea p a {
  color: #409eff;
}
</style>

const {token} es6语法 把对象解析过来
loaclstorage.setItem 保存loaclstorage
F12 Applicaiton 可以查看到localstore 存的token

路由守卫,进入router.js(默认指router的配置文件了)

import Vue from 'vue';
import Router from 'vue-router';
import Index from '../views/Index.vue';
import Register from '../views/Register';
import NotFound from '../views/404';
import Login from '../views/Login';

Vue.use(Router);

const router = new Router({
    mode: 'history',
    base: process.env.BASE_URL,
    routes: [
        { path: '/', redirect: '/index' },
        { path: '/index', component: Index },
        { path: '/register', name: 'register', component: Register },
        { path: '*', name: '/404', component: NotFound },
        { path: '/login', name: 'login', component: Login }
    ]
});

//路由守卫

router.beforeEach((to, from, next) => {
    // 如果存在返回真,如果不存在返回假
    const isLogin = localStorage.eleToken ? true : false;
    // 如果访问这两个页面都可以访问,否则要进行判断
    if (to.path == '/login' || to.path == '/register') {
        next();
    }else{
        // 登陆了进行跳转,否则去登陆页面
        isLogin ? next() : next('/login')
    }
});

export default router;

配置请求拦截和响应拦截,判断token和设置请求头
去http.js 引入router,写请求拦截和响应拦截

import axios from 'axios'
import { Loading } from 'element-ui';
import { Message } from 'element-ui';
import router from './router/index'
let loading;
function startLoading(){
    loading = Loading.service({
        lock: true,
        text: '拼命加载中',
        background: 'rgba(0,0,0,7)'
    });
}

function endLoading(){
    loading.close();
}

// 请求拦截
axios.interceptors.request.use(config => {
    //加载动画
    startLoading();
    if(localStorage.eleToken){
        //设置统一的请求头
        config.headers.Authorization = localStorage.eleToken
    }
    return config;
}, error => {
    return Promise.reject(error)
})

// 响应拦截

export default axios;  
axios.interceptors.response.use(response => {
    //结束加载动画
    endLoading();
    return response;

}, error => {
    endLoading();
    Message.error(error.response.data);

    // 获取错误状态码
    const {status} = error.response;
    if(status == 401){
        Message.error("token失效,请重新登陆!")
        // 清除token
        localStorage.removeItem('eleToken')
        // 跳转到登陆页面
        router.push('/login')
    }
    return Promise.reject(error)
})

安装能够解析token的模块
npm i jwt-decode

在Login.vue引入jwt-decode, 在<script>下面

import jwt from 'jwt-decode'

可以试着解析token‘


                            const decoded = jwt_decode(token)
                            console.log(decoded)
                            

去测试一下,ok

接下来,为了设置全局变量,要用到vue的store,先配置store.js

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

const types = {
    // 是否验证通过
    SET_AUTHENTICATED: 'SET_AUTHENTICATED',
    // 设置用户
    SET_USER: 'SET_USER'
};

// 设置状态
const state = {
    // 设置是否授权
    isAuthenticated: false,
    // 存储用户信息
    user: {}
};

const getters = {
    // 获取状态
    isAuthenticated: state => state.isAuthenticated,
    user: state => state.user
};

// 更改状态信息
const mutations = {
    // 规定类型SET_AUTHENTICATED, 传递两个参数
    // 设置是否授权的方法
    [types.SET_AUTHENTICATED](state, isAuthenticated) {
        if (isAuthenticated) {
            state.isAuthenticated = isAuthenticated;
        } else {
            state.isAuthenticated = false;
        }
    },
    [types.SET_USER](state, user) {
        if (user) {
            state.user = user;
        } else {
            state.user = {};
        }
    }
};

// 异步操作actions
const actions = {
    setAuthenticated: ({commit}, isAuthenticated)=>{
        // 规定类型
        commit(types.SET_AUTHENTICATED, isAuthenticated)
    },
    setUser: ({commit}, user) => {
        // 规定类型
        commit(types.SET_USER, user)
    }


};
export default new Vuex.Store({
    state,
    getters,
    mutations,
    actions
});

为了存储全局变量,在login.vue写,调用两个方法去存取

							//获取token
                            const { token } = res.data;
                            // 存储到ls
                            localStorage.setItem('eleToken', token);
                            // 解析token
                            const decoded = jwt_decode(token);
                            // 如果不为空Authenticated就会设为真
                            this.$store.dispatch('setAuthenticated', !this.isEmpty(decoded));
                            this.$store.dispatch('setUser', decoded);

回到login.vue 将解析到的token 存储到vueX里去,
测试前需要安装vue-devtool,网上有博客,点这里查看

测试登陆的时候,会调用这两个方法去保存user和登陆状态
在这里插入图片描述
在这里插入图片描述
这样说明store写好了,但是F5刷新会消失

将Login.vue的<script>拷贝到App.vue,删掉一些没必要的和修改一些

<script>
import jwt_decode from 'jwt-decode';
export default {
    name: 'app',
    component: {},
    created() {
        // 判断当前localstorage token是否存在
        if (localStorage.eleToken) {
            // 获取到token的信息
            const decoded = jwt_decode(localStorage.eleToken);
            // 存储到vueX中
            this.$store.dispatch('setAuthenticated', !this.isEmpty(decoded));
            this.$store.dispatch('setUser', decoded);
        }
    },
    methods: {
        isEmpty(value) {
            return (
                value === undefined ||
                value === null ||
                (typeof value === 'object' &&
                    Object.keys(value).length === 0) ||
                (typeof value === 'string' && value.trim().length === 0)
            );
        }
    }
};
</script>

将script加入到App.vue中,主要目的是刷新和打开新页面的时候保存token和user信息

设计顶部导航

在component下面新建HeadNav.vue,将HeadNav.vue引入到index.vue中

<script>
import HeadNav from '../components/HeadNav';
export default {
    name: 'index',
    components: { HeadNav }
};
</script>

写HeadNva.vue
@command=“setDialoInfo” 点击会调用setDialoInfo方法,并且将组件上对应的command值作为参数传递给setDialoInfo方法

写退出方法,要去store.js写clear Actions

clearCurrentState: ({commit}) => {
        commit(types.SET_AUTHENTICATED, false)
        commit(types.SET_USER, null)

    }

然后HeadNva中调用就行了,
技术点 :下拉菜单,退出登陆

		logout() {
            // 清除token
            localStorage.removeItem('eleToken')
            // 设置vuex store
            this.$store.dispatch('clearCurrentState')
            // 跳转
            this.$router.push('/login')
        }

HeadNav.vue

<template>
    <header class="head-nav">
        <el-row>
            <el-col :span="6" class="logo-container">
                <img src="../assets/logo.png" class="logo" alt="" />
                <span class="title">后台管理demo</span>
            </el-col>

            <el-col :span="6" class="user">
                <div class="userinfo">
                    <img :src="user.avatar" class="avatar" alt="" />
                    <div class="welcome">
                        <p class="name comename">欢迎</p>
                        <p class="name avatarname">{{ user.name }}</p>
                    </div>
                    <span class="username">
                        <!-- 下拉箭头 -->
                        <el-dropdown trigger="click" @command="setDialoInfo">
                            <span class="el-dropdown-link">
                                <i
                                    class="el-icon-caret-bottom el-icon--right"
                                ></i>
                            </span>

                            <el-dropdown-menu slot="dropdown">
                                <el-dropdown-item command="info">
                                    个人信息
                                </el-dropdown-item>

                                <el-dropdown-item command="logout"
                                    >退出</el-dropdown-item
                                >
                            </el-dropdown-menu>
                        </el-dropdown>
                    </span>
                </div>
            </el-col>
        </el-row>
    </header>
</template>

<script>
export default {
    name: 'head-nav',
    data() {
        return {};
    },
    computed: {
        user() {
            return this.$store.getters.user;
        }
    },
    component: {},
    methods: {
        setDialoInfo(commandItem) {
            switch (commandItem) {
                case 'info':
                    this.showInfoList();
                    break;
                case 'logout':
                    this.logout();
                    break;
            }
        },
        showInfoList() {
            
        },
        logout() {
            // 清除token
            localStorage.removeItem('eleToken')
            // 设置vuex store
            this.$store.dispatch('clearCurrentState')
            // 跳转
            this.$router.push('/login')
        }
    }
};
</script>

<style scoped>
.head-nav {
    width: 100%;
    height: 60px;
    min-width: 600px;
    padding: 5px;
    background: #324057;
    color: #fff;
    border-bottom: 1px solid #1f2d3d;
}
.logo-container {
    line-height: 60px;
    min-width: 400px;
}
.logo {
    height: 50px;
    width: 50px;
    margin: 0 5px;
    vertical-align: middle;
    display: inline-block;
}
.title {
    vertical-align: middle;
    font-size: 22px;
    font-family: 'Microsoft YaHei';
    letter-spacing: 5px;
    margin: 0 10px;
}
.user {
    line-height: 60px;
    text-align: right;
    float: right;
    padding-right: 10px;
}
.avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    vertical-align: middle;
    display: inline-block;
}
.welcome {
    display: inline-block;
    width: auto;
    vertical-align: middle;
    padding: 0 5px;
}
.name {
    line-height: 20px;
    text-align: center;
    font-size: 14px;
}
.comename {
    font-size: 12px;
}
.avatarname {
    color: #409eff;
    font-weight: bolder;
}
.username {
    cursor: pointer;
    margin-right: 5px;
}
.el-dropdown {
    color: #fff;
}
</style>

有点卡了换一篇bolg

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值