團隊中會遇到在線文檔管理的需求,包括技術文檔,接口文檔,excel文檔,和產品原型的託管等需求,一直沒有找到合適的開源項目來知足需求,因此動手實現了個文檔管理系統(實現起來並不複雜,該教程只是提供思路,並不是最佳實踐)javascript
一、瞭解vue技術棧開發<br/>
二、瞭解koa<br/>
三、瞭解egg<br/>
四、瞭解mongodb<br/>html
前端:<br/>vue
: 模塊化開發少不了angular,react,vue三選一,這裏選擇了vue。<br/>vuex
: 狀態管理<br/>sass
: css預編譯器。<br/>element-ui
:不造輪子,有現成的優秀的vue組件庫固然要用起來。<br/>前端
服務端:<br/>egg.js
:企業級框架,按照一套統一的約定進行應用開發,開發十分高效。<br/>mongodb
:一個基於分佈式文件存儲的數據庫,比較靈活。<br/>egg-alinode
:阿里提供的免費nodejs服務器性能監控。<br/>vue
這裏咱們將先後端項目放在同一個目錄下管理,分別用egg腳手架和vue-cli3生成初始化項目,拷貝合併到同一個目錄下,記得合併下package.json內容。(腳手架生成項目就不介紹了,按照文檔來就是了),合併後將vue項目src目錄改成web,以下:java
··· · |-- app // egg 初始化app目錄 |-- config // egg 初始化app目錄 |-- public // vue 靜態資源目錄 |-- web // 原 src 目錄,改爲 web 用做前端項目目錄 · ···
這樣的話 咱們須要再把咱們vue webpack打包配置稍做一下調整,首先是把原先的編譯指向src的目錄改爲 web,其次爲了 npm run build 能正常編譯 web 咱們也須要爲 babel-loader 再增長一個編譯目錄:node
根目錄新增vue.config.js,目的是爲了改造vue項目入口,改成:web/main.jsreact
module.exports = { pages: { index: { entry: "web/main.js" } } }
babel-loader能正常編譯 web目錄, 在vue.config.js新增以下配置webpack
// 擴展 webpack 配置 chainWebpack: config => { config.module .rule('js') .include.add(/web/).end() .use('babel') .loader('babel-loader') .tap(options => { // 修改它的選項... return options }) }
"dev-web": "vue-cli-service serve", "build-web": "vue-cli-service build",
至此先後端項目初始化工做就完了,前端開發啓動npm run dev-web
後端開發啓動 npm run dev
ios
|-- 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全局得一些中間件、擴展方法給配置上,爲接口開發作好準備工做
一、安裝mongoose模塊
npm install egg-mongoose --save
二、配置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: {}, };
一、後端接口開發中咱們須要一個統一得返回格式,能夠在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, {}, "成功");
二、添加統一處理錯誤得中間件
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'];
一、安裝egg-jwt token生成以及驗證包
npm install egg-jwt --save
二、安裝完成後在根目錄下的 config/plugin.js 配置一下,如:
'use strict'; /** @type Egg.EggPlugin */ module.exports = { jwt: { enable: true, package: "egg-jwt" }, mongoose: { enable: true, package: 'egg-mongoose', } };
三、接下來在 config/config.default.js 裏面繼續配置:
config.jwt = { secret: "123456"//自定義 token 的加密條件字符串 };
四、在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) }
五、編寫個中間件實現登陸驗證攔截
在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(); }; };
好了以上配置完成後就開始接下來的核心註冊功能相關操做了。
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上擴展的兩個通用方法
// 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;
axios({ method: 'get', url: 'http://127.0.0.1:7001/user/info', headers:{ // 切記 token 不要直接發送,要在前面加上 Bearer 字符串和一個空格 'Authorization':`Bearer ${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登陸受權這塊應該是講完了,其餘的業務接口應該實現起來難度不大
文檔編輯器使用Vditor, 一款瀏覽器端的 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>
安裝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頁面託管,參考WuliHub讓用戶上傳生成的html壓縮包,而後解壓到靜態資源目錄,返回訪問地址就ok, 前端拿到原型地址用內嵌iframe渲染出來就ok
一、配置前端vue頁面打包命令
// kage.json script新增打包命令 "build-web": "vue-cli-service build",
二、運行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
一、安裝egg-alinode
npm i egg-alinode
二、插件配置
// config/plugin.js exports.alinode = { enable: true, package: 'egg-alinode', };
三、配置config
// config/config.default.js exports.alinode = { enable: true, appid: 'my app id', secret: 'my app secret', };
這樣就能夠了,監控數據能夠在阿里Node.js 性能平臺控制檯看到監控面板
實現起來並不複雜,該教程只是提供思路,並不是最佳實踐