🍎 System Overview

  • This management system is a background management system based on Vite2 and Vue3.0. The purpose is to learn new technologies such as Vite and VUE3, so that they can be used in practical development.
  • This article will introduce and record the page layout of the management system, VUE route authentication, VUEX state management, data persistence, user information encryption and other aspects.
  • This is also the process of my learning and practice. This record is for the convenience of myself in the future development process for reference and review. Again, it is to provide some learning methods and technical points for those who are new to vue3 and those who try to build management systems with VITE2 and VUe3.
  • This Vue background management system uses the following technologies: Vite2, VUe3, VUE-Router4. x, vuex4.x, Vuex-PersistedState (VUEX data persistence), Element Plus, etc.

🚗 User Login

Login page code

<template>
    <div class="login">
        <el-card class="login_center">
            <template #header>
                <div class="card_header">
                    <span>The user login</span>
                </div>
            </template>
            <el-form :model="loginFormState" :rules="rules" ref="loginFormRef">
                <el-form-item prop="name">
                    <el-input
                        prefix-icon="el-icon-user-solid"
                        v-model.trim="loginFormState.name"
                        maxlength="32"
                        placeholder="Please enter your account number"
                        clearable
                    ></el-input>
                </el-form-item>
                <el-form-item prop="pwd">
                    <el-input
                        prefix-icon="el-icon-lock"
                        v-model.trim="loginFormState.pwd"
                        maxlength="16"
                        show-password
                        placeholder="Please enter your password"
                        clearable
                        @keyup.enter.exact="handleLogin"
                    ></el-input>
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" style="width: 100%" :loading="loginFormState.loading" @click="handleLogin">Log in</el-button>
                </el-form-item>
            </el-form>
        </el-card>
    </div>
</template>
Copy the code

Logon logic code

import { getCurrentInstance, reactive, ref } from "vue";
import { useRouter } from "vue-router";
import { useStore } from "vuex";
import { encode } from "js-base64";

export default {
    setup() {
        const { proxy } = getCurrentInstance();
        const router = useRouter();
        const store = useStore();
        const loginFormRef = ref();

        const loginFormState = reactive({
            name: "".pwd: "".loading: false
        });

        const rules = {
            name: [{ required: true.message: "The account cannot be empty.".trigger: "blur"}].pwd: [{required: true.message: "Password cannot be empty.".trigger: "blur" },
                { min: 5.max: 16.message: "Password length is 5-16 characters.".trigger: "blur"}};const handleLogin = () = > {
            loginFormRef.value.validate(valid= > {
                if(! valid) {return false;
                }

                loginFormState.loading = true;

                let params = { name: loginFormState.name, pwd: loginFormState.pwd };

                setTimeout(() = > {
                    let users = { role: loginFormState.name === "admin" ? "admin" : "".username: loginFormState.name };
                    Object.assign(params, users);
                    sessionStorage.setItem("jwt", encode(JSON.stringify(params)));
                    store.dispatch("setUser", params);
                    loginFormState.loading = false;
                    router.replace("/");
                }, 1000);

                // proxy.$axios
                // .post("/user/login", proxy.$qs.stringify(params))
                // .then(res => {
                // let { code, result_data, message } = res.data;
                // if (code == 1) {
                // console.log("login_success", result_data);
                // elmessage. success(" login succeeded ");
                // } else {
                // elmessage. error(" login failed: "+ message);
                / /}
                / /})
                // .catch(err => {
                // console.log("login err", err);
                // elmessage. error(" login failed ");
                / /});
            });
        };

        return{ loginFormRef, loginFormState, rules, handleLogin }; }};Copy the code

Login Introduction:

  • The login page uses level 1 recruitment, which is at the same level as the route on the console. In this way, it is convenient to control the vue-Router route permission verification.
  • In VUe2, we frequently use this to process event functions and component data. In VUe3, most event functions and data state storage are basically completed in the SETUP function. In VUe3, we cannot use this to obtain the instance of the current component, so we cannot operate data and event functions as in VUe2.
  • To obtain the instance of the current component in vue3, we can use getCurrentInstance provided in VUe3 to obtain the instance of the component.
  • When we use global objects or functions, we mostly bind the event function to the prototype instance of vue, and when we access it again, we only need to use this to access the event name we specify.
  • When using global variables or event functions in VUe3, we need to bind global event functions with globalProperties. The global property property can be accessed through the current component instance where it is needed;
  • To encrypt the information used for login, I use encode method of JS-Base64 to encrypt the login information. Use it as: encode(” JSON string that needs encryption “).

🚆 System home page

Layout code

<template>
    <el-header height="56px">
        <! -- header -->
        <div class="header_left">Element-Plus Create By Vite</div>
        <div class="header_right">
            <! -- Exit full screen, enter full screen button -->
            <el-tooltip :content="isFullScreen ? 'Exit full screen' : 'Full screen '">
                <i class="el-icon-full-screen" @click="handleFullScreen"></i>
            </el-tooltip>
            <! -- Dropdown menu -->
            <el-dropdown size="medium" @command="handleCommand">
                <! -- User information -->
                <div class="user_info">
                    <! -- User avatar -->
                    <el-avatar :size="36" :src="avatar" />
                    <! -- Username ning -->
                    <span class="username">{{ userName }}</span>
                </div>
                <template #dropdown>
                    <! -- Fold menu -->
                    <el-dropdown-menu>
                        <el-dropdown-item icon="el-icon-user" command="user">Personal center</el-dropdown-item>
                        <el-dropdown-item icon="el-icon-switch-button" command="logout">Log out</el-dropdown-item>
                    </el-dropdown-menu>
                </template>
            </el-dropdown>
        </div>
    </el-header>
</template>

<! -- Public Routing page -->
<template>
    <router-view v-slot="{ Component }">
        <transition name="fade" mode="out-in">
            <component :is="Component" />
        </transition>
    </router-view>
</template>
Copy the code

Home Header logic

import { computed, getCurrentInstance, reactive, toRefs } from "vue";
import { useRouter } from "vue-router";
import { useStore } from "vuex";
import screenfull from "screenfull";
import avatar from "@/assets/img/admin.png";

export default {
    setup() {
        const { proxy } = getCurrentInstance();
        const router = useRouter();
        const store = useStore();

        const state = reactive({
            isFullScreen: false,
            avatar,
            screenfull
        });
        const userName = computed(() = > store.getters.getUserName);

        const handleCommand = command= > {
            if (command === "user") {
                router.push("/user");
            } else {
                proxy.$message.success("Exit successful");
                store.dispatch("clearUser");
                router.replace("/login");
                sessionStorage.clear();
                localStorage.clear(); }};const handleFullScreen = () = > {
            if (screenfull.isEnabled) {
                state.isFullScreen = !state.isFullScreen;
                screenfull.toggle();
            }
        };

        return {
            userName,
            handleCommand,
            handleFullScreen,
            ...toRefs(state)
        };
    }
};
Copy the code

Note:

  • Header is divided into left and right parts. The left side is the name of the system, and the right side is the information related to the user’s login account and the buttons for entering and exiting the full-screen.
  • Different user permissions will correspond to different profile pictures of accounts, and the user permissions of different accounts will be restricted accordingly.
  • The full-screen switch is processed by third-party plug-ins, which reduces the amount of code and the compatibility problems of different browsers.
  • Log out of the account. When the user clicks to log out of the account, a pop-up prompt will be displayed to prompt the user to log out. After logging out, the user will initialize the data and clear the local storage information, and jump to the user login page.
  • The home page uses the map module, the map module is the H5 version of the webpage map with the help of “Amap” API, this Demo needs to register amAP developers to obtain the development keys to create map instances;
  • This note mainly analyzes the background management system. Amap is not introduced here. If you want to know more about it, please go to The Open platform of Autonavi for further study.

📕 Data Management

<template>
    <el-card shadow="never" class="index">
        <template #header>
            <div class="card_header">
                <b>Data list</b>
            </div>
        </template>
        <el-empty description="No data at present"></el-empty>
    </el-card>
</template>

<script></script>

<style lang="scss" scoped>
.card_header {
    display: flex;
    align-items: center;
    justify-content: space-between;
}
</style>
Copy the code

Note: Prompt message when there is no data;

📷 Video player

<template>
    <el-card shadow="never" class="index">
        <template #header>
            <div class="card_header">
                <b>🍉 Watermelon Player</b>
            </div>
        </template>
        <div id="xg"></div>
    </el-card>
</template>

<script>
import { onMounted, onBeforeUnmount, getCurrentInstance, ref } from "vue";
import Player from "xgplayer";

export default {
    setup() {
        const { proxy } = getCurrentInstance();

        let player;
        onMounted(() = > {
            initPlayer();
        });

        onBeforeUnmount(() = > {
            player.destroy();
            player = null;
        });

        const initPlayer = () = > {
            player = new Player({
                id: "xg".url: "https://sf1-cdn-tos.huoshanstatic.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-360p.mp4".poster: "https://img03.sogoucdn.com/app/a/07/f13b5c3830f02b6db698a2ae43ff6a67".fitVideoSize: "auto".fluid: true /* Streaming layout */.// Download: true /* Video */
                // PIP: true /* picture in picture */,
                // errorTips: 'Please  refresh the page  try' /* Custom error message */,
                lang: "zh-cn"
            });
        };

        return{}; }};</script>

<style lang="scss" scoped>
.card_header {
    display: flex;
    align-items: center;
    justify-content: space-between;
}
</style>
Copy the code

Note:

  • Install the watermelon video player yarn add xgPlayer
  • 🍉 watermelon player official documentation: v2.h5player.bytedance.com/
  • Watermelon player is suitable for video on demand or live broadcast of mobile phone version and PC version. For detailed parameter configuration, please refer to the official document.

🖊 rich text editor

Rich text editor plug-in encapsulation

<template>
    <div ref="editor" class="editor_ref"></div>
</template>

<script>
import { onMounted, onBeforeUnmount, watch, getCurrentInstance, ref } from "vue";
import WEditor from "wangeditor";

export default {
    props: {
        defaultText: { type: String.default: ""}},setup(props, context) {
        const { proxy } = getCurrentInstance();
        const editor = ref();

        let instance;
        onMounted(() = > {
            initEditor();
        });

        onBeforeUnmount(() = > {
            instance.destroy();
            instance = null;
        });

        watch(
            () = > props.defaultText,
            nv= >{ instance.txt.html(nv); !!!!! nv && context.emit("richHtml", nv); });const initEditor = () = > {
            instance = new WEditor(editor.value);
            // Configure rich text
            Object.assign(instance.config, {
                zIndex: 100.// placeholder: "/* placeholder text */,
                showFullScreen: true /* Display full screen button */.showLinkImg: true /* Display insert network picture */.showLinkVideo: true /* Display insert network video */.onchangeTimeout: 400 /* The time and frequency at which onchange is triggered. Default: 200ms */.uploadImgMaxLength: 1 /* Maximum number of images uploaded */.uploadImgMaxSize: 5 * 1024 * 1024 /* Upload image size limit */.uploadVideoAccept: ["mp4"."mov"] /* Upload video format limit */.uploadVideoMaxSize: 1024 * 1024 * 1024 /* Upload video size limit 1024m */.excludeMenus: ["strikeThrough"."todo"."code"] /* Remove system menu */.customAlert(msg, type) {
                    type == "success" ? proxy.$message.success(msg) : proxy.$message.error(msg);
                },
                customUploadImg(resultFiles, insertImgFn) {
                    / * * *@param {Object} file- File object *@param {String} rootPath- File root path (empty by default, for example, filepath/) *@param {Array} fileType- File type restriction (default [] is not restricted, for example: ['.png','.jpeg']) *@param {Number} size- File size limit (unit: MB, default: 0, example: 1) **/
                    proxy.$oss(resultFiles[0]).then(imgUrl= >!!!!! imgUrl && insertImgFn(imgUrl)); },customUploadVideo(resultFiles, insertVideoFn) {
                    proxy.$oss(resultFiles[0]).then(videoUrl= >!!!!! videoUrl && insertVideoFn(videoUrl));/* As above */
                },
                onchange(nv) {
                    context.emit("richHtml", nv); }}); instance.create(); };return{ editor }; }};</script>

<style scoped>
div.editor_ref :deep(iframe) {
    max-width: 100%;
    max-height: auto;
    width: 360px;
    height: 180px;
}
</style>
Copy the code

In-component use

<template>
    <el-card shadow="never" class="index">
        <template #header>
            <div class="card_header">
                <b>Rich text editor</b>
            </div>
        </template>
        <! -- Rich text -->
        <WEditor :defaultText="defaultText" @richHtml="getRichHtml" />
    </el-card>
</template>

<script>
import { onMounted, ref } from "vue";
import WEditor from ".. /.. /components/WEditor.vue";

export default {
    components: { WEditor },
    setup() {
        const defaultText = ref("");
        const richText = ref("");

        onMounted(() = > {
            // Initialize the data
            defaultText.value = "<h1>Editor</h1>";
        });

        const getRichHtml = nv= > {
            richText.value = nv;
        };

        return{ defaultText, getRichHtml }; }};</script>
Copy the code

Note:

  • This time it is a rich text editor based on Vue3 encapsulation. The editor uses the open source rich text editor WangEditor.
  • The first code block is related to the configuration of the rich text editor based on official documents and configuration information. The ali-OSS cloud storage is used by the rich text editor. If you want to know more about it, please refer to the previous blog notes of “Ali Cloud File Direct Transmission” for understanding and learning.
  • Ref is equivalent to the Id of a DOM element. Keep it unique. If you use multiple rich text editors on a page, make it easy to distinguish between components’ data.

⚽ Personal Center

🎉 summary

This article mainly introduces the use of element-Plus for page layout and data display processing. The following notes will continue to share and introduce the processing of dynamic routing menu and dynamic verification of user rights. (. ノ…)

🚀 source address

Vue3 Background management system: gitee.com/z_xiaokang/…