First, preparation

0. Set the minimum width of el-Container to prevent extrusion
0. Configure an alias for the directory
// in the vue.config.js file

const path = require('path')
// handle the path function
function resolve (dir) {
  console.log(__dirname)
  return path.join(__dirname, dir)
}


module.exports = {
  lintOnSave: true./ / enable eslint
  // Resolve [WDS] Disconnected! An error
  devServer: {
    headers: {
      'Access-Control-Allow-Origin': The '*'
    },
    hotOnly: true.disableHostCheck: true
  },
  // Go to the console plug-in for packaging
  configureWebpack: config= > {
    if (process.env.NODE_ENV === 'production') {
      config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true}},// Configure the alias
  chainWebpack (config) {
  config.resolve.alias.set('views',resolve('src/views')).set(The '@', resolve('src'))}}Copy the code
1. Login logic and token
1.Enter the user name and password to log in2.After verifying the successful login, the back-end server generates a token field and returns it to the front-end3.The front end stores tokens in a sessionStorage4.Each subsequent request carries this tokenCopy the code
2. Authorize API interfaces

In addition to the login interface, other apis that require Authorization must provide the token using the Authorization field in the request header

// Request the interceptor using AXIos
axios.interceptors.request.use(config= >{
  // Add the token authentication Authorization field for the request header object
  config.headers.Authorization = window.sessionStorage.getItem("token")
  return config
})
Copy the code
3. The left menu is highlighted by default after being refreshed
  • Method one:

    SessionStorage stores the unique index bound to the submenu option every time you click on the index, create inside the synchronization to data defined activePath, el-menu default-active binding to this activePath note: The value cannot be synchronized in the Mounted life cycle. For details, see events in each life cycleCopy the code
    https://zhuanlan.zhihu.com/p/71958016
    Copy the code

    Vue lifecycle diagram

  • Method 2: Bind $route.path to default-active of el-menu

Second, user management

4. Grid layout
5. Table Sets index columns
 <! -- Set index column -->
        <el-table-column
          label="#"
          type="index"
          width="60"
          align="center"
        >
        </el-table-column>
Copy the code
6. The scope slot implements the switch status
7. Trycatch usage
8. Form binding and validation
1.First define formData formData in data and form validation rules formRules. Object has multiple properties2.Not only is el-Input bound to the formData properties using the V-Model3.El-form is also bound to form data using :model4.El-form-item also uses prop to bind the validation rules specified in formRules. The property values can be arrays containing multiple rules (see prop)9The addUserFormRules)Copy the code
9. Customize verification rules for email and mobile phone numbers
1.Use the callback function to customize the validation rule in datareturnIn the2.Trigger: Blur is out of focus when the prop binding validator is used to point to the defined callbackCopy the code
// Callback defined in data
// Verify mailbox rules
    var checkEmail = (rule, value, cb) = > {
      // Validates the mailbox's regular expression
      const regEmail = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/;

      if (regEmail.test(value)) {
        // A valid email address
        return cb();
      }

      cb(new Error("Please enter a valid email address"));
    };

    // Verify the mobile phone number rule
    var checkMobile = (rule, value, cb) = > {
      // Verify the regular expression for the mobile phone number
      const regMobile = / ^ (0 | 86 | 17951)? (13 15 [012356789] [0-9] | | 17 [678] 18 [0-9] | | 14 [57]) [0-9] {8} $/;

      if (regMobile.test(value)) {
        return cb();
      }

      cb(new Error("Please enter a valid phone number."));
    };
Copy the code
// Define validation rules in data
// Add a validation rule object for the form
      addUserFormRules: {
        username: [{required: true.message: "Please enter user name".trigger: "blur" },
          {
            min: 3.max: 10.message: "The username must be between 3 and 10 characters long.".trigger: "blur"}].password: [{required: true.message: "Please enter your password".trigger: "blur" },
          {
            min: 6.max: 15.message: "The username must be between 6 and 15 characters long.".trigger: "blur"}].email: [{required: true.message: "Please enter email address".trigger: "blur" },
          { validator: checkEmail, trigger: "blur"}].mobile: [{required: true.message: "Please enter your mobile phone number.".trigger: "blur" },
          { validator: checkMobile, trigger: "blur"}}]Copy the code
10. Pre-verify the form before submitting it

If the prevalidation passes, the network request for form submission is made

// El-form-item binds prop's validation rule field with v-model to the value of the input box. El-form also binds the validation rule with :rule and the form data with :model
Copy the code
11. Side el-aside bottom scroll bar
Set the width of el-Aside to less than 200px, and when you click on the navigation bar inside the aside, a horizontal scroll bar appears at the bottom. After a simple test, there is no solution for width >=200px: Set an el-Scrollbar component outside of el-menu inside of el-Aside and set width in the style property:100%. In similar cases, if longitudinal roll occurs, set to height100%
Copy the code
12. List pagination
12-1. The current page number and number of pages per page are reset after the refresh
Ideas:1.Initial values pagenum and Pagesize defined in the pagination current-page and page-size binding data of the pagination component2.When you click change page number or capacity, go back inside the function and synchronize the current pagesize and Pagenum to sessionStorage3.The Created life cycle retrieves and assigns values to the original values and requests list dataCopy the code
Note: current-page is bound to page-sizeNumberType, to useparseInt() Convert typeCopy the code
created() {
    if (window.sessionStorage.getItem("currentPage")) {
      this.params.pagenum = parseInt(
        window.sessionStorage.getItem("currentPage")); }if (window.sessionStorage.getItem("currentPageSize")) {
      this.params.pagesize = parseInt(
        window.sessionStorage.getItem("currentPageSize")); }this.getUsers();
  }
Copy the code
12-2. Deleting the last page automatically jumps to the previous page
 // If the current page is not reconnected, determine the number of pages that should be in the current page, and then reassign pagenum
	// Since the total number of usersTotal data needs to be rerequested before the latest data can be synchronized, this calculation needs to be manually subtracted by 1
      let totalPage = Math.ceil((this.usersTotal - 1) / this.params.pagesize);
      let currentPage = this.params.pagenum > totalPage ? totalPage : this.params.pagenum;
      this.params.pagenum = currentPage < 1 ? 1 : currentPage;
 // Synchronize currentPage in sessionStorage
 window.sessionStorage.setItem("currentPage", currentPage);
Copy the code
// The pager code is as follows:<el-pagination
        :background="true"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="params.pagenum"
        :page-sizes="[2, 3, 4, 5]"
        :page-size="params.pagesize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="usersTotal"
      >
      </el-pagination>

Copy the code
// usersTotal is the total number of users defined in data, and params is the user request parameter
      params: {
        pagesize: 4.pagenum: 1.query: "" // The user searches for the keyword and binds it to the search input box
      },
Copy the code
12-3. Delete the data on the last page and refresh the display list. The data is empty
Because currentPage in sessionStorage was not synchronized when the last page was deleted, so the request to refresh is still using the currentPage value of the last page, so the request is still the data of the original last page, which is emptyCopy the code
 window.sessionStorage.setItem("currentPage", currentPage);
Copy the code

3. Permission management

13. The scope slot displays different grades of labels

Application scenario: The level returned by the interface is 0/1/2, which needs to be rendered into different labels in the table according to the level

The scope slot is implemented with V-IF condition judgment

<el-table-column prop="level" label="Permission level" align="center">
          <template slot-scope="level">
            <el-tag v-if="level.row.level === '0'" type="success">Grade a</el-tag>
            <el-tag v-else-if="level.row.level === '1'" type="warning">Level 2</el-tag>
            <el-tag v-else type="danger">Grade three</el-tag>
          </template>
</el-table-column>
Copy the code
Another idea: You can probably use filters as wellswitchImplement (??) Not yetCopy the code
14. Delete permissions TAB After view refresh, the expanded permissions list is closed again

Because the request after deletion is to request the permission data of all roles, so the page will be re-rendered, you can not re-request all data, only update all permission data of children assigned to the current role

15. Use recursion to obtain all the level-3 permission ids of the current role. El-tree is selected by default
 // Recursively obtain the id of the existing level 3 permission
    getDefaultKeys(data, arr) {
 1. Click Assign Permission, pass the data of the current role, obtain all the existing level 3 permission ids of the current role, form an array, and assign the value to defaultExpandedArray 2. If there are no children in roleData, push the id of the current object into array 3. If so, iterate through the children array and continue calling this.getDefaultKeys() for each element */
      if(! data.children) { arr.push(data.id);return false;
      } else {
        data.children.forEach(i= > this.getDefaultKeys(i, arr)); }},Copy the code
16. Assign permissions to the previous role and click the next role. By default, the permissions of the previous role are selected
// Before closing the dialog box, clear the default permissions of defaultCheckedKeysArray; otherwise, click the next role assignment permissions, the permissions of the previous role will be selected by default

 // Close the el-tree Permission assignment dialog box
    closeSetRightsDialog() {
      /* Before closing the dialog box, clear the default permissions for defaultCheckedKeysArray; otherwise, click the next role assignment permissions, the default permissions for the previous role */ will be selected
      this.defaultCheckedKeysArray = [];
    }
Copy the code
17. Vue-table-with-tree-grid Implements a tree table

vue-table-with-tree-grid

4. Commodity management

18. Moment.js format the time
  • Define global or local filters first

    import moment from 'moment'
    
    // Define a global time filter
    Vue.filter('dateFilter'.(time) = > {
      return moment(time).format('YYYY-MM-DD HH:mm:ss') // Format: 1970-01-18 20:39:05
    })
    
    Copy the code
  • reference

      <el-table-column
              header-align="center"
              align="center"
              width="250"
              label="Creation time">
              <template slot-scope="{row}">
                {{ row.add_time | dateFilter }}
              </template>
      </el-table-column>
    Copy the code
19. The el-Tab bar is linked to the El-Steps bar
https://i.postimg.cc/xTxK7ZMy/Screenshot-2.png
Copy the code
20. Hierarchical relationship between El-Tab and El-Form

El-form has to wrap El-Tab, And El-Tab-Pane has to wrap El-Form-Item inside

21. Use before-leave to judge before switching tabs

When adding an item, check whether the item category is selected when jumping from step 1 to step 2, otherwise prompt and block the jump

2. This.queryinfo.goods_cat.length! Returns false if == 3Copy the code
22. Upload an image, send a request and display an invalid token
// description upload a useless axios request
// Set the request header using the headers attribute

  uploadUrl: 'http://127.0.0.1:8888/api/private/v1/upload'.// Image upload server address
      headerObj: {
        // upload sets the request header
        Authorization: window.sessionStorage.getItem('token')}Copy the code
23. The image has been uploaded successfully, but the thumbnail is not displayed properly. It is a white square
// Because :file-list is bound to pICS in the submitted form information in the el-upload, the pICS array is pushed as follows:
[{"pic":"tmp_uploads\\6dc05d2211b648cefe5d4e593c952f8e.jpeg"."uid":1618827444464."status":"success"}]

// Normal should have PIC property only

Copy the code
24. Vue-quill-editor rich text editor use
25. Deep copy of _.clonedeep for Lodash

V. Data statistics

1. Use of E-Charts
  • Value initialized in the Mounted life cycle

Vi. Project optimization and launch

Blog.csdn.net/weixin_4438…

1. Top progress bar effect nprogress.js
  • Implemented in main.js using the AXIos interceptor
import Nprogress from 'nprogress'
import 'nprogress/nprogress.css'


// The progress bar is displayed in the request interceptor
axios.interceptors.request.use(config= > {
  Nprogress.start()
  // Add the token authentication Authorization field for the request header object
  config.headers.Authorization = window.sessionStorage.getItem('token')
  return config
})
// Stop the progress bar in response to interceptor
axios.interceptors.response.use(config= > {
  Nprogress.done()
  return config
})
Copy the code
  • Use the road to have guard implementation
router.beforeEach((to, from, next) = > {
if (to.path == '/login') {
    sessionStorage.removeItem('username');
  }
let user = sessionStorage.getItem('username');
if(! user && to.path ! ='/login') {
    next({path: '/login'})}else {
    NProgress.start();
    next()
  }
});

router.afterEach(transition= > {
  NProgress.done();
});
Copy the code
2. Remove all console during build
babel-plugin-transform-remove-consoleThe plug-inCopy the code
Process.env.node_env === 'production'
Copy the code
3. Generate a packaged report

4. Specify different packaging entry points for development mode and release mode

// make two copies of main.js and name them main-dev.js/main.prod.js respectively
// chainWebpack is used in vue.config.js
module.exports = {
    // Specify different package entry files using when to determine the environment
    // Publish mode
    config.when(process.env.NODE_ENV === 'production'.config= > {
      // entry Finds the default packing entry. Clear deletes the default packing entry
      // add adds a new packing entry
      config.entry('app').clear().add('./src/main-prod.js')})// Development mode
    config.when(process.env.NODE_ENV === 'development'.config= > {
      config.entry('app').clear().add('./src/main-dev.js')})}Copy the code
5. Use externals to load external CDN resources

  • The configuration is as follows:

6. Optimize packaging element-UI

7. Customize the homepage title

8. Lazy route loading
  • To use Babel, install @babel/plugin-syntax-dynamic-import

  • Declare the plug-in in babel.config.js

  • Change the component import mode in routing

9. Enable Gzip compression

The scope is usually back-end, requiring the server to be written by Node, and possibly front-end