Project: DolphinScheduler (Orchestration & Management / Scheduling & Orchestration)
File: frontend-development.md

``` Vue mvvm framework Es6 ECMAScript 6.0 Ans-ui Analysys-ui D3 Visual Library Chart Library Jsplumb connection plugin library Lodash high performance JavaScript utility library ``` Node package download (note version v12.20.2) `https://nodejs.org/download/release/v12.20.2/` Use the command line mode `cd` enter the `dolphinscheduler-ui` project directory and execute `npm install` to pull the project dependency package. If `npm install` is very slow, you can set the taobao mirror ``` npm config set registry http://registry.npmmirror.com/ ``` Modify `API_BASE` in the file `dolphinscheduler-ui/.env` to interact with the backend: ``` API_BASE = http://127.0.0.1:12345 ``` ```bash npm install node-sass --unsafe-perm #Install node-sass dependency separately ``` `npm start` project development environment (after startup address http://localhost:8888) `npm run build` project packaging (after packaging, the root directory will create a folder called dist for publishing Nginx online) Run the `npm run build` command to generate a package file (dist) package Copy it to the corresponding directory of the server (front-end service static page storage directory) Visit address` http://localhost:8888` Install pm2 `npm install -g pm2` Execute `pm2 start npm -- run dev` to start the project in the project `dolphinscheduler-ui `root directory Start `pm2 start npm -- run dev` Stop `pm2 stop npm` delete `pm2 delete npm` Status `pm2 list` ``` [root@localhost dolphinscheduler-ui]# pm2 start npm -- run dev [PM2] Process successfully started App name id version mode pid status restart uptime cpu mem user watching npm 0 N/A fork 6168 online 31 0s 0% 5.6 MB root disabled Use `pm2 show <id|name>` to get more details about an app ``` `build` some webpack configurations for packaging and development environment projects `node_modules` development environment node dependency package `src` project required documents `src => combo` project third-party resource localization `npm run combo` specific view `build/combo.js` `src => font` Font icon library can be added by visiting https://www.iconfont.cn Note: The font library uses its own secondary development to reintroduce its own library `src/sass/common/_font.scss` `src => images` public image storage `src => js` js/vue `src => lib` internal components of the company (company component library can be deleted after open source) `src => sass` sass file One page corresponds to a sass file `src => view` page file One page corresponds to an html file ``` Projects are developed using vue single page application (SPA) All page entry files are in the `src/js/conf/${ corresponding page filename => home} index.js` entry file The corresponding sass file is in `src/sass/conf/${corresponding page filename => home}/index.scss` The corresponding html file is in `src/view/${corresponding page filename => home}/index.html` ``` Public module and util `src/js/module` `components` => internal project common components `download` => download component `echarts` => chart component `filter` => filter and vue pipeline `i18n` => internationalization `io` => io request encapsulation based on axios `mixin` => vue mixin public part for disabled operation `permissions` => permission operation `util` => tool Home => `http://localhost:8888/#/home` Project Management => `http://localhost:8888/#/projects/list` ``` | Project Home | Workflow Workflow definition Workflow instance Task instance ``` Resource Management => `http://localhost:8888/#/resource/file` ``` | File Management | udf Management Resource Management Function management ``` Data Source Management => `http://localhost:8888/#/datasource/list` Security Center => `http://localhost:8888/#/security/tenant` ``` | Tenant Management | User Management | Alarm Group Management master worker ``` User Center

`http://localhost:8888/#/user/account` The project `src/js/conf/home` is divided into `pages` => route to page directory ``` The page file corresponding to the routing address ``` `router` => route management ``` vue router, the entry file index.js in each page will be registered. Specific operations: https://router.vuejs.org/zh/ ``` `store` => status management ``` The page corresponding to each route has a state management file divided into: actions => mapActions => Detailshttps://vuex.vuejs.org/zh/guide/actions.html getters => mapGetters => Detailshttps://vuex.vuejs.org/zh/guide/getters.html index => entrance mutations => mapMutations => Detailshttps://vuex.vuejs.org/zh/guide/mutations.html state => mapState => Detailshttps://vuex.vuejs.org/zh/guide/state.html Specific actionhttps://vuex.vuejs.org/zh/ ``` The component is named multiple words and is connected with a wire (-) to avoid conflicts with HTML tags and a clearer structure. ``` // positive example export default { name: 'page-article-item' } ``` The internal common component of the `src/js/module/components` project writes the folder name with the same name as the file name. The subcomponents and util tools that are split inside the common component are placed in the internal `_source` folder of the component. ``` components header header.vue _source nav.vue util.js conditions conditions.vue _source search.vue util.js ``` When you define Prop, you should always name it in camel format (camelCase) and use the connection line (-) when assigning values to the parent component. This follows the characteristics of each language, because it is case-insensitive in HTML tags, and the use of links is more friendly; in JavaScript, the more natural is the hump name. ``` // Vue props: { articleStatus: Boolean } // HTML <article-item :article-status="true"></article-item> ``` The definition of Prop should specify its type, defaults, and validation as much as possible. Example ``` props: { attrM: Number, attrA: { type: String, required: true }, attrZ: { type: Object, // The default value of the array/object should be returned by a factory function default: function () { return { msg: 'achieve you and me' } } }, attrE: { type: String, validator: function (v) { return !(['success', 'fail'].indexOf(v) === -1) } } } ``` When performing v-for traversal, you should always bring a key value to make rendering more efficient when updating the DOM. ``` <ul> <li v-for="item in list" :key="item.id"> {{ item.title }} </li> </ul> ``` v-for should be avoided on the same element as v-if (`for example: <li>`) because v-for has a higher priority than v-if. To avoid invalid calculations and rendering, you should try to use v-if Put it on top of the container's parent element. ``` <ul v-if="showList"> <li v-for="item in list" :key="item.id"> {{ item.title }} </li> </ul> ``` If the elements in the same set of v-if logic control are logically identical, Vue reuses the same part for more efficient element switching, `such as: value`. In order to avoid the unreasonable effect of multiplexing, you should add key to the same element for identification. ``` <div v-if="hasData" key="mazey-data"> <span>{{ mazeyData }}</span> </div> <div v-else key="mazey-none"> <span>no data</span> </div> ``` In order to unify the specification, the instruction abbreviation is always used. Using `v-bind`, `v-on` is not bad. Here is only a unified specification. ``` <input :value="mazeyUser" @click="verifyUser"> ``` Styles are packaged in a file, all the styles defined in a single vue file, the same name in other files will also take effect. All will have a top class name before creating a

Note: The sass plugin has been added to the project, and the sas syntax can be written directly in a single vue file. For uniformity and ease of reading, they should be placed in the order of `<template>``<script>``<style>`. ``` <template> <div class="test-model"> test </div> </template> <script> export default { name: "test", data() { return {} }, props: {}, methods: {}, watch: {}, beforeCreate() { }, created() { }, beforeMount() { }, mounted() { }, beforeUpdate() { }, updated() { }, beforeDestroy() { }, destroyed() { }, computed: {}, components: {}, } </script> <style lang="scss" rel="stylesheet/scss"> .test-model { } </style> ``` It is recommended to no longer use var, but use let / const, prefer const. The use of any variable must be declared in advance, except that the function defined by function can be placed anywhere. ``` const foo = 'after division' const bar = `${foo}ront-end engineer` ``` Anonymous functions use the arrow function uniformly. When multiple parameters/return values are used, the object's structure assignment is used first. ``` function getPersonInfo ({name, sex}) { // ... return {name, gender} } ``` The function name is uniformly named with a camel name. The beginning of the capital letter is a constructor. The lowercase letters start with ordinary functions, and the new operator should not be used to operate ordinary functions. ``` const foo = {a: 0, b: 1} const bar = JSON.parse(JSON.stringify(foo)) const foo = {a: 0, b: 1} const bar = {...foo, c: 2} const foo = {a: 3} Object.assign(foo, {b: 4}) const myMap = new Map([]) for (let [key, value] of myMap.entries()) { // ... } ``` Unified management of project modules using import / export. ``` // lib.js export default {} // app.js import app from './lib' ``` Import is placed at the top of the file. If the module has only one output value, use `export default`otherwise no. Do not write the type attribute when referencing external CSS or JavaScript. The HTML5 default type is the text/css and text/javascript properties, so there is no need to specify them. ``` <link rel="stylesheet" href="//www.test.com/css/test.css"> <script src="//www.test.com/js/test.js"></script> ``` The naming of Class and ID should be semantic, and you can see what you are doing by looking at the name; multiple words are connected by a link. ``` // positive example .test-header{ font-size: 20px; } ``` CSS attributes use abbreviations as much as possible to improve the efficiency and ease of understanding of the code. ``` // counter example border-width: 1px; border-style: solid; border-color: #ccc; // positive example border: 1px solid #ccc; ``` The HTML5 standard should always be used. ``` <!DOCTYPE html> ``` A block comment should be written to a module file. ``` / @module mazey/api @author Mazey <mazey@mazey.net> @description test. / ``` Note that non-zero is wrong for catching catch ``` const test = () => { return new Promise((resolve, reject) => { resolve({ a:1 }) }) } // transfer test.then(res => {

// {a:1} }) ``` Normal return ``` { code:0, data:{} msg:'success' } ``` Error return ``` { code:10000, data:{} msg:'failed' } ``` If the interface is a post request, the Content-Type defaults to application/x-www-form-urlencoded; if the Content-Type is changed to application/json, Interface parameter transfer needs to be changed to the following way ``` io.post('url', payload, null, null, { emulateJSON: false } res => { resolve(res) }).catch(e => { reject(e) }) ``` dag related interface `src/js/conf/home/store/dag/actions.js` Data Source Center Related Interfaces `src/js/conf/home/store/datasource/actions.js` Project Management Related Interfaces `src/js/conf/home/store/projects/actions.js` Resource Center Related Interfaces `src/js/conf/home/store/resource/actions.js` Security Center Related Interfaces `src/js/conf/home/store/security/actions.js` User Center Related Interfaces `src/js/conf/home/store/user/actions.js` (1) First place the icon icon of the node in the `src/js/conf/home/pages/dag/img `folder, and note the English name of the node defined by the `toolbar_${in the background. For example: SHELL}.png` (2) Find the `tasksType` object in `src/js/conf/home/pages/dag/_source/config.js` and add it to it. ``` 'DEPENDENT': { // The background definition node type English name is used as the key value desc: 'DEPENDENT', // tooltip desc color: '#2FBFD8' // The color represented is mainly used for tree and gantt } ``` (3) Add a `${node type (lowercase)}`.vue file in `src/js/conf/home/pages/dag/source/formModel/tasks`. The contents of the components related to the current node are written here. Must belong to a node component must have a function verification () After the verification is successful, the relevant data of the current component is thrown to the parent component. ``` / Verification */ _verification () { // datasource subcomponent verification if (!this.$refs.refDs._verifDatasource()) { return false } // verification function if (!this.method) { this.$message.warning(`${i18n.$t('Please enter method')}`) return false } // localParams subcomponent validation if (!this.$refs.refLocalParams._verifProp()) { return false } // store this.$emit('on-params', { type: this.type, datasource: this.datasource, method: this.method, localParams: this.localParams }) return true } ``` (4) Common components used inside the node component are under` _source`, and `commcon.js` is used to configure public data. (1) Find the `tasksState` object in `src/js/conf/home/pages/dag/_source/config.js` and add it to it. ``` 'WAITTINGDEPEND': { // 'WAITTINGDEPEND': { //Backend defines state type, frontend is used as key value id: 11, // front-end definition id is used as a sort desc: `${i18n.$t('waiting for dependency')}`, // tooltip desc color: '#5101be', // The color represented is mainly used for tree and gantt icoUnicode: '&#xe68c;', // font icon isSpin: false // whether to rotate (requires code judgment) } ``` (1) Find the `toolOper` object in `src/js/conf/home/pages/dag/_source/config.js` and add it to it. ``` { code: 'pointer', // tool identifier icon: '&#xe781;', // tool icon disable: disable, // disable desc: `${i18n.$t('Drag node and selected item')}` // tooltip desc } ``` (2) Tool classes are returned as a constructor `src/js/conf/home/pages/dag/_source/plugIn` `downChart.js` => dag image download processing `dragZoom.js` => mouse zoom effect processing `jsPlumbHandle.js` => drag and drop line processing `util.js` => belongs to the `plugIn` tool class The operation is handled in the `src/js/conf/home/pages/dag/_source/dag.js` => `toolbarEvent` event. (1) First add a routing address`src/js/conf/home/router/index.js` in route management ``` routing address{ path: '/test', // routing address name: 'test', // alias component: resolve => require(['../pages/test/index'], resolve), // route corresponding component entry file meta: { title: `${i18n.$t('test')} - EasyScheduler` // title display } }, ``` (2)Create a `test` folder in `src/js/conf/home/pages` and create an `index.vue `entry file in the folder. This will give you direct access to`http://localhost:8888/#/test` Find the `src/lib/localData/email.js` startup and timed email address input to automatically pull down the match. ``` export default ["test@analysys.com.cn","test1@analysys.com.cn","test3@analysys.com.cn"] ``` The permission gives the userType according to the backUser interface `getUserInfo` interface: `"ADMINUSER/GENERALUSER" `permission to control whether the page operation button is `disabled`. specific operation`src/js/module/permissions/index.js` disabled processing`src/js/module/mixin/disabledState.js`