preface

Team will meet the demand of the online document management, including technical documentation, interface documentation, excel document, and the prototype of mandatory requirements, such as there has been no find the right open source projects to meet the demand, so began to implement a document management system (to implement is not complicated, this tutorial is to provide train of thought, not a best practice)

Github: Portal demo address: Portal

Feature list

  • Log in to register
  • The workbench | document list
  • Document editing preview (support: MD, Excel, HTML product prototype hosting)
  • Collaborative editing
  • Access Settings
  • Team management
  • Thumb up to collect
  • Template management
  • Browsing history
  • The recycle bin
  • Folder reading (interface document)
  • Edit history version

System Interface Preview

Preparation before Reading

1. Know about VUE technology stack development 2. Know about KOA 3

Technology stack

Front-end: VUE: Modular development requires Angular, React, and VUE. Vue is chosen here. Vuex: state management sASS: CSS precompiler. Element-ui: Instead of building wheels, there is already a good library of VUE components to use.

Server: egg.js: enterprise-level framework, according to a unified set of conventions for application development, development is very efficient. Mongodb: a flexible database based on distributed file storage. Egg-alinode: Ali offers free NodeJS server performance monitoring.

Engineering structures,

Here we manage the front and back end projects in the same directory, using egg scaffolding and vue-cli3 generation to initialize the project, copy and merge into the same directory, remember to merge the package.json content. < span style = “box-sizing: inherit! Important; word-wrap: inherit! Important;”

... | | - app / / egg initialization app directory - config / / egg initialization app directory static resource directory | | - public / / vue web / / SRC directory, Change to web for front-end project directoryCopy the code

In this case, we need to make some changes to our vue webpack configuration. First, we need to change the directory that we used to compile to SRC to web. Second, we need to add another directory to babel-loader to enable NPM run build to compile to web.

  • Vue. Config. js is added to the root directory. The purpose is to change the vue project entry to :web/main.js

        module.exports = {    
          pages: {        
            index: {            
              entry: "web/main.js"        
            }    
          }
        }
    Copy the code
  • Babel-loader can compile web directories properly. Add the following configuration in vue.config.js

    // Expand webPack configuration chainWebpack: config => { config.module .rule('js') .include.add(/web/).end() .use('babel') .loader('babel-loader') .tap(options => { // Modify its options... return options }) }Copy the code
  • Package. json added front-end project packaging command

"dev-web": "vue-cli-service serve",
"build-web": "vue-cli-service build",
Copy the code

Front-end development starts NPM run dev- Web back-end development starts NPM run dev

Project directory structure

| -- -- -- -- -- -- -- -- -- app server program code | - controller -- -- -- -- -- -- -- -- used to resolve user input, Returns the corresponding results after processing | - the extend -- -- -- -- -- -- -- -- the expansion of the framework | - middleware -- -- -- -- -- -- -- -- write middleware | - model -- -- -- -- -- -- -- - | Schema data model - public -- -- -- -- -- -- -- -- to static resources | - service -- -- -- -- -- -- -- -- used to write the business logic layer | -- the router. Js -- -- -- -- -- -- -- - used to configure the URL routing rules | - config -- -- -- -- -- -- -- -- an egg configuration file | -- config. Default. Js -- -- -- -- -- -- -- - | -- config. The default configuration. The local js -- -- -- -- -- -- -- -- the development environment configuration | -- config. Prod. Js -- -- -- -- -- -- -- -- the production environment configuration | -- plugin. Js -- -- -- -- -- -- -- -- configuration needs to be loaded plugin | - web -- -- -- -- -- -- -- -- the front-end interface code project | - common -- -- -- -- -- -- -- -- the front-end interface corresponding static resources | - components -- -- -- -- -- -- -- -- component | - config -- -- -- -- -- -- -- - | - filter configuration file -- -- -- -- -- -- -- -- the filter | - pages -- -- -- -- -- -- -- -- page | - the router routing configuration -- -- -- -- -- -- -- - | - store -- -- -- -- -- -- -- -- vuex state management | - service -- -- -- -- -- -- -- -- axios package | -- App. Vue -- -- -- -- -- -- -- -- the App . | - the main js -- -- -- -- -- -- -- -- the entry file | -- permission. Js -- -- -- -- -- -- -- -- access control | - docs -- -- -- -- -- -- -- -- reserve writing project documents directory | -- vue. Config. Js -- -- -- -- -- -- -- -- the vue Webpack configuration file | -- package. Json... .Copy the code

After completing the initialization of the project directory, the next step is to configure mongodb globally with some middleware and extension methods to prepare for interface development

Mongo configuration

1. Install the Mongoose module

npm install egg-mongoose --save
Copy the code

2. Configure the Config file

// config/plugin.js exports.mongoose = { enable: true, package: 'egg-mongoose', }; / / config/config. Default. Js config. The mongoose = {url: 'mongo: / / 127.0.0.1:27017 / inkwash', the options: {},};Copy the code

Global middleware and extension configuration

1. In the development of back-end interface, we need a unified return format. We can extend the return data function under context object to uniformly process interface Response data

Extend context.js

// app/extend/context.js
module.exports = {
	/** * returns the contents of the client *@param Status // Whether the interface is successful *@param Body // Returns data *@param MSG // Message prompt * is displayed@param Code // Returns the status code */
	returnBody (status = true, body = {}, msg = 'success', code = 200) {
		this.status = code;
		this.body = {
			status: status,
			body: body,
			msg,
			code: code
		}
	}
}
Copy the code

// call const {CTX} = this; Ctx. returnBody(true, {}, “success “);

Create a new Middleware folder under middleware APP folder, create error_handler.js, and configure congFig global middleware configuration

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

	return async function errorHandler(ctx, next) {
		try {
			await next();
		} catch (err) {
			// All exceptions raise an error event on the app, and the framework logs an error
			ctx.app.emit('error', err, ctx);

			const status = err.status || 500;

			// If the production environment when the 500 error details are not returned to the client
			const error = status === 500 && ctx.app.config.env === 'prod' ? 'Network error' : err.message;

			ctx.body = {
				msg: error,
				status: false.body: {},
				code: status }; }}; };// app/middleware/error_handler.js
// config/config.default.js configures the global middleware
config.middleware = [ 'errorHandler'];

Copy the code

JWT authentication Login authentication

Install the egg-JWT token generation and validation package

npm install egg-jwt --save
Copy the code

2. After installation, configure the plugin in the root directory config/plugin.js, for example:

'use strict';

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

Config /config.default.js config/config.default.js

Config. JWT = {secret: "123456"// User-defined token encryption condition string};Copy the code

4. Extend two functions on context, getToken and checkToken, to generate tokens and validate tokens

// 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)
}
Copy the code

Create auth.js in app/ Middleware folder

// 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 invalid, please login again ', 401); } if (! User) {ctx.returnBody(false,{}, 'Token is invalid, please login again ', 401); return; } ctx.request.user = user; await next(); }; };Copy the code

After the above configuration is completed, the next core registration function related operations began.

  • First I create an access route in the root app/router.js:
import { Application } from 'egg'; export default (app: Application) => { const { controller, router, jwt } = app; Router.post ('/auth/register', controller.auth.register); // Add JWT router.post('/user/infor', JWT, controller.user.infor) only on routes that require token validation; };Copy the code
  • Next I went to write my controller in the root directory app/controller/home.ts: this uses two generic methods that we extend on app/extend/context.js
    1. Ctx. getToken(User information object object) Generates user information through JWT and returns the token to the front end
    2. Return data via 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)); Let token =await ctx.getToken(userDataStr); Ctx. returnBody(true, {access_token: token, userInfo: userData}, "Registered successfully!" ) } } module.exports = AuthController;Copy the code
  • Headers Authorization is the default Authorization for headers request.
axios({ method: 'get', url: 'http://127.0.0.1:7001/user/info' headers: {/ / remember not to token sent directly, Prefix Bearer string with a space 'Authorization': 'Bearer ${token}'}})Copy the code
  • The interface obtains encrypted information from toen
  1. Extend the getUser method in app/extend/context.js to obtain token encryption information
/ / app/extend/context. Js / / get the user information async getUserData () {var token = this. Headers. Authorization? this.headers.authorization : ''; Token = token.substring(7) Parsing we don't need to add the Bearer let user = {} try {user = this. App. JWT. Verify (token, this app. Config. JWT. Secret); } catch (err) { user = {} } return user; }Copy the code
  1. Achieve access to personal information interface
// 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;

Copy the code

So far, we have achieved JWT token generation, and then obtain the information of the current login user through the token passed from the front end. JWT login authorization should be finished, and other business interfaces should be easy to implement

Md Document Editing

Document editors use Vditor, a browser-side Markdown editor that supports WYSIWYG (rich text), instant rendering (similar to Typora) and split-screen preview mode installation of Vditor

npm install vditor --save
Copy the code

Introduces and initializes objects in code

<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>Copy the code

Excel table editor

Install X-ray data – the spreadsheet

npm install x-data-spreadsheet
Copy the code
<div id="x-spreadsheet-demo"></div>
Copy the code
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()
Copy the code

Axure prototype hosting

Prototype Axure page hosting, reference WuliHub let users upload generated HTML compression package, and then decompress to the static resource directory, return to access the address is OK, the front end to get the prototype address with embedded IFrame render out ok

Package compiler && static resource Settings

1. Configure front-end VUE page packaging commands

// kage.json script Add package command "build-web": "vue-cli-service build",Copy the code

Dist front-end code static file will be generated by running NPM run build-web root directory. Because egg supports setting multiple static resource directories, we will directly configure the dist folder in the root directory as a static directory and configure config

// config/config.default.js config.static = {prefix: '/',// change the static resource prefix to '/' (default is '/public') dir: [ path.join(__dirname, '../app/public'), path.join(__dirname, '../dist') ] }Copy the code

After the completion of the packaging start an egg, can be accessed through http://localhost:7001/index.html on the front page


404 can also be configured to redirect the following route ‘/’ to ‘/index.html’ since accessing http://localhost:7001 directly

Redirect ('/', '/index.html', 302); // app/ router-js // redirect to index page app.router-redirect ('/', '/index.html', 302);Copy the code

The deployment of

Run the start command for server deployment

npm run start
Copy the code

Performance monitoring

For node service performance monitoring, you can use The free and open source Alinode of Ali

npm i egg-alinode
Copy the code

2. Plug-in configuration

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

3. Configure config

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

The monitoring data can be seen in the Ali Node.js performance platform console in the monitoring panel

The end of the

It’s not complicated to implement, and this tutorial is just an idea, not a best practice