系统简介

用户登录

vue后台管理系统(vue后台管理系统最新版本)(1)

登录页面代码

<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后台管理系统(vue后台管理系统最新版本)(2)

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)
        };
    }
 };

注:

数据管理

vue后台管理系统(vue后台管理系统最新版本)(3)

<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>

注:没有数据时的提示信息;

视频播放器

vue后台管理系统(vue后台管理系统最新版本)(4)

<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>

注:

富文本编辑器

vue后台管理系统(vue后台管理系统最新版本)(5)

富文本编辑器插件封装

<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>

注:

个人中心

vue后台管理系统(vue后台管理系统最新版本)(6)