describe

Recently, WHEN WRITING Vue3, I used Element-Plus. I don’t know if it was because of the team, but I found that each version had different features or style differences, for example

  1. "Element - plus" : "6" ^ 1.1.0 - beta.The following versions modify the theme style configuration andbeta.6The above version is not the same writing method to configure
  2. Some components have been renamed:<el-submenu>renamed<el-sub-menu>
  3. <el-dialog>Use in componentsreactiveBound response data andrefThe declared ring data has different behavior, but does not report errors

After comparing the team’s more stable Ant Design Vue, we found that this framework is poor in animation switching, and the icon components need to be introduced and used one by one. Compared with element-UI, there are still some minor flaws. I decided to write a background management template that does not need to rely on any UI framework to be used, so that when a new, user-friendly, favorite UI framework appears, it can be very convenient and fast coupled in.

The code address

Preview the address

Layout The overall core layout

Overall layout function list

Side menu bar with routing permissions

Route navigation breadcrumb + Route history TAB combination top bar

Route view Content body + Route cache configuration

Side menu bar (special instructions)

Before writing this article, I did some research on how often business people (who are daily users) use abbreviated menu features like the one below…

Their answers were pretty much what I expected, that the purpose was to see more text, not to use a shortened version of the menu navigation; And when the number of menu levels reaches 3 or above, this function is very useless. For example, when I want to find a sub-menu, I need to put the mouse on it layer by layer to find and click one by one. Since this feature is not very useful as a user, the next step is to remove this useless feature and keep only the core basic display feature (which can be experienced in the preview address above).

Routing permissions

Let’s take a look at the data structure, which is basically the same routing configuration as before, with one or two additional fields as function permission markers

import { RouteRecordRaw } from "vue-router";

export interface RouteMeta {
    /** Sidebar menu name, document.title */
    title: string./ * * ` SVG ` * /icon? :string
    /** Whether the route is not displayed in the side menu bar */hidden? :boolean
    /** * Whether to cache routes * - When set to 'true', the route must be set to 'name', as well as' name 'in the page component, otherwise the route cache will not take effect */keepAlive? :boolean
}

/** Custom route type - inherit from 'RouteRecordRaw' */
export type RouteItem = {
    /** * Routing name, similar to unique 'key' * - Routing layer 1 must be set, because dynamic routing deletion is required, and unique * - When 'meta. KeepAlive' is set to 'true', this value is mandatory and unique, and the component 'name' also needs to be synchronized. Otherwise, the route cache does not take effect */name? :string
    /** External chain address, the priority will be higher than 'path' */link? :string
    /** * Array of user types that can access this permission, corresponding to 'userinfo.type'; * pass an empty array or don't write this field representatives can all users access to * * | number * | | | user type * | -- - | -- - | | | 0 super administrator * | 1 | | * / ordinary usersauth? :Array<number>
    /** Sub-route */children? :Array<RouteItem>
    Header / * * * /
    meta: RouteMeta
} & RouteRecordRaw
Copy the code

Note that the path field is required to complete the path in the routing submenu, i.e., the submenu, like this:

const example = [
    {
        path: "/example".name: "example".component: Layout,
        meta: { title: "Sample page column".icon: "menu" },
        redirect: "/example/request".children: [{path: "/example/request".// Complete this,
                // path: "/request", //
                name: "example-request".meta: { title: "HTTP - Sample Request".keepAlive: true },
                component: () = > import(".. /views/example/xxx.vue"}]}]Copy the code

User role permission routing is configured separately for each route according to the auTH field. The side bar menu is very simple, implementation is in after the success of the login, or have been login, routing initialization, inside the component made of the array filtering, which is based on the routing of the array auth and hidden whether to configure the display, and then generate a menu bar, see code comments prompted several other fields.

HTTP Network Request

Here I use ajax (code address) written native according to personal habits, the reason is that the code is less, the function is enough; You can also extend third-party libraries like AXIos if you like.

SVG Icon Component

How to use: Download the ICONS you want from ali Cloud icon library, then download the SVG file and put it in the SRC/ICONS/SVG directory

Also write a loader, the code is very simple:

import { readFileSync, readdirSync } from "fs";

// svg-sprite-loader does not work in vite
// This file can only be imported as' vite.config.ts'
// An error will be reported when imported elsewhere, because the browser environment does not support the 'fs' module

/** 'id' prefix */
let idPerfix = "";

const svgTitle = /
      
       +].*?) >/
      ([^>;

const clearHeightWidth = /(width|height)="([^>+].*?) "/g;

const hasViewBox = /(viewBox="[^>+].*?" )/g;

const clearReturn = /(\r)|(\n)/g;

/** * Find 'SVG' file *@param Dir File directory */
function findSvgFile(dir: string) :Array<string> {
    const svgRes = []
    const dirents = readdirSync(dir, {
        withFileTypes: true
    })
    for (const dirent of dirents) {
        if(dirent.isDirectory()) { svgRes.push(... findSvgFile(dir + dirent.name +"/"));
        } else {
            const svg = readFileSync(dir + dirent.name).toString().replace(clearReturn, "").replace(svgTitle, (value, group) = > {
                // console.log(++i)
                // console.log(dirent.name)
                let width = 0;
                let height = 0;
                let content = group.replace(clearHeightWidth, (val1: string, val2: string, val3: number) = > {
                        if (val2 === "width") {
                            width = val3;
                        } else if (val2 === "height") {
                            height = val3;
                        }
                        return ""; })if(! hasViewBox.test(group)) { content +=`viewBox="0 0 ${width} ${height}"`;
                }
                return `<symbol id="${idPerfix}-${dirent.name.replace(".svg"."")}" ${content}> `;
            }).replace("</svg>"."</symbol>"); svgRes.push(svg); }}return svgRes;
}

/** * 'SVG' packer *@param Path Resource path *@param Perfix suffix (label 'ID' prefix) */
export function svgBuilder(path: string, perfix = "icon") {
    if (path.trim() === "") return;
    idPerfix = perfix;
    const res = findSvgFile(path);
    // console.log(res.length)
    return {
        name: "svg-transform".transformIndexHtml(html: string) {
            return html.replace("<body>".`<body>
                <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
                ${res.join("")}
                </svg>`)}}}Copy the code

State management

After Vue3, I don’t need Vuex (although I don’t use Vue2 either). Instead, I use typescript-friendly design patterns with less code, more intuitive code snippets: Extract shared data into a separate module file, declare it in Reactive, and then export it to components for use. You don’t need VUEX.

For state management, one might say, with Vue 3.x, why not use hooks instead of a global singleton store? Yes, it is possible to use the hooks design pattern instead of the global singleton store. At first I rewrite the time of this project is to use hooks, then find the code more, the use of hooks are too scattered, each method, each variable to export import to use, this led to I rely too much on a component or page used, hooks too much imported, and too ugly, if the name is not standard, There will also be functions or variables with the same name. Sometimes too much subdivision of code does not lead to proper code maintenance, so a modular singleton makes sense. So I switched back to singleton mode.

Vue 2.x

The main branch of the current project, master, is similarly refactoring independently of the UI framework, for the same reason as in the beginning: it can be easily and quickly coupled in when a preferred UI framework is available