<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用 Egg + Vue 開(kāi)發(fā)在線(xiàn)文檔管理平臺(tái)(8000字,手把手教程)

          共 10359字,需瀏覽 21分鐘

           ·

          2020-12-13 10:30

          關(guān)注?圖雀社區(qū),回復(fù)“交流

          加入我們一起學(xué)習(xí),天天進(jìn)步

          授權(quán)轉(zhuǎn)載自:圍的圍

          https://segmentfault.com/a/1190000037621367

          前言

          團(tuán)隊(duì)中會(huì)遇到在線(xiàn)文檔管理的需求,包括技術(shù)文檔,接口文檔, excel 文檔,和產(chǎn)品原型的托管等需求,一直沒(méi)有找到合適的開(kāi)源項(xiàng)目來(lái)滿(mǎn)足需求,所以動(dòng)手實(shí)現(xiàn)了個(gè)文檔管理系統(tǒng)(實(shí)現(xiàn)起來(lái)并不復(fù)雜,該教程只是提供思路,并非最佳實(shí)踐)

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

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

          功能列表

          • [x] 登錄注冊(cè)
          • [x] 工作臺(tái)|文檔列表
          • [x] 文檔編輯預(yù)覽(支持:md, excel,html產(chǎn)品原型托管)
          • [x] 協(xié)作編輯
          • [x] 訪(fǎng)問(wèn)權(quán)限設(shè)置
          • [x] 團(tuán)隊(duì)管理
          • [x] 點(diǎn)贊收藏
          • [x] 模板管理
          • [x] 瀏覽歷史
          • [x] 回收站
          • [ ] 文件夾形式閱讀(接口文檔)
          • [ ] 編輯歷史版本

          系統(tǒng)界面預(yù)覽

          閱讀前準(zhǔn)備

          1、了解 vue 技術(shù)棧開(kāi)發(fā) 2、了解 koa3、了解 egg4、了解 mongodb

          技術(shù)棧

          前端:vue: 模塊化開(kāi)發(fā)少不了angular,react,vue三選一,這里選擇了vue。vuex: 狀態(tài)管理sass: css預(yù)編譯器element-ui:不造輪子,有現(xiàn)成的優(yōu)秀的vue組件庫(kù)當(dāng)然要用起來(lái)。

          服務(wù)端:egg.js:企業(yè)級(jí)框架,按照一套統(tǒng)一的約定進(jìn)行應(yīng)用開(kāi)發(fā),開(kāi)發(fā)十分高效。mongodb:一個(gè)基于分布式文件存儲(chǔ)的數(shù)據(jù)庫(kù),比較靈活。egg-alinode:阿里提供的免費(fèi)nodejs服務(wù)器性能監(jiān)控。

          工程搭建

          這里我們將前后端項(xiàng)目放在同一個(gè)目錄下管理,分別用 egg 腳手架和 vue-cli3 生成初始化項(xiàng)目,拷貝合并到同一個(gè)目錄下,記得合并下 package.json 內(nèi)容。(腳手架生成項(xiàng)目就不介紹了,按照文檔來(lái)就是了),合并后將 vue 項(xiàng)目 src 目錄改為 web ,如下:

          ···
          ·
          |--?app????????????????????//?egg?初始化app目錄
          |--?config????????????????//?egg?初始化app目錄
          |--?public????????????????//?vue?靜態(tài)資源目錄
          |--?web????????????????????//?原?src?目錄,改成?web?用作前端項(xiàng)目目錄
          ·
          ···

          這樣的話(huà) 我們需要再把我們vue webpack打包配置稍作一下調(diào)整,首先是把原先的編譯指向src的目錄改成 web,其次為了 npm run build 能正常編譯 web 我們也需要為 babel-loader 再增加一個(gè)編譯目錄

          • 根目錄新增 vue.config.js ,目的是為了改造 vue 項(xiàng)目入口,改為: web/main.js

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

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

          至此前后端項(xiàng)目初始化工作就完了,前端開(kāi)發(fā)啟動(dòng)npm run dev-web 后端開(kāi)發(fā)啟動(dòng) npm run dev

          工程目錄結(jié)構(gòu)

          |--?app????????????????????--------服務(wù)器端項(xiàng)目代碼
          ????|--controller????????????????--------用于解析用戶(hù)的輸入,處理后返回相應(yīng)的結(jié)果
          ????|--extend????????????????????--------框架的擴(kuò)展
          ????|--middleware????????????????--------編寫(xiě)中間件
          ????|--model????????????????????--------Schema數(shù)據(jù)模型
          ????|--public????????????????????--------用于放置靜態(tài)資源
          ????|--service????????????????????--------用于編寫(xiě)業(yè)務(wù)邏輯層
          ????|--router.js????????????????--------用于配置?URL?路由規(guī)則
          |--?config????????????????????--------egg?配置文件
          ????|--config.default.js????????????--------默認(rèn)配置
          ????|--config.local.js????????????????--------開(kāi)發(fā)環(huán)境配置
          ????|--config.prod.js????????????????--------生產(chǎn)環(huán)境配置
          ????|--plugin.js????????????????????--------配置需要加載的插件
          |--?web????????????????????????--------前端項(xiàng)目界面代碼
          ????|--common????????????????????--------前端界面對(duì)應(yīng)靜態(tài)資源
          ????|--components????????????????--------組件
          ????|--config????????????????????--------配置文件
          ????|--filter????????????????????--------過(guò)濾器
          ????|--pages????????????????????--------頁(yè)面
          ????|--router????????????????????--------路由配置
          ????|--store????????????????????--------vuex狀態(tài)管理
          ????|--service????????????????????--------axios封裝
          ????|--App.vue????????????????????--------App
          ????|--main.js????????????????????--------入口文件
          ????|--permission.js????????????--------權(quán)限控制
          |--?docs????????????????????--------預(yù)留編寫(xiě)項(xiàng)目文檔目錄
          |--?vue.config.js????????????--------vue?webpack配置文件
          |--?package.json
          ...
          ...

          完成項(xiàng)目目錄初始化后,接下來(lái)先把 mongodb 全局得一些中間件、擴(kuò)展方法給配置上,為接口開(kāi)發(fā)做好準(zhǔn)備工作

          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:?{},
          };

          全局中間件和擴(kuò)展配置

          1、統(tǒng)一處理接口

          后端接口開(kāi)發(fā)中我們需要一個(gè)統(tǒng)一得返回格式,可以在 context 對(duì)象下擴(kuò)展個(gè)返回?cái)?shù)據(jù) function 用于統(tǒng)一處理接口 response data

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

          //?app/extend/context.js
          module.exports?=?{
          ????/**
          ?????*?返回客戶(hù)端的內(nèi)容
          ?????*?@param?status?//?接口是否成功
          ?????*?@param?body?//?返回?cái)?shù)據(jù)
          ?????*?@param?msg?//?返回信息提示
          ?????*?@param?code?//?返回狀態(tài)碼
          ?????*/

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

          2、添加統(tǒng)一處理錯(cuò)誤得中間件

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

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

          ????return?async?function?errorHandler(ctx,?next)?{
          ????????try?{
          ????????????await?next();
          ????????}?catch?(err)?{
          ????????????//?所有的異常都會(huì)在app上出發(fā)一個(gè)error事件,框架會(huì)記錄一條錯(cuò)誤日志
          ????????????ctx.app.emit('error',?err,?ctx);

          ????????????const?status?=?err.status?||?500;

          ????????????//?如果時(shí)生產(chǎn)環(huán)境的時(shí)候?500錯(cuò)誤的詳細(xì)錯(cuò)誤內(nèi)容不返回給客戶(hù)端
          ????????????const?error?=?status?===?500?&&?ctx.app.config.env?===?'prod'???'網(wǎng)絡(luò)錯(cuò)誤'?:?err.message;

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

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

          jwt鑒權(quán)登錄認(rèn)證

          1、安裝 egg-jwt token生成以及驗(yàn)證包

          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、接下來(lái)在 config/config.default.js 里面繼續(xù)配置:

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

          4、在 context 上擴(kuò)展兩個(gè) function , getTokencheckToken 用于生成 token 和驗(yàn)證 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、編寫(xiě)個(gè)中間件實(shí)現(xiàn)登錄驗(yàn)證攔截 在 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?無(wú)效,請(qǐng)重新登錄',?401);
          ????????}
          ????????if?(!user)?{
          ????????????ctx.returnBody(false,{},?'Token?無(wú)效,請(qǐng)重新登錄',?401);
          ????????????return;
          ????????}
          ????????ctx.request.user?=?user;
          ????????await?next();
          ????};
          };

          好了以上配置完成后就開(kāi)始接下來(lái)的核心注冊(cè)功能相關(guān)操作了。

          • 首先我在根目錄下的 app/router.js 創(chuàng)建訪(fǎng)問(wèn)路由:
          import?{?Application?}?from?'egg';

          export?default?(app:?Application)?=>?{
          ??const?{?controller,?router,?jwt?}?=?app;
          ??//正常路由
          ??router.post('/auth/register',?controller.auth.register);
          ??
          ??//?只有在需要驗(yàn)證?token?的路由上添加jwt
          ??router.post('/user/infor',jwt,?controller.user.infor);?
          };
          • 接下來(lái)我去編寫(xiě)我的控制器,在根目錄下的 app/controller/home.ts 編寫(xiě)內(nèi)容:這里使用了兩個(gè)我們?cè)?app/extend/context.js 上擴(kuò)展的兩個(gè)通用方法
            1. 通過(guò) ctx.getToken (用戶(hù)信息 object 對(duì)象)將用戶(hù)信息通過(guò) jwt 生成 token 返回給前端
            2. 通過(guò) ctx.returnBody 返回?cái)?shù)據(jù)
          //?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},?"注冊(cè)成功!")
          ????}
          }

          module.exports?=?AuthController;
          • 前端請(qǐng)求的時(shí)候需要在 headers 里面上默認(rèn)的驗(yàn)證字?jǐn)?Authorization 就可以了,如:
          axios({
          ?method:?'get',
          ??url:?'http://127.0.0.1:7001/user/info',
          ??headers:{
          ??????//?切記?token?不要直接發(fā)送,要在前面加上?Bearer?字符串和一個(gè)空格
          ????'Authorization':`Bearer?${token}`
          ??}
          })
          • 接口從 token 獲取加密信息
          1. app/extend/context.js 再擴(kuò)展個(gè) getUser 方法獲取 token 加密信息
          //?app/extend/context.js
          //?獲取用戶(hù)信息
          ????async?getUserData()?{
          ????????var?token?=?this.headers.authorization???this.headers.authorization?:?'';
          ????????token?=?token.substring(7)?//把Bearer?截取掉,解析的時(shí)候不需要加上Bearer
          ????????let?user?=?{}
          ????????try?{
          ????????????user?=?this.app.jwt.verify(token,?this.app.config.jwt.secret);
          ????????}?catch?(err)?{
          ????????????user?=?{}
          ????????}
          ????????return?user;
          ????}
          1. 實(shí)現(xiàn)獲取個(gè)人信息接口
          //?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;

          至此我們就實(shí)現(xiàn)了 jwt 生成 token , 然后通過(guò)前端傳過(guò)來(lái)的 token 獲取當(dāng)前登錄用戶(hù)的信息, ?jwt 登錄授權(quán)這塊應(yīng)該是講完了,其他的業(yè)務(wù)接口應(yīng)該實(shí)現(xiàn)起來(lái)難度不大

          md文檔編輯

          文檔編輯器使用 Vdito r, 一款瀏覽器端的 Markdown 編輯器,支持所見(jiàn)即所得(富文本)、即時(shí)渲染(類(lèi)似 Typora )和分屏預(yù)覽模式 安裝 Vditor

          npm?install?vditor?--save

          在代碼中引入并初始化對(duì)象