基于Vue全家桶+Express全栈开发王者荣耀助手总结技巧

项目已上传到git 感兴趣的下伙伴欢迎下载Git连接

创建VUE项目

vue create 名字

初始化项目

npm init -y

安装nodemon

npm i -g nodemon

 "scripts": {

  "serve":"nodemon index.js",

  "test": "echo \"Error: no test specified\" && exit 1"

 },

安装element

vue add element

router

vue add router

express

导入:npm i express@next mongoose cors

"use strict";

const express = require("express");
const app = express();

// 启用跨域
app.use(require("cors")());

// 启用 json 解析
app.use(express.json());

// 连接数据库
var MongoClient = require("mongodb").MongoClient;
var url = "mongodb://localhost:27017/";

let dbexam;
MongoClient.connect(url, function (err, db) {
  if (err) throw err;
  dbexam = db.db("exam");
});

// 创建路由
const router = express.Router({ mergeParams: true });

// 路由匹配
router.post("/query", async (req, res) => {
  let stuID = req.body["stuID"];
  let stuNumber = req.body["stuNumber"];
  let queryResult;

  dbexam
    .collection("stuInfo")
    .find({ $and: [{ ksbh: stuNumber }, { zjhm: stuID }] })
    .toArray((err, result) => {
      // 返回集合中所有数据·
      if (err) throw err  ;
      queryResult = result;
      res.send(queryResult);
    });
});

// 路由挂载
app.use(router);

// 启动
app.listen(3001, () => {
  console.log("Server running at http://localhost:3001");
});
//ajax
function btnSubmit() {
  console.log("click");
  $.ajax({
    /* url 地址可以是 /get-json/ 的方式
     *  也可以是 http://www.qfedu.com/get-json/ 的方式
     */
    url: "http://localhost:3001/query",
    type: "POST",
    contentType: "application/json",
    dataType: "json",
    data: JSON.stringify({
      stuID: "152122198812156600",
      stuNumber: "104460123401042",
    }),
    success: function (res) {
      // 成功处理逻辑
      console.log(res);
    },
    error: function (res) {
      // 错误时处理逻辑
      console.log(res);
    },
  });
}

通用CRUD接口

实现类名数据格式转换inflection

npm i inflection

// 获取详情页面的接口 :id路径匹配,相当于前端传递的一个参数,即接收参数,参数名为id
//http://localhost:3000/admin/api/rest/categories中categories即为参数
可以通过req.params.resoure获取这个参数
app.use('/admin/api/rest/:resoure', router)

// 定义一个express的子路由
const router = express.Router({
    //表示合并URL参数
    mergeParams:true
})

// 挂载子路由在app对象上面,路径会自动加载为http://localhost:3000/admin/api/rest/参数
app.use('/admin/api/rest/:resoure',async(req,res,next)=>{
    //创建中间键,每次发送的url请求都会经过中间键的处理
    const modelName = require('inflection').classify(req.params.resoure)
    //将传递的参数改为首字母大写并构建成模型的地址
    req.Model = require(`../../models/${modelName}`)
    //next()表示执行下一步
    next()
}, router)

图片上传

baseURL:'http://localhost:3000/admin/api'
//全局获取baseUrl并且构造图片的上传地址url,elemen组件中upload组件
:action="$http.defaults.baseURl + '/upload'"

//托管静态文件,让__dirname + '/uploads'中的文件可以通过/uploads来访问
app.use('/uploads',express.static(__dirname + '/uploads'))

const multer = require('multer')
const upload = multer({
    //__dirname:使用绝对地址 __dirname这个文件所在的文件夹
    dest: __dirname + '/../../uploads'
})
//upload.single('file')中间键获取前端提交的file信息并且绑定到req请求对象上
app.use('/admin/api/upload',upload.single('file'),async(req,res)=>{
    const file = req.file
    file.url = `http://localhost:3000/uploads/${file.filename}`
    res.send(file)
})
//vue数据响应式处理
this.$set(this.model,'icon',res.url) //vue的显示赋值效果,用于数据一开始不存在,后面需要加上的情况

中间键的专门处理上传数据multer

npm i multer

一个model对象关联多个其他model对象

//关联数组,实现一个model对象关联多个其它对象
    categories: [{
        type: mongoose.SchemaType.ObjectId,
        ref: 'Category'
    }]

命名规范

数组库:model类名:首字母大写

前端js:变量名:首字母小写的驼峰命名,类名:首字母大写的驼峰命名

MYSQL:多个单词下划线

mongo:首字母小写的驼峰命名

避免前端定义对象被后端返回对象覆盖问题

 <el-input v-model="model.scores.difficult"></el-input>//未避免difficult未定义报错,需要在data的model对象里面加上scores空对象
//vue里面的显示赋值
this.model = Object.assign({},this.model,res.data);//保证model对象为this.model的数据加上res.data的数据,如果this.model里面有的,res.data就会覆盖,如果res.data里面没有的数据就不会影响this.madel的数据

属性赋值

this.$set(this.model,'avatar',res.url) //vue的显示赋值效果,用于数据一开始不存在,后面需要加上的情况
this.model.avatar = res.url //data里面定义了数据属性
// data为对象数组,需要修改其中一个对象的某个属性
cancelfavo(ID) {
    //向后台返回学生stuNo,取消收藏该学生
    //调用函数,重新加载页面
    for (let index = 0; index < this.data.length; index++) {
        const element = this.data[index];
        if (element.stuNo === ID) {
            this.$set(this.data[index], "isfavorites", false);
            this.cancel(element.stuNo);
            break;
        }
    }
}

数组插入删除操作

//插入
@click="model.skills.push({})"//插入空对象
//删除
@click="model.skills.splice(i,count)"//从第i个位置开始删除count个元素

富文本编辑器安装

npm install --save vue2 --editor

import { VueEditor } from "vue2-editor";//引用vue2-editor中的VueEditor子对象
import a from "vue2-editor"; //获取全部,r使用a.VuEditr


//保存图片到服务器
<vue-editor
useCustomImageHandler
    @image-added="handleImageAdded"
v-model="model.body"
></vue-editor>
methods: {
    async handleImageAdded(file, Editor, cursorLocation, resetUploader) {
        // An example of using FormData
        // NOTE: Your key could be different such as:
        // formData.append('file', file)

        const formData = new FormData();
        formData.append("file", file);
        const res = await this.$http.post('upload',formData)
        Editor.insertEmbed(cursorLocation, "image", res.data.url);
        resetUploader();
    },

全局样式引入

在main.js文件中 import ‘./style.css’ 即可全局引入style.css

根据路由路径来高亮显示

      <el-menu router :default-openeds="['1']" unique-opened :default-active="$route.path">

密码加密

安装bcrypt

npm i bcrypt

const schema = new mongoose.Schema({
    username: {
        type: String
    },
    password: {
        type: String,
        select:false,//密码散列默认查不出来,即前端无法得到密码的散列
        //用set实现对传递的数据进行处理后再存储到数据库,val代表需要处理的值 
        set(val){
            return require('bcrypt').hashSync(val,10) //生成参数的散列,实现加密,后一个指数越高越耗时,10到12即可,不可列散列,绝对唯一性
        }
    }
})

响应拦截器(http)

// 给http整个请求加一个拦截器response,会拦截处理所有返回服务器的内容,如果有错进行相应的处理,响应拦截器
http.interceptors.response.use(res => {
    return res
}, err => {
    if (err.response.data.message) {
        // Vue.prototype.$message.error(err.response.data.message)
        Vue.prototype.$message({
            type: 'error',
            message: err.response.data.message
        })
    }
    return Promise.reject(err)
})

登录验证

安装jsonwebtoken

npm i jsonwebtoken

安装http-assert

npm install http-assert

var assert = require('http-assert')
var ok = require('assert')
 
var username = 'foobar' // username from request
 
try {
    // assert(判断条件, 如果不对返回状态码, '返回信息') 会抛出异常
  assert(username === 'fjodor', 401, 'authentication failed')
} catch (err) {
  ok(err.status === 401)
  ok(err.message === 'authentication failed')
  ok(err.expose)
}


 // assert(判断条件, 如果不对返回状态码, '返回信息') 会抛出异常 在全局捕获错误加中间件处理
app.use(async(err,req,res,next)=>{
    res.status(err.statusCode).send({
        message:err.message
    })
})

Vue全局绑定mixin

//全局绑定mixin,相当于为每一个Vue组件绑定了一个方法(组件)
Vue.mixin({
  computed:{
    uploadUrl(){
      return this.$http.defaults.baseURL + '/upload'
    }
  },
  methods:{
    getAuthHeaders(){
      return{
       Authorization:`Bearer ${localStorage.token || ''}` 
      }
    }
  }
})

HTTP拦截器

import axios from 'axios'
import Vue from 'vue'
import router from './router'

const http = axios.create({
    baseURL: 'http://localhost:3000/admin/api'
})
// 给http整个请求加一个拦截器response,会拦截处理所有返回服务器的内容,如果有错进行相应的处理,响应拦截器
http.interceptors.response.use(res => {
    return res
}, err => {
    if (err.response.data.message) {
        // Vue.prototype.$message.error(err.response.data.message)
        Vue.prototype.$message({
            type: 'error',
            message: err.response.data.message
        })
        if(err.response.status === 401){
            router.push('/login')
        }
    }
    return Promise.reject(err)
})

//给http整个请求加一个拦截器request,会拦截处理所有请求,进行相应的处理,请求拦截器
http.interceptors.request.use(function (config) {
    if (localStorage.token) {
        config.headers.Authorization = 'Bearer ' + (localStorage.token)
    }
    return config
}, function (error) {
    return Promise.reject(error)
})
export default http

路由导航守卫(router)

{
    path: '/login',
    name: 'Login',
    component: Login,
    // 设置路由源信息,是否需要路由验证
    meta:{
      isPublic:true
    }
  },
      
//路由导航守卫
router.beforeEach((to,from,next)=>{
  if(!to.meta.isPublic && !localStorage.token){
    return next('/login')
  }
  next()
})
//父子路由
{
    path: '/',
    name: 'Main',
    component: Main,
    children: [{
        path: "/categories/create",
        component: CategoryEdit
      },]

index路由分配

const AdminUser = require('../../models/AdminUser')

// 导出一个函数,这个函数接收一个app对象,在函数里面就可以用接收的对象
module.exports = app => {
    const express = require('express')
    const jwt = require('jsonwebtoken')
    const AdminUser = require('../../models/AdminUser')
    const assert = require('http-assert')
    // 引入req.Model模型
    // const req.Model = require('../../models/req.Model')

    // 定义一个express的子路由
    const router = express.Router({
        //表示合并URL参数
        mergeParams: true
    })
    // 登录检验中间键
    const authMiddleware = require('../../middleware/auth')

    //资源中间键
    const resourceMiddleware = require('../../middleware/resource')

    // post路径为:http://localhost:3000/admin/api/ 创建资源
    router.post('/', async (req, res) => {
        // 客户端提交的内容中的body为需要构建模型的内容,需要用await转换为同步
        const model = await req.Model.create(req.body)
        // 返回创建的model,req是客户端对象,res是服务端对象
        res.send(model)
    })

    //删除资源
    router.delete('/:id', async (req, res) => {
        // params为提交的URl,可获得其中的内容,findByIdAndUpdate(更具什么来查,更新的内容)
        await req.Model.findByIdAndDelete(req.params.id)
        // 返回创建的model,req是客户端对象,res是服务端对象
        res.send({
            success: true
        })
    })

    // put路径为:http://localhost:3000/admin/api/ 修改资源
    router.put('/:id', async (req, res) => {
        // params为提交的URl,可获得其中的内容,findByIdAndUpdate(更具什么来查,更新的内容)
        const model = await req.Model.findByIdAndUpdate(req.params.id, req.body)
        // 返回创建的model,req是客户端对象,res是服务端对象
        res.send(model)
    })

    // 路径为:http://localhost:3000/admin/api/ 资源列表
    router.get('/',authMiddleware(), async (req, res) => {
        // populate可以将关联数据以对象的形式传递
        const queryOptions = {}
        //如果模型有分类加上parent
        if (req.Model.modelName === 'Category') {
            queryOptions.populate = 'parent'
        }
        // 查询req.Model模型数据返回,限制10条
        //setOptions()创建数据集
        const items = await req.Model.find().setOptions(queryOptions).limit(10)
        // 返回创建的model,req是客户端对象,res是服务端对象
        res.send(items)
    })

    // 获取详情页面的接口 :id路径匹配,相当于前端传递的一个参数,即接收参数,参数名为id 资源详情页
    router.get('/:id', async (req, res) => {
        // 根据前端请求的id用findById查询CorCategy模型数据返回
        const model = await req.Model.findById(req.params.id)
        // 返回创建的model,req是客户端对象,res是服务端对象
        res.send(model)
    })

    // 挂载子路由在app对象上面,路径会自动加载为http://localhost:3000/admin/api
    app.use('/admin/api/rest/:resoure', authMiddleware(),resourceMiddleware() , router)


    const multer = require('multer')
    const upload = multer({
        //__dirname:使用绝对地址 __dirname这个文件所在的文件夹
        dest: __dirname + '/../../uploads'
    })

    app.use('/admin/api/upload',authMiddleware(), upload.single('file'), async (req, res) => {
        const file = req.file
        file.url = `http://localhost:3000/uploads/${file.filename}`
        res.send(file)
    })

    //管理员登录路由
    app.post('/admin/api/login', async (req, res) => {
        const {
            username,
            password
        } = req.body
        // 1.根据用户名找用户
        // const AdminUser = require('../../models/AdminUser')
        const user = await AdminUser.findOne({
            username: username
            // username
        }).select('+password') //因为再AdminUser模型中设置select:false,所以默认取不到password,故加上select('+password)可以取到
        // if (!user) {
        //     return res.status(422).send({
        //         message: "用户名不存在"
        //     })
        // }
        assert(user, 422, "用户不存在")

        // 2.校验密
        //compareSync(明文,密文);比较明文和密文是否相等
        const isValid = require('bcrypt').compareSync(password, user.password)
        assert(isValid, 422, "密码错误")

        // 3.返回token
        // const jwt = require('jsonwebtoken')
        // sing(需要加密的信息[对象],加密的密钥[全局配置]),app.get获取app上全局绑定的属性
        const token = jwt.sign({
            id: user._id,
            // _id:user._id,
        }, app.get('secret'))
        res.send({
            token
        })
    })
    //错误处理函数
    app.use(async (err, req, res, next) => {
        //没有状态码会报 500 错误
        res.status(err.statusCode || 500).send({
            message: err.message
        })
    })
}

auth中间键

module.exports = option =>{
    const jwt = require('jsonwebtoken')
    const AdminUser = require('../models/AdminUser')
    const assert = require('http-assert')
    return async (req, res, next) => {
        //添加中间件 String(req.header.authorization || '') 将传递的token转换为字符串或者为空(不存在)再通过空格分割,pop()获取最后一个元素
        const token = String(req.headers.authorization || '').split(' ').pop()
        assert(token, 401, "请先登录")
        //verify解析token
        const {
            id
        } = jwt.verify(token, req.app.get('secret'))
        assert(id, 401, "请先登录")
        //给客户端绑定 user,使得后一个函数也可以使用同一个req
        req.user = await AdminUser.findById(id)
        assert(req.user, 401, "请先登录")
        await next()
    }
}

File中间键

module.exports = option => {
    return async (req, res, next) => {
        //创建中间键,每次发送的url请求都会经过中间键的处理
        const modelName = require('inflection').classify(req.params.resoure)
        //将传递的参数改为首字母大写并构建成模型的地址
        req.Model = require(`../models/${modelName}`)
        //next()表示执行下一步
        next()
    }
}

server服务器index

const express = require("express")
const app=express()

// 引入跨域模块
app.use(require('cors')())
// 接收客户端传递的json数据必须引用express.json
app.use(express.json())

//托管静态文件,让uploads中的文件可以通过/uploads来访问
app.use('/uploads',express.static(__dirname + '/uploads'))

// 将app传递给admin中,在admin中可以用module.exports = app=>{}接收
require('./routes/admin')(app)

require('./plugins/db')(app)

//设置token密钥,app.set方法全局绑定属性
app.set('secret','s21321ajhdakj2131asd213')

app.listen(3000,()=>{
    console.log('http://localhst:3000');
})

Hero模型

// 导入mongoose
const mongoose = require('mongoose')
// 定义模型字段
const schema = new mongoose.Schema({
    name: {
        type: String
    },
    avatar: {
        type: String
    },
    title: {
        type: String
    },
    //关联数组,实现一个model对象关联多个其它对象
    categories: [{
        type: mongoose.SchemaTypes.ObjectId, ref: 'Category'
    }],

    scores: {
        difficult: {
            type: Number
        },
        skills: {
            type: Number
        },
        attack: {
            type: Number
        },
        survive: {
            type: Number
        }
    },
    // 技能对象数组
    skills: [{
        icon: {
            type: String
        },
        name: {
            type: String
        },
        discription: {
            type: String
        },
        tips: {
            type: String
        },
    }],
    // //顺风局势
    items1: [{
        type: mongoose.SchemaTypes.ObjectId,
        ref: 'Item'
    }],
    // //逆风局势
    items2: [{
        type: mongoose.SchemaTypes.ObjectId,
        ref: 'Item'
    }],
    // //技巧
    usageTips: {
        type: String
    },
    battleTips: {
        type: String
    },
    teamTips: {
        type: String
    },
    //搭档
    partners:[{
        hero: {
            type: mongoose.SchemaTypes.ObjectId,
            ref: 'Hero'
        },
        description:{type:String}
    }]
})
// 导出模型Categorys
module.exports = mongoose.model('Hero', schema)

结语

如果你发现文章有什么问题,欢迎留言指正。
如果你觉得这篇文章还可以,别忘记点个赞加个关注再走哦。
如果你不嫌弃,还可以关注微信公众号———梦码城(持续更新中)。
梦码在这里感激不尽!!

项目已经上传到git 感兴趣的下伙伴欢迎下载

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦码城

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值