zoukankan      html  css  js  c++  java
  • SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 前端篇(四):引入 vuex 进行状态管理、引入 vue-i18n 进行国际化管理

    前提:

    (1) 相关博文地址:

    SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 前端篇(一):搭建基本环境:https://www.cnblogs.com/l-y-h/p/12930895.html
    SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 前端篇(二):引入 element-ui 定义基本页面显示:https://www.cnblogs.com/l-y-h/p/12935300.html
    SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 前端篇(三):引入 js-cookie、axios、mock 封装请求处理以及返回结果:https://www.cnblogs.com/l-y-h/p/12955001.html

    (2)代码地址:

    https://github.com/lyh-man/admin-vue-template.git

    一、引入 vuex 进行状态管理

    1、简介

      vuex指的是一种状态管理模式,集中式管理所有组件的状态(管理数据)。

    【vuex 官方文档:】
        https://vuex.vuejs.org/zh/guide/
    
    【vuex 参考地址:】
        https://www.cnblogs.com/l-y-h/p/11666653.html

    使用场景分析:
      之前 Home.vue 页面中,Header 部分有个折叠按钮,点击之后,可以折叠与展开 Aside 组件,这之间就设计到数据在组件间的共享。
      之前使用 this.$emit 触发父组件的方法,然后通过 props 属性传递数据来实现的。

      上面一种方式,虽然可以实现,但是组件太多的话,数据传递起来会很复杂。
      使用 vuex 后,数据统一管理,当数据发生变化时,其所有引用的地方均会修改。

    2、安装、模块化使用 vuex

    (1)安装
      项目构建时,已经安装过了。
      可以使用 npm 手动安装,然后在 vuex 中全局引入。

    【npm 安装:】
        npm install vuex

    如下图:项目已经构建好了,在 store 文件夹下进行相关编写即可。

    (2)引入 vuex

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      state: {
      },
      mutations: {
      },
      actions: {
      },
      modules: {
      }
    })

    (3)根据功能拆分成各个模块
      所有的状态管理写在一个 js 里,不方便维护。
      可以根据不同的功能,抽取去不同的模块 js 进行处理。
    比如:登录模块可以定义 user.js 去处理、公共模块可以定义 common.js 去处理。

    如下:定义一个 user.js,其中 state 管理一个 userName 数据,当登录成功时,保存该数据,并显示在主页面的右上角。

    export default {
        // 开启命名空间(防止各模块间命名冲突),访问时需要使用 模块名 + 方法名
        namespaced: true,
        // 管理数据(状态)
        state: {
            // 用于保存用户名
            userName: 'Admin'
        },
        // 更改 state(同步)
        mutations: {
            updateName(state, data) {
                if (data) {
                    state.userName = data
                } else {
                    state.userName = 'Admin'
                }
            }
        },
        // 异步触发 mutations
        actions: {
            updateName({commit, state}, data) {
                commit("updateName", data)
            }
        }
    }

    (4)将定义好的模块,在 index.js 中引入。

    import Vue from 'vue'
    import Vuex from 'vuex'
    import user from './module/user.js'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
        modules: {
            user
        }
    })

    (5)使用
      在组件中,可以引用 state、action 。
    其中:
      使用 $store.state 可以引用 state 中的数据,也可使用 mapState 替代 $store.state。
      使用 $store.dispatch 可以引用 action 中的方法,也可使用 mapActions 替代·$store.dispatch。

    如下:
      在 Header.vue 页面中,使用 mapState 引入 userName,并显示。
      在 computed 中 引入 state 定义的属性(可以使用 数组 或者 对象的形式)。
      由于使用了 模块进行封装,所以在引入时,第一个参数需要指定 模块名。

    import {mapState} from 'vuex'
    export default {
        computed: {
            // ...mapState('user', {userName: 'userName'}),
            ...mapState('user', ['userName'])
        }
    }

    在 Login.vue 中引入 action 方法,用于修改 用户名。

    import {mapActions} from 'vuex'
    
    export default {
        data() {
            return {
                dataForm: {
                    userName: '',
                    password: ''
                }
            },
            methods: {
                ...mapActions('user', ['updateName'])
           }
        }
    }

    简单测试一下:
      输入用户名,登录后,右上角显示 自定义用户名。
      不输入用户名,登录后,右上角显示 Admin(默认)。

    二、引入 vue-i18n 进行国际化管理

    1、简介

      一般项目都要求多语言显示,比如: 中文、英文 快速切换。
      使用 vue-i18n 插件可以快捷、方便的进行这种操作。

    【官方文档:】
        https://kazupon.github.io/vue-i18n/zh/introduction.html

    2、安装、使用

    (1)npm 安装

    npm install vue-i18n

    (2)引入 vue-i18n
      在 src 下新建一个 i18n 目录,并创建一个 index.js,用于引入 vue-i18n。
      并引入 各语言文件。

    import Vue from 'vue'
    // 引入 VueI18n
    import VueI18n from 'vue-i18n'
    // 全局挂载 VueI18n
    Vue.use(VueI18n)
    
    // 创建 i18n 实例,并引入语言文件(可以是 js 文件、也可以为 json 文件)
    const i18n = new VueI18n({
        // locale 为语言标识,通过切换locale的值来实现语言切换( this.$i18n.locale )
        locale: 'zh',
        messages: {
            'zh': require('@/i18n/languages/zh.json'),
            'en': require('@/i18n/languages/en.json')
        }
    })
    
    export default i18n

    (3)定义各语言文件
      此处以 zh.json、en.json 为例(也可以定义成 js 文件)。

    【zh.json】
    {
        "login": {
            "title": "管理员登录",
            "userName": "用户名",
            "password": "密码",
            "language": "语言选择",
            "signIn": "登录",
            "userNameNotNull": "用户名不能为空",
            "passwordNotNull": "密码不能为空",
            "signInSuccess": "登录成功"
        },
        "language": {
            "setting": "设置",
            "languageSettings": "语言设置: ",
            "zh": "中文",
            "en": "英语"
        },
        "header": {
            "foldAside": "折叠侧边栏",
            "unFoldAside": "展开侧边栏",
            "setUp": "设置",
            "help": "帮助",
            "blogAddress": "博客地址",
            "codeAddress": "代码地址",
            "userSetUp": "用户设置",
            "updatePassword": "修改密码",
            "logOut": "退出"
        },
        "aside": {
            "adminCenter": "后台管理中心",
            "admin": "后台",
            "homePage": "首页"
        }
    }
    
    
    【en.json】
    {
        "login": {
            "title": "Administrator Login",
            "userName": "UserName",
            "password": "Password",
            "language": "Language",
            "signIn": "SignIn",
            "userNameNotNull": "UserName Not Null",
            "passwordNotNull": "Password Not Null",
            "signInSuccess": "SignIn Success"
        },
        "language": {
            "setting": "Settings",
            "languageSettings": "Language Settings: ",
            "zh": "Chinese",
            "en": "English"
        },
        "header": {
            "foldAside": "Fold Aside",
            "unFoldAside": "Un Fold Aside",
            "setUp": "SetUp",
            "help": "Help",
            "blogAddress": "Blog Address",
            "codeAddress": "Code Address",
            "userSetUp": "User SetUp",
            "updatePassword": "Update Password",
            "logOut": "LogOut"
        },
        "aside": {
            "adminCenter": "Admin Center",
            "admin": "AC",
            "homePage": "Home Page"
        }
    }

    (4)在 main.js 中全局引入 定义好的 i18n。

    import i18n from '@/i18n/index.js'
    
    new Vue({
        router,
        store,
        i18n,
        render: h => h(App)
    }).$mount('#app')

    (5)根据 zh.json、en.json 定义好的数据,将各个组件中的中文替换掉。
      使用 $t("") 去替换值。形式:{{$t("")}} 或者 this.$t("")。
    如下例:
      替换 Login.vue 中的数据(留个坑)。

    (6)填坑
      上面我留了个坑,在 data 中使用 this.$t(""),修改语言后,不会变化。
      可以将其写在 computed 属性中。
    如下例,在 Header.vue 中,使用一个 language 对象进行国际化管理。

    computed: {
        // 定义国际化显示
        language() {
            return {
                foldAside: this.$t("header.foldAside"),
                unFoldAside: this.$t("header.unFoldAside"),
                setUp: this.$t("header.setUp"),
                help: this.$t("header.help"),
                blogAddress: this.$t("header.blogAddress"),
                codeAddress: this.$t("header.codeAddress"),
                userSetUp: this.$t("header.userSetUp"),
                updatePassword: this.$t("header.updatePassword"),
                logOut: this.$t("header.logOut")
            }
        }
    }

    替换后的 Header.vue 如下,其余页面修改类似修改即可。

    <template>
        <div class="header">
            <!-- 是否展开侧边栏 -->
            <div class="header-title" @click="foldOrOpen">
                <a class="el-icon-s-fold" v-if="foldAside" :title="language.foldAside" />
                <a class="el-icon-s-unfold" v-else :title="language.unFoldAside" />
            </div>
            <!-- 设置、文档、用户设置等 -->
            <div class="header-menu">
                <el-menu mode="horizontal" class="header-menu-submenu">
                    <!-- 设置 -->
                    <el-menu-item :title="language.setUp" index="1" @click="showSetup">
                        <i class="el-icon-setting"></i>{{language.setUp}}
                    </el-menu-item>
                    <!-- 帮助文档 -->
                    <el-submenu :title="language.help" index="2">
                        <template slot="title">
                            <i class="el-icon-info"></i>{{language.help}}
                        </template>
                        <el-menu-item index="2-1">
                            <a href="https://www.cnblogs.com/l-y-h/" target="_blank" class="header-submenu-a">{{language.blogAddress}}</a>
                        </el-menu-item>
                        <el-menu-item index="2-2">
                            <a href="https://github.com/lyh-man/admin-vue-template.git" target="_blank" class="header-submenu-a">{{language.codeAddress}}</a>
                        </el-menu-item>
                    </el-submenu>
                    <!-- 用户设置 -->
                    <el-submenu :title="language.userSetUp" index="3">
                        <template slot="title">
                            <span class="header-span">
                                <img src="~@/assets/avatar.gif" :alt="userName"> {{ userName }}
                            </span>
                        </template>
                        <el-menu-item index="3-1" @click="showPasswordBox">
                            <i class="el-icon-edit"></i>{{language.updatePassword}}
                        </el-menu-item>
                        <el-menu-item index="3-2" @click="logout">
                            <i class="el-icon-close"></i>{{language.logOut}}
                        </el-menu-item>
                    </el-submenu>
                </el-menu>
            </div>
            <!-- 密码修改框 -->
            <UpdatePassword v-if="updatePasswordVisible" ref="updatePassowrd"></UpdatePassword>
            <!-- 设置框 -->
            <Setup v-if="setUpVisible" ref="setUp"></Setup>
        </div>
    </template>
    
    <script>
        import UpdatePassword from '@/views/home/UpdatePassword.vue'
        import Setup from '@/views/home/Setup.vue'
        import {mapState} from 'vuex'
        export default {
            name: 'Header',
            data() {
                return {
                    // 是否展开侧边栏
                    foldAside: true,
                    // 默认用户名
                    // userName: 'admin',
                    // 是否展开密码框
                    updatePasswordVisible: false,
                    // 是否展开设置框
                    setUpVisible: false
                }
            },
            computed: {
                // ...mapState('user', {userName: 'userName'}),
                ...mapState('user', ['userName']),
                // 定义国际化显示
                language() {
                    return {
                        foldAside: this.$t("header.foldAside"),
                        unFoldAside: this.$t("header.unFoldAside"),
                        setUp: this.$t("header.setUp"),
                        help: this.$t("header.help"),
                        blogAddress: this.$t("header.blogAddress"),
                        codeAddress: this.$t("header.codeAddress"),
                        userSetUp: this.$t("header.userSetUp"),
                        updatePassword: this.$t("header.updatePassword"),
                        logOut: this.$t("header.logOut")
                    }
                }
            },
            components: {
                // 引入密码框组件
                UpdatePassword,
                // 引入设置框组件
                Setup
            },
            methods: {
                // 展开设置框
                showSetup() {
                    this.setUpVisible = true;
                    this.$nextTick(() => {
                        this.$refs.setUp.init()
                    })
                },
                // 展开密码修改框
                showPasswordBox() {
                    this.updatePasswordVisible = true
                    // this.$nextTick 表示数据渲染后,执行密码框初始化
                    this.$nextTick(() => {
                        this.$refs.updatePassowrd.init()
                    })
                },
                // 展开、折叠侧边栏
                foldOrOpen() {
                    this.foldAside = !this.foldAside
                    // this.$emit 用于触发父组件的方法,并传递参数值
                    this.$emit("foldOrOpenAside", this.foldAside)
                },
                // 退出登录,回到登录界面
                logout() {
                    // TODO:退出逻辑待完成
                    // alert("退出逻辑未完成");
                    this.$router.push({
                        name: "Login"
                    })
                }
            }
        }
    </script>
    
    <style>
        .header {
            padding: 0 10px;
            display: flex;
            height: 50px;
            line-height: 50px;
        }
    
        .header-title {
            height: 50px;
             50px;
            float: left;
            font-size: 50px;
            cursor: pointer;
        }
    
        .header-menu {
            height: 50px;
             100%;
            flex: 1;
            line-height: 50px;
            font-size: 30px;
        }
        .header-menu-submenu {
            float: right;
        }
        .header-submenu-a {
            text-decoration: none;
            color: #4CC4B8;
            font-weight: bold;
            font-size: 16px;
        }
        .header-submenu-a:hover {
            background-color: #2C3E50;
        }
        .el-menu--horizontal>.el-menu-item,
        .el-menu--horizontal>.el-submenu .el-submenu__title {
            height: 50px !important;
            line-height: 50px !important;
        }
        .el-menu--collapse .el-menu .el-submenu, .el-menu--popup {
            min- auto !important;
        }
        .header-span img {
             40px;
            height: 40px;
            line-height: 40px;
            margin: 5px 10px 10px 10px;
        }
        .header-span {
            font-size: 20px;
        }
    </style>

    替换后的 Aside.vue 如下:

    <template>
        <div>
            <!-- 系统 Logo -->
            <el-aside class="header-logo" :width="asideWidth">
                <div @click="$router.push({ name: 'Home' })">
                    <a v-if="foldAside">{{language.adminCenter}}</a>
                    <a v-else>{{language.admin}}</a>
                </div>
            </el-aside>
            <el-aside class="aside" :width="asideWidth" :class='"icon-size-" + iconSize'>
                <el-scrollbar style="height: 100%;  100%;">
                    <!--
                            default-active 表示当前选中的菜单,默认为 home。
                            collapse 表示是否折叠菜单,仅 mode 为 vertical(默认)可用。 
                            collapseTransition 表示是否开启折叠动画,默认为 true。
                            background-color 表示背景颜色。
                            text-color 表示字体颜色。
                        -->
                    <el-menu :default-active="menuActiveName || 'home'" :collapse="!foldAside" :collapseTransition="false"
                     background-color="#263238" text-color="#8a979e">
                        <el-menu-item index="home" @click="$router.push({ name: 'Home' })">
                            <i class="el-icon-s-home"></i>
                            <span slot="title">{{language.homePage}}</span>
                        </el-menu-item>
                        <el-submenu index="demo">
                            <template slot="title">
                                <i class="el-icon-star-off"></i>
                                <span>demo</span>
                            </template>
                            <el-menu-item index="demo-echarts" @click="$router.push({ name: 'Echarts' })">
                                <i class="el-icon-s-data"></i>
                                <span slot="title">echarts</span>
                            </el-menu-item>
                            <el-menu-item index="demo-ueditor" @click="$router.push({ name: 'Ueditor' })">
                                <i class="el-icon-document"></i>
                                <span slot="title">ueditor</span>
                            </el-menu-item>
                        </el-submenu>
                    </el-menu>
                </el-scrollbar>
            </el-aside>
        </div>
    </template>
    
    <script>
        export default {
            name: 'Aside',
            props: ['foldAside'],
            data() {
                return {
                    // 保存当前选中的菜单
                    menuActiveName: 'home',
                    // 保存当前侧边栏的宽度
                    asideWidth: '200px',
                    // 用于拼接当前图标的 class 样式
                    iconSize: 'true'
                }
            },
            computed: {
                // 国际化
                language() {
                    return {
                        adminCenter: this.$t("aside.adminCenter"),
                        admin: this.$t("aside.admin"),
                        homePage: this.$t("aside.homePage")
                    }
                }
            },
            watch: {
                // 监视是否折叠侧边栏,折叠则宽度为 64px。
                foldAside(val) {
                    this.asideWidth = val ? '200px' : '64px'
                    this.iconSize = val
                }
            }
        }
    </script>
    
    <style>
        .aside {
            margin-bottom: 0;
            height: 100%;
            max-height: calc(100% - 50px);
             100%;
            max- 200px;
            background-color: #263238;
            text-align: left;
            right: 0;
        }
    
        .header-logo {
            background-color: #17b3a3;
            text-align: center;
            height: 50px;
            line-height: 50px;
             200px;
            font-size: 24px;
            color: #fff;
            font-weight: bold;
            margin-bottom: 0;
            cursor: pointer;
        }
        .el-submenu .el-menu-item {
            max- 200px !important;
        }
        .el-scrollbar__wrap {
            overflow-x: hidden !important;
        }
        .icon-size-false i {
            font-size: 30px !important;
        }
        .icon-size-true i {
            font-size: 18px !important;
        }
    </style>

    3、在主界面导航栏添加一个设置页面 Setup.vue

    (1)简介
      用于定义系统设置,比如设置语言。

    (2)定义页面内容:
      用于之前引入了 vuex,可以使用 vuex 保存 language 的状态。
      添加一个 common.js 模块,用于保存公共的状态。

    export default {
        // 开启命名空间(防止各模块间命名冲突),访问时需要使用 模块名 + 方法名
        namespaced: true,
        // 管理数据(状态)
        state: {
            // 用于保存语言设置(国际化),默认为中文
            language: 'zh'
        },
        // 更改 state(同步)
        mutations: {
            updateLanguage(state, data) {
                state.language = data
            }
        },
        // 异步触发 mutations
        actions: {
            updateLanguage({commit, state}, data) {
                commit("updateLanguage", data)
            }
        }
    }

    将定义好的模块在 vuex 的入口文件 index.js 中引入。

    引入 vuex,通过 ...mapState 以及 ...mapActions 引入。
    {{$t("")}} 进行国际化处理。

    <template>
        <el-dialog :title="setUp.setting" :visible.sync="visible" :append-to-body="true">
            <el-row :gutter="20">
                <el-col :span="7" :offset="2">
                    {{setUp.languageSettings}}
                </el-col>
                <el-col :span="4">
                    <el-radio v-model="language" label="zh">{{setUp.zh}}</el-radio>
                </el-col>
                <el-col :span="4">
                    <el-radio v-model="language" label="en">{{setUp.en}}</el-radio>
                </el-col>
            </el-row>
        </el-dialog>
    </template>
    
    <script>
        import {mapState, mapActions} from 'vuex'
        export default {
            name: 'setUp',
            data() {
                return {
                    visible: false,
                    language: 'en'
                }
            },
            computed: {
                ...mapState('common', {lang: 'language'}),
                setUp() {
                    return {
                        setting: this.$t("language.setting"),
                        languageSettings: this.$t("language.languageSettings"),
                        zh: this.$t("language.zh"),
                        en: this.$t("language.en")
                    }
                }
            },
            methods: {
                ...mapActions('common', ['updateLanguage']),
                // 初始化
                init() {
                    this.visible = true
                    this.language = this.lang
                    this.$i18n.locale = this.lang
                }
            },
            watch: {
                language(val) {
                    this.updateLanguage(val)
                    this.$i18n.locale = val
                }
            }
        }
    </script>
    
    <style>
    </style>

    (3)在 Header.vue 中引入,引入方式类似于 UpdatePassword.vue
    大概步骤:
      Step1:import 导入 vue 组件。
      Step2:在 components 中声明组件。
      Step3:添加开关属性,并指定方法触发组件。

    (4)页面显示如下:

    4、在登录界面添加一个语言选择框

      上面在主界面的导航栏上添加了一个语言设置选项,但是登录界面没有语言设置选项,看着有点别扭,给登录界面加上一个语言选择框,可以将数据传向后台,控制后台的国际化。

    (1)添加一个语言选择框

    <el-form-item>
        <el-select v-model="dataForm.language" :placeholder="language" class="login-select">
            <el-option :label="zh" value="zh"></el-option>
            <el-option :label="en" value="en"></el-option>
        </el-select>
    </el-form-item>
    
    
    【样式显示有些问题,稍作调整】
    .el-scrollbar__wrap {
        overflow-x: scroll !important;
    }
    
    .login-select {
        left: -120px;
         120px;
    }

    (2)同样引入 vuex 对语言进行管理(写法类似于 Setup.vue)。
    Step1:
      通过 mapActions 引入 common 模块中的方法:,并定义方法进行操作。

    import { mapActions } from 'vuex'
    
    ...mapActions('common', {updateLang: "updateLanguage"})
    
    updateLanguage() {
        this.$i18n.locale = this.dataForm.language
        this.updateLang(this.dataForm.language)
    }

    Step2:
      定义触发方法,通过 el-select 标签的 change 事件可以触发。

    <el-form-item>
        <el-select v-model="dataForm.language" :placeholder="language" class="login-select" @change="updateLanguage">
            <el-option :label="zh" value="zh"></el-option>
            <el-option :label="en" value="en"></el-option>
        </el-select>
    </el-form-item>

    完整的 Login.vue:

    <template>
        <div class="login-wrapper">
            <div class="login-content">
                <div class="login-main">
                    <h2 class="login-main-title">{{language.title}}</h2>
                    <el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" status-icon>
                        <el-form-item prop="userName">
                            <el-input v-model="dataForm.userName" :placeholder="language.userName"></el-input>
                        </el-form-item>
                        <el-form-item prop="password">
                            <el-input v-model="dataForm.password" type="password" :placeholder="language.password"></el-input>
                        </el-form-item>
                        <el-form-item>
                            <el-select v-model="dataForm.language" :placeholder="language.language" class="login-select" @change="updateLanguage">
                                <el-option :label="language.zh" value="zh"></el-option>
                                <el-option :label="language.en" value="en"></el-option>
                            </el-select>
                        </el-form-item>
                        <el-form-item>
                            <el-button class="login-btn-submit" type="primary" @click="dataFormSubmit()">{{language.signIn}}</el-button>
                        </el-form-item>
                    </el-form>
                </div>
            </div>
        </div>
    </template>
    <script>
        import {
            mapState,mapActions
        } from 'vuex'
        export default {
            data() {
                return {
                    dataForm: {
                        userName: '',
                        password: '',
                        language: 'zh'
                    },
                    dataRule: {
                        userName: [{
                            required: true,
                            message: this.$t("login.userNameNotNull"),
                            trigger: 'blur'
                        }],
                        password: [{
                            required: true,
                            message: this.$t("login.passwordNotNull"),
                            trigger: 'blur'
                        }]
                    }
                }
            },
            computed: {
                // 国际化
                language() {
                    return {
                        title: this.$t("login.title"),
                        userName: this.$t("login.userName"),
                        password: this.$t("login.password"),
                        language: this.$t("login.language"),
                        zh: this.$t("language.zh"),
                        en: this.$t("language.en"),
                        signIn: this.$t("login.signIn")
                    }
                }
            },
            methods: {
                ...mapActions('user', ['updateName']),
                ...mapActions('common', {updateLang: "updateLanguage"}),
                // 提交表单
                dataFormSubmit() {
                    // TODO:登录代码逻辑待完善
                    // alert("登录代码逻辑未完善")
                    this.$http({
                        url: '/auth/token',
                        method: 'get'
                    }).then(response => {
                        this.$message({
                            message: this.$t("login.signInSuccess"),
                            type: 'success'
                        })
                        this.updateName(this.dataForm.userName)
                        console.log(response)
                        this.$router.push({
                            name: 'Home'
                        })
                    })
                },
                updateLanguage() {
                    this.$i18n.locale = this.dataForm.language
                    this.updateLang(this.dataForm.language)
                }
            },
            created() {
                // 页面创建时,获取当前系统语言,并显示在下拉框中
                this.dataForm.language = this.$i18n.locale
            }
        }
    </script>
    <style>
        .login-wrapper {
            position: absolute;
            top: 0;
            right: 0;
            bottom: 0;
            left: 0;
            overflow: hidden;
            background-color: rgba(38, 50, 56, .6);
            background: url(~@/assets/login_bg.jpg) no-repeat;
            background-size: 100% 100%;
        }
    
        .login-content {
            position: absolute;
            top: 0;
            right: 0;
            bottom: 0;
            left: 0;
            margin: auto;
            height: 350px;
             400px;
            background-color: #112234;
            opacity: .8;
        }
    
        .login-main {
            color: beige;
            padding: 20px 20px 10px 20px;
        }
        
        .el-scrollbar__wrap {
            overflow-x: scroll !important;
        }
        
        .login-select {
            left: -120px;
             120px;
        }
    </style>

    (3)页面显示

  • 相关阅读:
    程序员装逼指南(网上看到的,笑喷了……)
    【转】Flex4 中skinclass使用Path绘制多边形
    批处理根据星期自动备份到服务器的相应文件夹
    精妙Sql语句备忘
    The project file ' ' has been renamed or is no longer in the solution. 解决方案
    HttpPostedFileBase always null when upload file by asp.net mvc4 mobile
    解决.net绘制的 WinForm 在 windows7下变形的方法
    select语句中,直接为选出的数据添加uniqueidentifier字段
    向各位请教一个关于Decorator模式的问题
    子查询基础知识
  • 原文地址:https://www.cnblogs.com/l-y-h/p/12963576.html
Copyright © 2011-2022 走看看