preface

In the previous two articles have completed the basic work environment construction, has also completed the background core login and permission issues, now touch hands, together into the actual operation.

Element

When I started using VUE as a management background last October, I chose Element-UI without hesitation. At that time, VUE 2.0 was just released, and there were not many other VUE 2.0 UI frameworks to choose from. I chose Element-UI despite its many drawbacks and early bugs. Here are a few reasons why I chose it:

  • There are big factory endorsement: although the core development only two or three people, but at least do not worry about which day does not maintain, with the sister-in-law ran away
  • Continuous iteration:element-uiWith more than 40 releases to date, the network has averaged one small-release update a week. As of Dec. 4, 2017, the network has iterated 74 releases and maintained a high update frequency.
  • Excellent ecosystems and active communities: There are more than 250 functional coding molecules (I’ve contributed a few PR with interest and participated in 70 or 80 issues in the early stage), and plenty of scaffolding in the communityelement-uiThere are also many related QQ discussion groups orgitter.
  • Community recognition: Element is currently the most open source vUE related star project, reflecting community recognition.

There is still a gap between Ant Design and React’s big brother, in terms of component richness, parameter compatibility, document integrity, and UI interaction and aesthetics. But it took ant nearly 9K commits to get where it is today. I also believe that Element-UI will get better and better.

There are a few other frameworks (PC only) that you can choose from:

  • Ivew personally wrote a framework, beautiful and interactive, there is a feeling between Element and Ant, before a small tear with the Element team, interested in yourself to watch, the framework is still not done, a person can make such, is also not easy. Author’s open letter
  • Vue-admin is also a good choice, code written and good, the official also issued an Admin shelf, is also worth learning
  • Vue-material A Material Design VUE framework library
  • Vuetify is another library of the Material Design Vue framework
  • Keen-ui is another library of the Material Design Vue framework
  • The coreui-free-bootstrap-admin-template Template is a complete framework that can be extended to vue, React, and Angular versions
  • Framework7-VuePersonally, I feel that this is the best framework for my mobile terminal experience so far. However,Framework7-VueFeeling is not very perfect, still need to wait and see for a period of time. And it has its own routing rules, so it can’t be usedvue-routerThis is still very inconvenient.

I have simply listed some mainstream frameworks, and I have to sigh that the ecosystem of VUE is too prosperous now. The owner of the above framework has not been used in depth, so it is not good to give too many suggestions. We can identify the framework suitable for our business by ourselves.


Here we begin by introducing some of our experience with Element development.

Element-based dynamic peels

Some products are just so cruel that they can fulfill their needs and make us do dynamic peels. Element’s website also offers custom theme solutions and an online demo of custom themes

! [img](data:image/svg+xml; utf8,)

Is it cool? The author also explains the implementation of the solution address, the general idea:

  1. Start by replacing the color-related CSS values in the default theme file with keywords
  2. Generates a series of color values based on the theme color selected by the user
  3. Replace the keyword with the corresponding color value you just generated
  4. Add the style tag directly to the page and fill in the generated style

I think it’s really a little complicated. Is there an easy solution? Let’s think about it for a second. Let’s write dynamic peels ourselves. The most common way to do this is to write two sets of themes, one called day theme and the other called night theme. The night theme is in the same. Remove the night – the theme. This is dynamic peels in their simplest form. So can we also follow this idea and implement dynamic skin peels based on Element-UI?

First we download the officially approved Theme Generator, a tool specifically designed to generate the Element Theme. From the documentation, we generated the required themes.

! [img](data:image/svg+xml; utf8,)

After that, all we need to do is wrap a class around each element of the theme to create a namespace. We used gulp-CSS-wrap to easily achieve the desired result

var path = require('path')
var gulp = require('gulp')
var cleanCSS = require('gulp-clean-css');
var cssWrap = require('gulp-css-wrap');

var customThemeName='.custom-theme'

gulp.task('css-wrap'.function() {
  return gulp.src( path.resolve('./theme/index.css'))
    .pipe(cssWrap({selector:customThemeName}))
    .pipe(cleanCSS())
    .pipe(gulp.dest('dist'));
});

gulp.task('move-font'.function() {
  return gulp.src(['./theme/fonts/**']).pipe(gulp.dest('dist/fonts'));
});

gulp.task('default'['css-wrap'.'move-font']); Copy the codeCopy the code

This gives us a custom theme with a. Custom-theme namespace, which we then introduce into the project

//main.js
import 'assets/custom-theme/index.css'Copy the codeCopy the code

We just toggleClass(document.body, ‘custom-theme’) toggle the body class where the peels are. We simply achieved dynamic peels.

! [img](data:image/svg+xml; utf8,)

However, there is a drawback to this pattern implementation of skin, which is to pack both themes into the project, if your project needs seven or eight themes, this pattern is not suitable. We need to load CSS dynamically. Here is the simplest example of adding CSS dynamically. Of course, you can encapsulate it, add a success or failure callback, determine whether the changed resources are loaded, and so on.

var head = document.getElementsByTagName('HEAD').item(0);
var style = document.createElement('link');
style.href = 'style.css';
style.rel = 'stylesheet';
style.type = 'text/css'; head.appendChild(style); Copy the codeCopy the code

Update (2017.12)

Element-ui has been officially updated in version 2.0, with a new skin twist. The document


The sidebar

Here and talk about the problem of the navigation bar, sidebar in this project is based on the router, js configuration of routing and according to the permissions dynamically generated, thus eliminating the routing to manually write it again to write a sidebar this trouble, but also met a problem, there may be multiple nested routing, a lot of people can have 3 feedback your sidebar, There’s even a grade five. So the sidebar was refactored to use recursive components so that no matter how many levels you have, you can happily display them. code

! [img](data:image/svg+xml; utf8,)

Sidebar highlighting QUESTION: A lot of people in the group ask why their sidebar can’t be highlighted with their own routes. It’s actually quite simple

:default-active="$route.path"Copy the codeCopy the code

Just keep default-active pointing to the current route. That’s it.

Click the sidebar to refresh the current route

Before spa(single page development), most of the development mode was multi-page background. Every time the user clicked on the sidebar, the user would request the page again. The user gradually developed the habit of clicking on the current path of the sidebar to refresh the page. The user clicking on the highlighted route doesn’t refresh the view, because vue-Router intercepts your route and determines that your URL hasn’t changed, so it doesn’t trigger any hooks or view changes. The issue has also been hotly debated in the community.

! [img](data:image/svg+xml; utf8,)

Utah also said it would add another way to power View, but then changed his mind. But the need is here. What can we do? He said that nothing will be triggered without changing the current URL, so can I force the trigger to you? There are policies on the top and countermeasures on the bottom. We change flowers to hack. The method is also very simple, by constantly changing the QUERY url to trigger the view change. We listen for click events for each link in the sidebar, and each click pushes the Router a different Query to ensure that the view is refreshed.

clickLink(path) {
  this.$router.push({path, query: {t: +new Date() // Make sure the query item is different every time you click the route, make sure view}} is refreshed)} copy the codeCopy the code

The downside of this is that the URL has an ugly query suffix like xxx.com/article/list?t=1496832345025, but our users are ok with it… I can only hack temporarily. I don’t know if you have a better way to learn.


Table

After several iterations, the Table component of Element-UI has been able to meet most of the business needs. However, rowSpan colSpan table row/column merging is not currently supported (since element-UI version 2.0). Please follow this issue for official updates on this feature.

Here I will focus on the table table several common business forms.

Table drop sort

! [img](data:image/svg+xml; utf8,)

This is mainly based on Sortable

import Sortable from 'sortablejs'
let el = document.querySelectorAll('.el-table__body-wrapper > table > tbody') [0]letSortable = sortable.create (el) Copies the codeCopy the code

Select * from sortable.create (el); / / select * from sortable.create (el); / / select * from sortable.create (el). So we have to manage our list manually.

this.sortable = Sortable.create(el, { onEnd: Const tempIndex = this.newList.splice(evt.oldIndex, 1)[0]; this.newList.splice(evt.newIndex, 0, tempIndex); }}); Copy the codeCopy the code

So we can easily complete the table drag sort. Vue.draggable is recommended for non-DOM based sorting. The complete code


Table inline editing

Table inline editing is also a common requirement.

! [img](data:image/svg+xml; utf8,)

This is simple: after we get the list data, we first wash the data and insert an Edit [true or false] identifier into each data to indicate whether the current row is being edited. Then you can dynamically switch different corresponding views through V-show. The complete code

<el-table-column min-width="300px" label="Title">
  <template scope="scope">
    <el-input v-show="scope.row.edit" size="small" v-model="scope.row.title"></el-input>
    <span v-show=! "" scope.row.edit">{{ scope.row.title }}</span>
  </template>
</el-table-column>
<el-table-column align="center" label="Edit" width="120">
  <template scope="scope">
    <el-button v-show='! scope.row.edit' type="primary" @click='scope.row.edit=true' size="small" icon="edit"> </el-button> <el-button v-show='scope.row.edit' type="success" @click='scope.row.edit=false' size="small" icon="check"> Complete </el-button> </template> </el-table-column> copy codeCopy the code

Common pit Table

The business scenario of editing, creating, and deleting table elements via a dialog is more common than the previous two scenarios. And there are quite a few pits. First of all, we need to make it clear that VUE is an MVVM framework. Our traditional code is imperative programming. After we get the DOM of table, it is imperative to add, delete and change the DOM. Now, we’re using declarative programming, and we’re just going to focus on data, so we’re going to add, subtract, and change based on the list array. Here we also need to clarify a few considerations for vUE list rendering

Due to JavaScript limitations, Vue cannot detect the following array changes: * When you set an item directly with an index, e.g. Vm. items[indexOfItem] = newValue

So we want to change the value of the first item in the table. This. list[0]=newValue does not work.

// array.prototype.splice 'example1.items.splice(indexOfItem, 1, newValue) copy codeCopy the code

So we can go through

// Add data this.list.unshift(this.temp); // Delete data const index = this.list.indexof (row); This.list.splice (index, 1); this.list.splice(index, 1); Const index = this.list.indexof (row); // Delete data by splice // modify data const index = this.list.indexof (row); This.list. splice(index, 1,this.updatedData); // Replace data with splice to trigger view update replication codeCopy the code

Now we have added, deleted and changed the table, and the list View automatically responds to the change. There is also a small hole that needs to be main when modifying data. When we get the data to modify the row, we cannot assign it directly to the dialog, otherwise the following problem will occur.

! [img](data:image/svg+xml; utf8,)

As shown in the figure above, when we change state in the dialog, the state of the row in the table below the mask changes there as well. You can guess why. The assigned data is an OBjeC reference type that shares a memory region. Instead of waiting for a copy, we need to redirect to a new reference, as follows:

// The assignment Object is an obj this.objData= object.assign ({}, row) // so as not to share the same Object // We also have a neat defense newArray = oldarray.slice (); / / will slicecloneReturns a new array copy codeCopy the code

Tabs

Tabs are also commonly used in background projects. Suppose we have four TAB options, and each TAB will request data from the back end, but we want to only request data from the current TAB at the beginning, and the TAB will not be repeated as it switches back and forth, only instantiated once. The first thing that came to mind was to use v-if so that the tabs would not be mounted initially, but the problem was that every time the TAB component was clicked it would be remounted again, which we didn’t want to see, so we could use

instead.

When keep-alive wraps dynamic components, it caches inactive component instances rather than destroying them. It is an abstract component: it does not render a DOM element on its own, nor does it appear in the parent component chain.

So we can write tabs like this

<el-tabs v-model="activeTab">
  <el-tab-pane label="Introduction and Announcements" name="announcement">
    <announcement />
  </el-tab-pane>
  <el-tab-pane label="Information" name="information">
    <keep-alive>
      <information v-if="activeTab=='information'" />
    </keep-alive>
  </el-tab-pane>
  <el-tab-pane label="Live Stream Configuration" name="stream">
    <keep-alive>
      <stream v-if="activeTab=='stream'"/> </keep-alive> </el-tab-pane> </el-tabs> Copy the codeCopy the code

Select the selector


Select a value from obj. It requires that the same reference issue must remain. This means that when we echo the data, we want to find the position of the data in the ARR before canceling: demo. And that’s not in the case of remote search, which would have hurt. Vue-multiselect is recommended to solve the problem of Element select perfectly. At present, it is also a relatively easy to use vue Component, and the UI is very good-looking. I suggest you try it, it is really very good.


The Upload to Upload

Upload itself has nothing to say, the document is quite clear. Here I will mainly discuss how to combine Upload component and Qiniu direct transmission.

Here we choose the WAY of API direct transmission, that is, we first need to generate the necessary token(upload certificate) and key(the final name of the resource) through the back-end (go, Node, PHP can be) document. So now you just need to find a way to insert tokens and keys into the POST request. Fortunately, the authorities also provide this method.

! [img](data:image/svg+xml; utf8,)

. But how do you get the token asynchronously and then stuff it into the request?

! [img](data:image/svg+xml; utf8,)

At this point, we found that the before-upload hook also supported Promises, which we liked. But we said how can we dynamically change the previous dataObj? By looking at the source code we found that we can get the data we want by _self._data. Online code

<template>
  <el-upload
      action="https://upload.qbox.me"
      :data="dataObj"
      drag
      :multiple="true"
      :before-upload="beforeUpload">
    <i class="el-icon-upload"></i>
    <div class="el-upload__text"> Drag the file here, or <em> click upload </em></div> </el-upload> </template> <script> import {getToken} from'api/qiniu'; / / get seven cows token back-end through Access Key, the Secret Key, buckets, etc. To generate token seven cattle official SDK / / https://developer.qiniu.com/sdk#official-sdk
    export default{
      data() {
        return {
          dataObj: { token: ' ', key: ' ' },
          image_uri: [],
          fileList: []
        }
      },
      methods: {
        beforeUpload() {
          const _self = this;
          return new Promise((resolve, reject) => {
            getToken().then(response => {
              const key = response.data.qiniu_key;
              const token = response.data.qiniu_token;
              _self._data.dataObj.token = token;
              _self._data.dataObj.key = key;
              resolve(true);
            }).catch(err => {
              console.log(err)
              reject(false)}); }); }}} </script> Copy the codeCopy the code

jsx

When using Element, there are many official places where you can write your own render function, but because Element uses JSX to write its own render function, so the demo is also JSX, but many people do not have their own projects actually installed, resulting in error. But really writing the render function with createElement naked is a bit of a pain. Babel-plugin-transform-vue-jsx install babel-plugin-transform-vue-jsx

npm install\ babel-plugin-syntax-jsx\ babel-plugin-transform-vue-jsx\ babel-helper-vue-jsx-merge-props\ Babel-preset - ES2015 \ --save-dev copies the codeCopy the code

. Babelrc: file

{
  "presets": ["es2015"]."plugins": ["transform-vue-jsx"} copy the codeCopy the code

Then we can happily write render Function using JSX.


Element FaQs

** The click event does not trigger the problem:

click Me

The official state states that all native events must have the.native modifier.

Fixing Element styling issues: Using a UI component requires some customization, so we need to override some element styling. Vue Scoped is based on PostCss and uses the concept of a scope.

Example {color: red; } // after compiling.example[_v-f3f3eg9] { color: red; } Duplicate codeCopy the code

It is not fundamentally different from our traditional namespaces approach to avoiding CSS collisions. Now let’s talk about how to override the Element-UI style. Since the element-UI style was introduced globally, you can’t add scoped if you want to cover the style in a view, but if you want to cover only the element style in the page, you can add a class to its parent to solve the problem with namespaces.

.aritle-page{// your namespace. El-tag {// elder-ui element margin-right: 0px; }} Copy the codeCopy the code

Create an SCSS file for customizing element- UI styles. Online code

There is really nothing to say about other things related to element, other people’s documentation and source code are there, have a problem to look at the document, and then go to the issue, and then look at the source code, most of the problems can be solved. Give a trick Most weird problems can be solved by adding a key or vue.nexttick.


The rich text

Management background rich text is also a very important function, the building Lord here also stepped on a lot of pits. Tinymce was chosen for the project

Here’s a quick summary of the reasons for using Tinymce: Tinymce is an old rich text company (ckeditor is also a good new version of a rich text company), and its product has been well received by the market, both in terms of documentation and configuration freedom. In the use of rich text is also very key is to copy formatting, before using a Korean rich text Summernote was its formatting pit dead and alive, but Tinymce to formatting is quite good, it also has a value-added project is powerpaste, that is very powerful, Support from word inside copy a variety of things, there will be no problem. Rich text is also key to extensibility. Tinymce has written several plug-ins, learning cost and ease are good, very convenient to expand. The last point is that it’s well documented. It has almost every configuration item you can think of. Tinymce also supports on-demand loading, so you can customize your own plugins via its official Build page. Let me analyze some of the other rich texts on the market:

  • Summernote starts with a rich text that I definitely don’t recommend. This is a Korean open source rich text (not recommended for that reason, of course), it reverses much of the rich text industry’s accepted default behavior, and it just uses dialog functionality, introduces Boostrap, and a lot of people are protesting. The formatting is also poor. Don’t use it anyway! Don’t use! Don’t use!
  • Ckeditor ckeditor is also an old rich text company, the original version of the background is used for this, this year also released version 5.0, the UI has changed a lot of beautiful, quite good, and it claims to be the richest rich text plug-in. I recommend you to try it.
  • Quill is also a hot rich text and looks great. It’s also easy to write plug-ins based on it, and API design is simple. The reason why I don’t choose it is that it is not friendly to the various operations of pictures, and it is difficult to change. Recommended for users who don’t have much to do with images.
  • Medium -* Editor * Rich text of the famous Medium (unofficial production), but the completion is not very good, and the expansion is good. But I don’t think most users will get used to writing medium.
  • Squire is a lightweight rich text, at 11.5 KB compressed, very small compared to other rich text, and is recommended for use without complex features.
  • WangEditor is a rich text written by Chinese people. It feels good to use it. But after all is personal, unlike specialized companies do rich text, configuration type and lack of richness. Why are rich text editors called sinkholes? But it’s not easy for a person to do that.
  • Baidu UEditor has not been used in depth, only briefly in an angular1X project, but the UI mentioned is really not pretty and does not conform to today’s aesthetic, and the official hasn’t updated it for a long time.

This list contains a lot of rich text but does not list any VUe-related rich text, mainly because rich text is more complex than you think. As mentioned in the previous article, it is very convenient to encapsulate components in VUE. There’s no need to use something that they’ve wrapped, like vue-quill Vue-Editor, that’s just a simple package, it’s not that difficult. It’s better to encapsulate it yourself, which is more flexible and controllable. There is no good rich text based on Vue, unlike React with Draft-js from Facebook and Ory with Editor.

You can also choose from paid rich text editors, such as Froala-Editor, which the author uses for a project at his company. Both aesthetics and ease of use are good, the company bought the professional version, only $349 a year, the price is also very reasonable, but in fact, the cost of saving programmer development may be far more than that.

Tinymce

Here’s a quick guide to using Tinymce in your own projects.

Due to the complexity and problems of installing Tinymce using NPM at present (this mode may be adopted in the future). :space_invader:

Global references are currently used. Static /tinymce Static files are not packaged and imported in index.html.

Since rich text is not suitable for two-way data flow, watch will only pass in one change of the content of rich text, and then it will not listen again. If there is a need to change the content of rich text later. This can be set with this.refs.xxx.setContent()

Source code is very simple, also have any other requirements can be in @ / components/Tinymce/index. To modify the vue.


Markdown

Markdown we use Simplemde-markdown-editor here, simply encapsulate the address with VUE. If the requirements can accept markdown, they must use Markdown, which will be much less than rich text. Here we use Markdown as an editor, and we need something to parse. You can pass it to the back end and have the back end do it for you, or the front end can do it itself. Here’s a recommended conversion library, Moresco. Usage:

import('showdown'| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | // Initialize this.html = Converter.makehtml (this.content)// Convert}) copy the codeCopy the code

Markdown to HTML is easy to use in just two lines of code. Of course, it also has a lot of character drawing configuration, you can find your own needs.

! [img](data:image/svg+xml; utf8,)


Export excel

To be clear, if your business needs do not have any requirements on the format of the exported file, it is not recommended to export the file to XLSX format, directly export to CSV, really much simpler. Create an A tag and say data:text/ CSV; Charset = utF-8 header, encodeURI(csvContent); We focus on XLSX, we use jS-XLSX here, a very powerful Excel processing library, just download various formats of Excel, but also support to read Excel, but the difficulty is also very large, quite complex, which involves a lot of binary related things. But fortunately, the official gave us a demo example, we can not write to copy, so we borrowed the official example to transform, the specific principle is not detailed, really very complicated… It’s about how we use it! First we package an Export2Excel. Js, which in turn relies on three libraries

require('script-loader! file-saver'); // save the file with require('script-loader! vendor/Blob'); // require('script-loader! xlsx/dist/xlsx.core.min'); Since these files do not support import, we need 'script-loader' to mount them to the global environment. Copy the codeCopy the code

It exposes two interfaces: export_table_to_excel and export_json_to_excel. We use export_json_to_excel because it’s a little bit more controllable and we can freely wash data.

handleDownloadEnsure ([], () => {// use webpack Code Splitting XLSL const {export_json_to_excel} = require()'vendor/Export2Excel');
    const tHeader = ['number'.'Article Title'.'the writer'.'Reading number'.'Release time']; // Excel header const filterVal = ['id'.'title'.'author'.'pageviews'.'display_time']; const list = this.list; const data = this.formatJson(filterVal, list); Export_json_to_excel (tHeader, data,'list excel'); }, formatJson(filterVal, jsonData) {returnJsondata.map (v => filterval.map (j => v[j]))} copy codeCopy the code

Complete display of online code


ECharts

Managing back-end charts is also a common requirement. The chart here only recommends ECharts, full of features, and the community demo is also rich in gallery. My opinion remains that most plugins should be wrapped in vue, it’s really easy. ECharts = require(‘ ECharts ‘); ECharts = require(‘ ECharts ‘); Still, ECharts is not small, and for the most part we only use a small number of features, which I’m used to introducing on demand.

Var ECharts = require('echarts/lib/echarts'); // require('echarts/lib/chart/bar'); // Introduce prompt box and title component require('echarts/lib/component/tooltip');
require('echarts/lib/component/title'); Copy the codeCopy the code

[] used in webpack ECharts document (echarts.baidu.com/tutorial.ht… ECharts is used in webpack. ECharts imports module documentation on demand. Next we declare ECharts initialization in vUE. Since ECharts initializations must bind to the DOM, we can only initialize them during the Mounted life cycle of vue.

mounted() {
  this.initCharts();
},
methods: {
  this.initCharts() {
    this.chart = echarts.init(this.$el);
    this.setOptions();
  },
  setOptions() {
    this.chart.setOption({
      title: {
        text: 'Getting started with ECharts'
      },
      tooltip: {},
      xAxis: {
        data: ["Shirt"."Cardigan"."Chiffon shirt."."Pants"."High heels"."Socks"]
      },
      yAxis: {},
      series: [{
        name: 'sales'.type: 'bar', data: [5, 20, 36, 10, 10, 20]}]})}Copy the code

That’s it, ECharts is configured, what if you want to say my data is fetched remotely, or I’m changing ECharts configuration dynamically? We can use watch to trigger the setOptions method

// The first watch options change takes advantage of vue's deep watchersetOption
watch: {
  options: {
    handler(options) {
      this.chart.setOption(this.options)
    },
    deep: true{seriesData(val) {this.setoptions ({series:val})}Copy the code

In fact, are almost the same, or to combine their own business to encapsulate. There is no difference between using ECharts as usual. As an aside, ECharts has a lot of configurable options, so you might want to spend a little time learning about its API. Zhihu has a question: Baidu has what more conscientious products? Answer: ECharts, visible ECharts powerful and easy to use.


Different parameters for the same Component

The background create and edit function is the most common, which is different from the foreground project, but most of the create page and edit page fields and UI are almost the same, so we are going to use a common component for different pages. There are two common ways to distinguish between create and edit.

  1. The simplest and most violent way is to route the path. I use this method in my own project, and determine edit mode by convention if ‘edit’ appears in the path. It’s a lot easier and easier, but you have to follow the specification when you write paths.

  2. Compare recommendations by meta.

computed: {
  isEdit() {
    return this.$route.meta. IsEdit // Judge by meta //return this.$route.path.indexOf('edit')! == -1 // Judge by route}},created() {
  if(this.isEdit) { this.fetchData(); }}, copy the codeCopy the code

This simple implementation of multiple route reuse of a component, actually not only create and edit can be used in this way, like two lists are identical, but one is internal article and the other is external article can reuse components, through the meta way to determine the different interface call.