9

egg+vue+mongodb实践开发在线文档管理平台——水墨文档

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=Mzg2NDAzMjE5NQ%3D%3D&%3Bmid=2247487696&%3Bidx=1&%3Bsn=01ddf4964047c21de6b50d4c354fa967
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

RvQFziR.png!mobile

授权转载自: 围的围

https://segmentfault.com/a/1190000037621367

前言

团队中会遇到在线文档管理的需求,包括技术文档,接口文档, excel 文档,和产品原型的托管等需求,一直没有找到合适的开源项目来满足需求,所以动手实现了个文档管理系统(实现起来并不复杂,该教程只是提供思路,并非最佳实践)

Github: https://github.com/huangwei9527/Ink-wash-docs

演示地址:http://www.inkwash.online/

功能列表

  • [x] 登录注册

  • [x] 工作台|文档列表

  • [x] 文档编辑预览(支持:md, excel,html产品原型托管)

  • [x] 协作编辑

  • [x] 访问权限设置

  • [x] 团队管理

  • [x] 点赞收藏

  • [x] 模板管理

  • [x] 浏览历史

  • [x] 回收站

  • [ ] 文件夹形式阅读(接口文档)

  • [ ] 编辑历史版本

系统界面预览

URzqErb.png!mobile

阅读前准备

1、了解 vue 技术栈开发 2、了解 koa 3、了解 egg 4、了解 mongodb

技术栈

前端: vue : 模块化开发少不了angular,react,vue三选一,这里选择了vue。 vuex : 状态管理 sass : css预编译器 element-ui :不造轮子,有现成的优秀的vue组件库当然要用起来。

服务端: egg.js :企业级框架,按照一套统一的约定进行应用开发,开发十分高效。 mongodb :一个基于分布式文件存储的数据库,比较灵活。 egg-alinode :阿里提供的免费nodejs服务器性能监控。

工程搭建

这里我们将前后端项目放在同一个目录下管理,分别用 egg 脚手架和 vue-cli3 生成初始化项目,拷贝合并到同一个目录下,记得合并下 package.json 内容。(脚手架生成项目就不介绍了,按照文档来就是了),合并后将 vue 项目 src 目录改为 web ,如下:

···
·
|-- app // egg 初始化app目录
|-- config // egg 初始化app目录
|-- public // vue 静态资源目录
|-- web // 原 src 目录,改成 web 用作前端项目目录
·
···

这样的话 我们需要再把我们vue webpack打包配置稍作一下调整,首先是把原先的编译指向src的目录改成 web,其次为了 npm run build 能正常编译 web 我们也需要为 babel-loader 再增加一个编译目录:

  • 根目录新增 vue.config.js ,目的是为了改造 vue 项目入口,改为: web/main.js

    module.exports = {    
    pages: {
    index: {
    entry: "web/main.js"
    }
    }
    }
  • babel-loader 能正常编译 web 目录, 在 vue.config.js 新增如下配置

// 扩展 webpack 配置
chainWebpack: config => {
config.module
.rule('js')
.include.add(/web/).end()
.use('babel')
.loader('babel-loader')
.tap(options => {
// 修改它的选项...
return options
})
}
  • package.json 新增前端项目打包命令
"dev-web": "vue-cli-service serve",
"build-web": "vue-cli-service build",

至此前后端项目初始化工作就完了,前端开发启动 npm run dev-web 后端开发启动 npm run dev

工程目录结构

|-- app                    --------服务器端项目代码
|--controller --------用于解析用户的输入,处理后返回相应的结果
|--extend --------框架的扩展
|--middleware --------编写中间件
|--model --------Schema数据模型
|--public --------用于放置静态资源
|--service --------用于编写业务逻辑层
|--router.js --------用于配置 URL 路由规则
|-- config --------egg 配置文件
|--config.default.js --------默认配置
|--config.local.js --------开发环境配置
|--config.prod.js --------生产环境配置
|--plugin.js --------配置需要加载的插件
|-- web --------前端项目界面代码
|--common --------前端界面对应静态资源
|--components --------组件
|--config --------配置文件
|--filter --------过滤器
|--pages --------页面
|--router --------路由配置
|--store --------vuex状态管理
|--service --------axios封装
|--App.vue --------App
|--main.js --------入口文件
|--permission.js --------权限控制
|-- docs --------预留编写项目文档目录
|-- vue.config.js --------vue webpack配置文件
|-- package.json
...
...

完成项目目录初始化后,接下来先把 mongodb 全局得一些中间件、扩展方法给配置上,为接口开发做好准备工作

mongodb配置

1、安装 mongoose 模块

npm install egg-mongoose --save

2、配置 config 文件

// config/plugin.js
exports.mongoose = {
enable: true,
package: 'egg-mongoose',
};

// config/config.default.js
config.mongoose = {
url: 'mongodb://127.0.0.1:27017/inkwash',
options: {},
};

全局中间件和扩展配置

1、统一处理接口

后端接口开发中我们需要一个统一得返回格式,可以在 context 对象下扩展个返回数据 function 用于统一处理接口 response data

app 下新建文件夹 extend 新建 context.js

// app/extend/context.js
module.exports = {
/**
* 返回客户端的内容
* @param status // 接口是否成功
* @param body // 返回数据
* @param msg // 返回信息提示
* @param code // 返回状态码
*/

returnBody (status = true, body = {}, msg = 'success', code = 200) {
this.status = code;
this.body = {
status: status,
body: body,
msg,
code: code
}
}
}
// 调用
const { ctx } = this;
ctx.returnBody(true, {}, "成功");

2、添加统一处理错误得中间件

app文件夹下新建 middleware 文件夹,新建 error_handler.js , 并配置 congfig 全局中间件配置

// app/middleware/error_handler.js
module.exports = () => {

return async function errorHandler(ctx, next) {
try {
await next();
} catch (err) {
// 所有的异常都会在app上出发一个error事件,框架会记录一条错误日志
ctx.app.emit('error', err, ctx);

const status = err.status || 500;

// 如果时生产环境的时候 500错误的详细错误内容不返回给客户端
const error = status === 500 && ctx.app.config.env === 'prod' ? '网络错误' : err.message;

ctx.body = {
msg: error,
status: false,
body: {},
code: status
};
}
};
};

// app/middleware/error_handler.js
// config/config.default.js 配置全局中间件
config.middleware = [ 'errorHandler'];

jwt鉴权登录认证

1、安装 egg-jwt token生成以及验证包

npm install egg-jwt --save

2、安装完成后在根目录下的 config/plugin.js 配置一下,如:

'use strict';

/** @type Egg.EggPlugin */
module.exports = {
jwt: {
enable: true,
package: "egg-jwt"
},
mongoose: {
enable: true,
package: 'egg-mongoose',
}
};

3、接下来在 config/config.default.js 里面继续配置:

config.jwt = {
secret: "123456"//自定义 token 的加密条件字符串
};

4、在 context 上扩展两个 function , getTokencheckToken 用于生成 token 和验证 token

// app/extend/context.js
async getToken(data) {
return await this.app.jwt.sign(data, this.app.config.jwt.secret, {expiresIn: 30* 24 * 60 * 60 + 's'});
},
async checkToken(token) {
return await this.app.jwt.verify(token, this.app.config.jwt.secret)
}

5、编写个中间件实现登录验证拦截 在 app/middleware 文件夹下新建 auth.js

// app/middleware/auth.js
module.exports = () => {
return async function(ctx, next) {
let token = '';
if (
ctx.headers.authorization && ctx.headers.authorization.split(' ')[0] === 'Bearer'
) {
token = ctx.headers.authorization.split(' ')[1];
} else if (ctx.query.accesstoken) {
token = ctx.query.accesstoken;
} else if (ctx.request.body.accesstoken) {
token = ctx.request.body.accesstoken;
}
let user;
try{
user = await ctx.checkToken(token);
}catch (e) {
ctx.returnBody(false,{}, 'Token 无效,请重新登录', 401);
}
if (!user) {
ctx.returnBody(false,{}, 'Token 无效,请重新登录', 401);
return;
}
ctx.request.user = user;
await next();
};
};

好了以上配置完成后就开始接下来的核心注册功能相关操作了。

  • 首先我在根目录下的 app/router.js 创建访问路由:
import { Application } from 'egg';

export default (app: Application) => {
const { controller, router, jwt } = app;
//正常路由
router.post('/auth/register', controller.auth.register);

// 只有在需要验证 token 的路由上添加jwt
router.post('/user/infor',jwt, controller.user.infor);
};
  • 接下来我去编写我的控制器,在根目录下的 app/controller/home.ts 编写内容:这里使用了两个我们在 app/extend/context.js 上扩展的两个通用方法
  1. ctx.getToken
    object
    jwt
    token
    
  2. 通过 ctx.returnBody 返回数据
// app/controller/auth.js
const Controller = require('egg').Controller
class AuthController extends Controller {
async login() {
//... 略
}
async register() {
const { ctx, service } = this;
const { username, password, email } = ctx.request.body
let userData = await ctx.service.user.createUser(username, password, email);
userData = userData.toObject();
let userDataStr = JSON.parse(JSON.stringify(userData));
// 生成token
let token =await ctx.getToken(userDataStr);
ctx.returnBody(true, {access_token: token, userInfo: userData}, "注册成功!")
}
}

module.exports = AuthController;
  • 前端请求的时候需要在 headers 里面上默认的验证字断 Authorization 就可以了,如:
axios({
method: 'get',
url: 'http://127.0.0.1:7001/user/info',
headers:{
// 切记 token 不要直接发送,要在前面加上 Bearer 字符串和一个空格
'Authorization':`Bearer ${token}`
}
})
  • 接口从 token 获取加密信息
  1. app/extend/context.js
    getUser
    token
    
// app/extend/context.js
// 获取用户信息
async getUserData() {
var token = this.headers.authorization ? this.headers.authorization : '';
token = token.substring(7) //把Bearer 截取掉,解析的时候不需要加上Bearer
let user = {}
try {
user = this.app.jwt.verify(token, this.app.config.jwt.secret);
} catch (err) {
user = {}
}
return user;
}
  1. 实现获取个人信息接口

// app/controller/user.js
'use strict';

const Controller = require('egg').Controller;

class UserController extends Controller {
async info() {
let {ctx} = this;
let user = await this.ctx.getUserData()
ctx.returnBody(true, user)
}
}

module.exports = UserController;

至此我们就实现了 jwt 生成 token , 然后通过前端传过来的 token 获取当前登录用户的信息,   jwt 登录授权这块应该是讲完了,其他的业务接口应该实现起来难度不大

md文档编辑

文档编辑器使用 Vdito r , 一款浏览器端的 Markdown 编辑器,支持所见即所得(富文本)、即时渲染(类似 Typora )和分屏预览模式 安装 Vditor

npm install vditor --save

在代码中引入并初始化对象

<template>
<div class="editor-component editor-md" ref="editor-component">
<div id="editor-md-dom"></div>
</div>

</template>

<script>
import Vditor from 'vditor'
import "vditor/
src/assets/scss/index.scss"

let timer = null;
export default {
data(){
return {
contentEditor: '',
}
},
mounted () {
this.contentEditor = new Vditor('vditor', {
height: 360,
toolbarConfig: {
pin: true,
},
cache: {
enable: false,
},
after: () => {
this.contentEditor.setValue('hello, Vditor + Vue!')
},
})
},
}
</script>

excel表格编辑

安装 x-data-spreadsheet

npm install x-data-spreadsheet
<div id="x-spreadsheet-demo"></div>
import Spreadsheet from "x-data-spreadsheet";
// If you need to override the default options, you can set the override
// const options = {};
// new Spreadsheet('#x-spreadsheet-demo', options);
const s = new Spreadsheet("#x-spreadsheet-demo")
.loadData({}) // load data
.change(data => {
// save data to db
});

// data validation
s.validate()

axure原型托管

原型 axure 页面托管,参考 WuliHub 让用户上传生成的 html 压缩包,然后解压到静态资源目录,返回访问地址就 ok , 前端拿到原型地址用内嵌 iframe 渲染出来就 ok

打包编译&&静态资源设置

1、配置前端 vue 页面打包命令

// kage.json script新增打包命令
"build-web": "vue-cli-service build",

2、运行 npm run build-web 根目录会生成 dist 前端代码静态文件,因为 egg 支持设置多个静态资源目录,这里就直接配置根目录下的 dist 文件夹为静态目录, 配置 config

// config/config.default.js
config.static = {
prefix: '/',// 将静态资源前缀改为'/'(默认是 '/public')
dir: [
path.join(__dirname, '../app/public'),
path.join(__dirname, '../dist')
]
}

打包完成后启动 egg , 就可以通过 http://localhost/:7001/index.html 访问到前端页面了

因为直接访问 http://localhost/:7001404 可以再配置个路由重定向,将跟路由 '/' 重定向到 '/index.html'

// app/router.js 
// 重定向到index页面
app.router.redirect('/', '/index.html', 302);

部署

服务端部署运行 start 命令

npm run start

性能监控

node 服务性能监控这块可以使用阿里免费开源的 alinode 1、安装 egg-alinode

npm i egg-alinode

2、插件配置

// config/plugin.js
exports.alinode = {
enable: true,
package: 'egg-alinode',
};

3、配置 config

// config/config.default.js
exports.alinode = {
enable: true,
appid: 'my app id',
secret: 'my app secret',
};

这样就可以了,监控数据可以在阿里 Node.js 性能平台控制台看到监控面板

“在看和转发” 就是最大的支持


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK