管理页面及后端express相关
添加技能的实现
- 数组删除用
skill.splice(index,1)配合v-for的(skill,index) in skills中的index使用
- 在
@click中直接写方法
- 增加数组数据
model.skills.push({}) 添加一个空对象
- 用
v-for循环创建skills模板
- 使用
el-row 和 el-rol建立布局 相当于div
mongoDB模型与其他模型联系
mongoDB建立模型时,如果一条数据是多个用数组
- 与其他模型有关联时,
type应该是mogoose.Schematypes.ObjectID,再加一个ref:'Category'
categories: [{ type: mongoose.SchemaTypes.ObjectId, ref: 'Category' }],
富文本编辑
<vue-editor
id="editor"
useCustomImageHandler
@image-added="handleImageAdded"
v-model="model.body"
></vue-editor>
async handleImageAdded(file, Editor, cursorLocation, resetUploader) {
const formData = new FormData();
formData.append("file", file);
const res = await this.$http.post("uploads", formData);
Editor.insertEmbed(cursorLocation, 'image', res.data.src);
resetUploader();
},
http-assert抛错
- 使用
http-assert抛出错误,在最后用错误处理函数接受错误
assert(user, 401, '用户不存在')
app.use(async (err, req, res, next) => {
res.status(err.statusCode || 500).send({
message: err.message
})
})
上传头像以及静态文件托管
- 使用
multer处理上传文件,先指定一个目标文件夹,使用single()函数接受formData数据,需配合express的静态文件托管使用
const multer = require('multer')({
dest: __dirname + '../../../uploads'
})
app.post('/admin/api/uploads',authMiddleWare(), multer.single('file'),async (req, res) => {
const file = req.file
file.src = `http://localhost:3000/uploads/${file.filename}`
res.send(file)
})
密码相关
- 使用
bcrypt做密码散列和验证,在数据库模型中使用以及服务端验证使用
password: {
type: String,
set(val) {
return require('bcrypt').hashSync(val, 10)
},
}
app.post('/admin/api/login', async (req, res) => {
const { username, password } = req.body
const user = await User.findOne({ username }).select('+password')
assert(user, 401, '用户不存在')
const isPasswordCorrect = require('bcrypt').compareSync(password, user.password)
assert(isPasswordCorrect, 401, '密码错误')
const token = jwt.sign({
id: user._id
}, app.get('secret'))
res.send(token)
})
token相关
const token = jwt.sign({
id: user._id
}, app.get('secret'))
localStorage.token = res.data
http.interceptors.request.use(config => {
if (localStorage.token) {
config.headers.Authorization = 'Bearer ' + String(localStorage.token)
}
return config
}, err => {
return Promise.reject(err)
})
async (req, res, next) => {
const token = (req.headers.authorization || '').split(' ').pop()
assert(token, 401, '请先登录')
const { id } = jwt.verify(token, req.app.get('secret'))
assert(id, 401, '请先登录')
req.user = User.findById(id)
assert(req.user, 401, '请先登录')
await next()
}
如何在Vue中添加全局方法
Vue.mixin({
computed: {
uploadURL() {
return this.$http.defaults.baseURL + '/uploads'
}
},
methods: {
getAuthHeader() {
return {
Authorization: `Bearer ${localStorage.token || ''}`
}
}
}
})
web前端页面
样式重置
* {
box-sizing: border-box;
outline: none;
}
html {
font-size: 13px;
}
body {
margin: 0;
font-family: Arial, Helvetica, sans-serif;
line-height: 1.2em;
background: #f1f1f1;
}
a {
text-decoration: none;
color: #999;
}
工具样式的定义
@import './variable'
//text
//靠左 居中 靠右
@each $var in (left, center, right) {
.text-#{$var} {
text-align: $var;
}
}
@each $colorKey, $color in $colors {
.text-#{$colorKey} {
color: $color;
}
}
@each $colorKey, $color in $colors {
.bg-#{$colorKey} {
background-color: $color;
}
}
@each $sizeKey, $size in $sizes {
.fs-#{$sizeKey} {
font-size: $size * $base-font-size;
}
}
.d-flex {
display: flex;
}
.flex-column {
flex-direction: column;
}
.flex-1 {
flex: 1;
}
.flex-grow-1 {
flex-grow: 1;
}
@each $k, $v in $jc {
.jc-#{$k} {
justify-content: $v;
}
}
@each $k, $v in $ai {
.ai-#{$k} {
align-items: $v;
}
}
@each $typeKey, $type in $spacing-types {
@each $sizeKey, $size in $spacing-sizes {
.#{$typeKey}-#{$sizeKey} {
#{$type}: $size * $base-spacing-size;
}
.#{$typeKey}x-#{$sizeKey} {
#{$type}-left: $size * $base-spacing-size;
#{$type}-right: $size * $base-spacing-size;
}
.#{$typeKey}y-#{$sizeKey} {
#{$type}-top: $size * $base-spacing-size;
#{$type}-bottom: $size * $base-spacing-size;
}
}
@each $directionKey, $direction in $spacing-direction {
@each $sizeKey, $size in $spacing-sizes {
.#{$typeKey}#{$directionKey}-#{$sizeKey} {
#{$type}-#{$direction}: $size * $base-spacing-size;
}
}
}
}
$colors: (
"grey-1": #666,
"grey-2": #999,
"white": #fff,
"yellow": #db9e3f,
"blue-1": #4b67af,
"blue-2": #464f73,
"black-1": #222,
"black-2": #212222
);
$base-font-size: 1rem;
$sizes: (
"xs": 0.9231,
"sm": 1,
"md": 1.0769,
"lg": 1.2308
);
$jc: (
"start": flex-start,
"end": flex-end,
"center": center,
"between": space-between,
"around": space-around
);
$ai: (
"start": flex-start,
"end": flex-end,
"center": center,
"stretch": stretch
);
$spacing-types: (
"m": margin,
"p": padding
);
$spacing-direction: (
"t": top,
"l": left,
"r": right,
"b": bottom
);
$base-spacing-size: 1rem;
$spacing-sizes: (
0: 0,
1: 0.25,
2: 0.5,
3: 1,
4: 1.5,
5: 3
);
sprite精灵图片
- 利用
http://www.spritecow.com/
.sprite {
background: url("../img/index.png") no-repeat;
background-size: 28.8462rem;
display: inline-block;
&.sprite-disclose {
background-position: 63.546% 15.517%;
width: 1.7692rem;
height: 1.5385rem;
}
}
swiper轮播插件
//基本用法
<swiper
:options="swiperOption"
ref="mySwiper"
>
<swiper-slide></swiper-slide>
<div
class="swiper-pagination text-right pr-3"
slot="pagination"
></div>
</swiper>
swiperOption: {
autoHeight: true,
loop: true,
speed: 400,
autoplay: {
delay: 2500
},
pagination: {
el: ".swiper-pagination"
}
}
- 几个常用的api
- 先通过
ref找到swiper
$refs.mySwiper.swiper.slideTo(i)
$refs.mySwiper.swiper.realIndex
- 在
swiper元素上可以监听改变@slide-change
要实现点击高亮效果
@click="active = i"
:class="{active : active === i}"
- 配合
v-for的index使用
封装卡片组件
- 把
icon和title父子传值
- 使用具名插槽
- 类似于父子组件传值的方式
slot给name属性,其他地方通过#name可以关联起来,在通过{xx}可以拿到传的值
<card
:categories="categories"
newHero="https://ossweb-img.qq.com/upload/webplat/info/yxzj/20200108/20796372351730.jpg"
title="英雄列表"
icon="hero"
>
//具名插槽传值
<template #swiperslide="{category}">
<div class="d-flex ai-center jc-around flex-wrap pt-2">
<div
class="d-flex flex-column text-black-2 text-center p-2"
style="width:20%"
v-for="(hero,hi) in category.heros"
:key="hi"
>
<img
class="w-100"
:src="hero.heroAvatar"
alt=""
>
<span class="pt-2">{{hero.heroName}}</span>
</div>
</div>
</template>
</card>
//具名插槽
<slot
name="swiperslide"
:category="category"
></slot>
在chrome的console拿数据的方式
后台录入数据
打乱顺序
const randomCats = cats.slice(0).sort((a, b) => Math.random() - 0.5)
require-all
数据库插入
await Article.deleteMany({})
await Article.insertMany(newsList)
利用mongoose的API写接口
点击router-link组件不刷新?
- 试着给
router-view绑定key为$route.path