vue3后台管理系统(模板)
系统简介
- 此管理系统是基于Vite2和Vue3.0构建生成的后台管理系统。目的在于学习vite和vue3等新技术,以便于后续用于实际开发工作中;
- 本文章将从管理系统页面布局、vue路由鉴权、vuex状态管理、数据持久化、用户信息加密等方面进行介绍和记录;
- 这也是我边学习边实践的过程,此次记录一是方便自己日后开发过程中有用到时候便于借鉴和复习,再次是为了初学vue3和尝试上手vite2和vue3搭建管理系统的小伙伴提供一些学习方法和技术点;
- 本Vue后台管理系统使用的技术点主要有:vite2、vue3、vue-router4.x、vuex4.x、vuex-persistedstate(vuex数据持久化)、Element Plus等。
用户登录
登录页面代码
<template>
    <div class="login">
        <el-card class="login_center">
            <template #header>
                <div class="card_header">
                    <span>用户登录</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="请输入账号"
                        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="请输入密码"
                        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">登 录</el-button>
                </el-form-item>
            </el-form>
        </el-card>
    </div>
</template>
登录逻辑代码
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: "账号不能为空", trigger: "blur" }],
            pwd: [
                { required: true, message: "密码不能为空", trigger: "blur" },
                { min: 5, max: 16, message: "密码长度为5-16位", 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("登录成功");
                // 		} else {
                // 			ElMessage.error("登录失败:" + message);
                // 		}
                // 	})
                // 	.catch(err => {
                // 		console.log("login err", err);
                // 		ElMessage.error("登录失败");
                // 	});
            });
        };
        return { loginFormRef, loginFormState, rules, handleLogin };
    }
};
登录简介:
- 登录页面采用的是一级录用,与控制台的路由同级,这样写便于对vue-router路由权限校验的控制;
- 在vue2中我们频繁使用 this 来处理事件函数和组件数据,vue3大多事件函数和数据状态的存储基本都实在setup函数中完成的,在vue3中无法通过 this 来获取当前组件的实例,故无法像vue2中那样操作数据和事件函数;
- vue3中为了获取到当前组件的实例,我们可以采用 vue3中提供的 getCurrentInstance 来获取组件的实例;
- 当我们使用全局对象或者函数时,我们大多是将事件函数绑定在vue的原型实例上,当再次访问时只需使用过this来访问自己指定的事件名即可;
- 在vue3中我们若是使用全局变量或者事件函数时,我们需要借助 globalProperties 来实现全局事件函数的绑定;此时在需要使用的地方可以通过当前组件实例来访问全局的 property 属性;
- 对登录用的的信息进行加密处理,我采用的是 js-base64 的 encode 方法来实现登录信息的加密。使用方式为:encode(“需要加密的JSON字符串”)。
系统主页
Layout布局代码
<template>
    <el-header height="56px">
        <!-- header -->
        <div class="header_left">Element-Plus Create By Vite</div>
        <div class="header_right">
            <!-- 退出全屏、进入全屏按钮 -->
            <el-tooltip :content="isFullScreen ? '退出全屏' : '全屏'">
                <i class="el-icon-full-screen" @click="handleFullScreen"></i>
            </el-tooltip>
            <!-- 下拉菜单 -->
            <el-dropdown size="medium" @command="handleCommand">
                <!-- 用户信息 -->
                <div class="user_info">
                    <!-- 用户头像 -->
                    <el-avatar :size="36" :src="avatar" />
                    <!-- 用户名宁 -->
                    <span class="username">{{ userName }}</span>
                </div>
                <template #dropdown>
                    <!-- 折叠菜单 -->
                    <el-dropdown-menu>
                        <el-dropdown-item icon="el-icon-user" command="user">个人中心</el-dropdown-item>
                        <el-dropdown-item icon="el-icon-switch-button" command="logout">退出登录</el-dropdown-item>
                    </el-dropdown-menu>
                </template>
            </el-dropdown>
        </div>
    </el-header>
</template>
<!-- 二级路由公用路由页面 -->
<template>
    <router-view v-slot="{ Component }">
        <transition name="fade" mode="out-in">
            <component :is="Component" />
        </transition>
    </router-view>
</template>
主页Header相关逻辑
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("退出成功");
                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)
        };
    }
};
注:
- Header分左右两部分,其中左侧为系统的名字,右侧为用户登录的账户相关的信息以及进入和退出全屏的按钮;
- 不同用户权限会对应不同的账户头像,会对不同账户的用户权限做相应的限制处理;
- 全屏的切换借助的是第三方的插件进行处理的,此方式减少代码量的同时也减少了不同浏览器兼容性问题的出现;
- 退出账户逻辑的处理,当用户点击退出账户的时候进行相应的退出登录的弹窗提示,在退出后进行数据的初始化和本地存储信息的清除处理,并跳转到用户登录页。
- 主页使用了地图模块,地图模块是借助的“高德地图”API实现的H5版的网页地图,此Demo需要使用注册高德地图开发者来获取开发的keys来创建地图实例;
- 本笔记主要就后台管理系统做笔记分析,高德地图此处不做过多介绍,若想进一步了解,请前往高德开放平台进行了解学习。
数据管理
<template>
    <el-card shadow="never" class="index">
        <template #header>
            <div class="card_header">
                <b>数据列表</b>
            </div>
        </template>
        <el-empty description="暂无数据"></el-empty>
    </el-card>
</template>
<script></script>
<style lang="scss" scoped>
.card_header {
    display: flex;
    align-items: center;
    justify-content: space-between;
}
</style>
注:没有数据时的提示信息;
视频播放器
<template>
    <el-card shadow="never" class="index">
        <template #header>
            <div class="card_header">
                <b>西瓜播放器</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 /* 流式布局 */,
                // download: true /* 视频下载 */
                // pip: true /* 画中画 */,
                // errorTips: `请<span>刷新页面</span>试试` /* 自定义错误提示 */,
                lang: "zh-cn"
            });
        };
        return {};
    }
};
</script>
<style lang="scss" scoped>
.card_header {
    display: flex;
    align-items: center;
    justify-content: space-between;
}
</style>
注:
- 安装西瓜视频播放器:yarn add xgplayer
- 西瓜播放器官方文档:http://v2.h5player.bytedance.com/
- 西瓜播放器适合手机版和PC电脑版视频点播或直播使用,详细参数配置请参考官方文档。
富文本编辑器
富文本编辑器插件封装
<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);
            // 配置富文本
            Object.assign(instance.config, {
                zIndex: 100,
                // placeholder: "" /* 提示文字 */,
                showFullScreen: true /* 显示全屏按钮 */,
                showLinkImg: true /* 显示插入网络图片 */,
                showLinkVideo: true /* 显示插入网络视频 */,
                onchangeTimeout: 400 /* 触发 onchange 的时间频率,默认 200ms */,
                uploadImgMaxLength: 1 /* 单次上传图片数量限制 */,
                uploadImgMaxSize: 5 * 1024 * 1024 /* 上传图片大小限制 */,
                uploadVideoAccept: ["mp4", "mov"] /* 上传视频格式限制 */,
                uploadVideoMaxSize: 1024 * 1024 * 1024 /* 上传视频大小限制1024m */,
                excludeMenus: ["strikeThrough", "todo", "code"] /* 移除系统菜单 */,
                customAlert(msg, type) {
                    type == "success" ? proxy.$message.success(msg) : proxy.$message.error(msg);
                },
                customUploadImg(resultFiles, insertImgFn) {
                    /**
                     * @param {Object} file - 文件对象
                     * @param {String} rootPath - 文件根路径(默认为空、例:“filepath/”)
                     * @param {Array} fileType - 文件类型限制(默认 [] 不限制,例:['.png','.jpeg'])
                     * @param {Number} size - 文件大小限制(单位:兆、默认 0 不限制、例:1)
                     **/
                    proxy.$oss(resultFiles[0]).then(imgUrl => !!imgUrl && insertImgFn(imgUrl));
                },
                customUploadVideo(resultFiles, insertVideoFn) {
                    proxy.$oss(resultFiles[0]).then(videoUrl => !!videoUrl && insertVideoFn(videoUrl)); /* 参数同上 */
                },
                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>
组件内使用
<template>
    <el-card shadow="never" class="index">
        <template #header>
            <div class="card_header">
                <b>富文本编辑器</b>
            </div>
        </template>
        <!-- 富文本 -->
        <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(() => {
            // 初始化数据
            defaultText.value = "<h1>Editor</h1>";
        });
        const getRichHtml = nv => {
            richText.value = nv;
        };
        return { defaultText, getRichHtml };
    }
};
</script>
注:
- 此次是基于Vue3封装的富文本编辑器,编辑器使用的是开源的富文本编辑器wangeditor;
- 代码块一是基于官方文档和配置信息对富文本编辑器进行的相关配置,其中富文本编辑器使用的ali-OSS的云存储,若想详细了解请参照之前的“阿里云文件直传”博客笔记进行了解和学习;
- ref相当于DOM元素的Id,要保持唯一,若一个页面要使用多个富文本编辑器,请做好区分,以便于区分组件的数据。
个人中心
总结
本篇文章主要介绍使用element-plus进行页面的布局和数据展示处理,后续笔记将继续分享和介绍动态路由菜单的处理,以及用户权限的动态校验。(。・・)ノ
源码地址
vue3后台管理系统(模板)的更多相关文章
- vue3项目后台管理系统模板
		Vue3.0 发布第一个版本至今有一段时间了,到现在一直在更新优化,在性能方面,对比 Vue2.x ,性能的提升比较明显,打包后体积更小 来看下 Vue3.x 新增了哪些功能和特性. Performa ... 
- VUE3后台管理系统【路由鉴权】
		前言: 在"VUE3后台管理系统[模板构建]"文章中,详细的介绍了我使用vue3.0和vite2.0构建的后台管理系统,虽然只是简单的一个后台管理系统,其中涉及的技术基本都覆盖了, ... 
- Admin Panel  – 非常漂亮的后台管理系统模板
		网站或者应用系统的管理后台的设计虽然不像前台界面那样要求设计精美,但是也需要有清晰的管理模块划分,下面分享的这个后台管理模板的设计非常漂亮,特别是导航部分,头部还有未读的短消息和提醒的条数显示.赶紧下 ... 
- easyui+ztree 后台管理系统模板
		用easyui+ztree做了个后台管理系统模板,效果图: 下载地址: csdn:http://download.csdn.net/detail/jackpay/6744505 github:http ... 
- 3YAdmin-专注通用权限控制与表单的后台管理系统模板
		3YAdmin基于React+Antd构建.GitHub搜索React+Antd+Admin出来的结果没有上百也有几十个,为什么还要写这个东西呢? 一个后台管理系统的核心我认为应该是权限控制,表单以及 ... 
- Bootstrap 轻量级后台管理系统模板--ACE使用介绍
		在上一篇基于Bootstrap介绍了一个免费的后台管理模板Charisma UI. 参见链接: 基于Jquery.Bootstrap的后台管理免费UI框架推荐--Charisma UI 今天继续分享一 ... 
- SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(五): 数据表设计、使用 jwt、redis、sms 工具类完善注册登录逻辑
		(1) 相关博文地址: SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 前端篇(一):搭建基本环境:https://www.cnblogs.com/l-y-h/p ... 
- SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作
		相关 (1) 相关博文地址: SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 前端篇(一):搭建基本环境:https://www.cnblogs.com/l-y- ... 
- vue+element-ui JYAdmin后台管理系统模板-集成方案【项目搭建篇2】
		项目搭建时间:2020-06-29 本章节:讲述基于vue/cli, 项目的基础搭建. 本主题讲述了: 1.跨域配置 2.axios请求封装 3.eslint配置 4.环境dev,test,pro(开 ... 
随机推荐
- Django使用Ace实现在线编辑器
			前言:最近自己开发SQL工单功能,期间接触到了Ace在线编辑器,折腾一下,感觉功能挺多,特意去了解学习一下分享跟大家. ACE 是一个功能非常强大的编辑器,实现语法高亮.代码补全功能,还有很多主题,支 ... 
- TVM自定义数据类型
			TVM自定义数据类型 本文将介绍"自定义数据类型"框架,该框架可在TVM中使用自定义数据类型. 介绍 在设计加速器时,关键是如何近似地表示硬件中的实数.这个问题具有长期的行业标准解 ... 
- OFRecord 图片文件制数据集
			OFRecord 图片文件制数据集 在 OFRecord 数据格式 和 加载与准备 OFRecord 数据集 中,分别学习了 OFRecord 数据格式,以及如何将其它数据集转为 OFRecord 数 ... 
- TensorFlow Keras API用法
			TensorFlow Keras API用法 Keras 是与 TensorFlow 一起使用的更高级别的作为后端的 API.添加层就像添加一行代码一样简单.在模型架构之后,使用一行代码,可以编译和拟 ... 
- 在python_request 中 nb-log 日志模块的使用,应用到项目实际使用
			一.安装 pip install nb-log pycham 中安装: 二.基本使用 2.1 pycham中调整设置控制台日志打印出的颜色 2.2 设置完成后去掉console弹出的颜色设置 2.3 ... 
- NCF 如何导入Excel数据
			简介 学了上一节的WebApi之后,我们会发现一片新天地 本节跟大家聊一聊,如何把本地的Excel数据导入到NCF中 仓库地址:https://github.com/NeuCharFramework/ ... 
- python pyyaml操作yaml配置文件
			在测试工作中,可以使用yaml编写测试用例,执行测试用例时直接获取yaml中的用例数据进行测试(如:接口自动化测试) 1.什么是yaml 是一种可读的数据序列化语言,通常用于配置文件 非常简洁和强大, ... 
- Centos7 安装 Cacti 1.2
			1. 安装web server(略) 注意系统时间/时区 ntp server是否定时同步 2. 安装 mysql 5.7 (略) 安装成功后 2.1 编辑 my.ini 文件在 [mysqld] 下 ... 
- Idea的安装破解及配置
			安装激活 30天试用无线版 博客园下载地址:https://files.cnblogs.com/files/blogs/482725/无限30天试用插件.zip 百度云下载链接: https://pa ... 
- 【NX二次开发】获取用户输入的字符串uc1603
			效果: 源码: extern DllExport void ufsta(char *param, int *returnCode, int rlen) { UF_initialize(); strin ... 
