egg+vue+mongodb实践开发在线文档管理平台——水墨文档
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.
授权转载自: 围的围
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] 回收站
-
[ ] 文件夹形式阅读(接口文档)
-
[ ] 编辑历史版本
系统界面预览
阅读前准备
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
, getToken
和 checkToken
用于生成 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
上扩展的两个通用方法
-
ctx.getToken object jwt token
-
通过
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
获取加密信息
-
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;
}
-
实现获取个人信息接口
// 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/:7001
会 404
可以再配置个路由重定向,将跟路由 '/'
重定向到 '/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
性能平台控制台看到监控面板
“在看和转发” 就是最大的支持
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK