SpringBoot + Vue + ElementUI 實現後臺管理系統模板 -- 前端篇(四):引入 vuex 進行狀態管理、引入 vue-i18n 進行國際化管理

来源:https://www.cnblogs.com/l-y-h/archive/2020/05/26/12963576.html
-Advertisement-
Play Games

前提: (1) 相關博文地址: SpringBoot + Vue + ElementUI 實現後臺管理系統模板 -- 前端篇(一):搭建基本環境:https://www.cnblogs.com/l-y-h/p/12930895.html SpringBoot + Vue + ElementUI 實現 ...


前提:

(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;
        width: 50px;
        float: left;
        font-size: 50px;
        cursor: pointer;
    }

    .header-menu {
        height: 50px;
        width: 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-width: auto !important;
    }
    .header-span img {
        width: 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%; width: 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);
        width: 100%;
        max-width: 200px;
        background-color: #263238;
        text-align: left;
        right: 0;
    }

    .header-logo {
        background-color: #17b3a3;
        text-align: center;
        height: 50px;
        line-height: 50px;
        width: 200px;
        font-size: 24px;
        color: #fff;
        font-weight: bold;
        margin-bottom: 0;
        cursor: pointer;
    }
    .el-submenu .el-menu-item {
        max-width: 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;
    width: 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;
        width: 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;
        width: 120px;
    }
</style>

 

(3)頁面顯示

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ECharts安裝與使用 博客說明 文章所涉及的資料來自互聯網整理和個人總結,意在於個人學習和經驗彙總,如有什麼地方侵權,請聯繫本人刪除,謝謝! 安裝 下載地址 https://echarts.apache.org/zh/download.html 在使用的時候需要引入對應的js 使用CDN 可以使 ...
  • 1 分析: 2 需要動態的向資料庫中插入數據,既然要進行添加操作,就會觸發一個點擊的事件,所以我們的第一步就是要先綁定事件 3 向伺服器進行請求介面操作,請求的方法為 ‘POST’ 4 最後就是在methods中寫函數 5 操作: 6 (1)綁定事件 7 <view @tap="saveOpport ...
  • ECharts介紹 博客說明 文章所涉及的資料來自互聯網整理和個人總結,意在於個人學習和經驗彙總,如有什麼地方侵權,請聯繫本人刪除,謝謝! 說明 ECharts 是一個使用 JavaScript 實現的開源可視化庫,涵蓋各行業圖表,滿足各種需求 官網 https://echarts.apache.o ...
  • 大江東去,浪淘盡,千古風流人物。故壘西邊,人道是,三國周郎赤壁。亂石穿空,驚濤拍岸,捲起千堆雪。江山如畫,一時多少豪傑。遙想公瑾當年,小喬初嫁了,雄姿英發。羽扇綸巾,談笑間,檣櫓灰飛煙滅。故國神游,多情應笑我,早生華髮。人生如夢,一尊還酹江月。--來自奔跑的panda部落,panda每天與您一起進步 ...
  • 登高遠眺 滄海拾遺,積跬步以至千里 基礎技術 官宣: Typescript 3.9 正式發佈 TypeScript 3.9 正式發佈,這個版本主要聚焦於性能、改進某些特性和提升穩定性。編譯器效率在這一版有了極大提升,平均編譯時長從 26 秒縮短至 10 秒,改善了編輯體驗,同時修複了早期 Types ...
  • 首先前端三大件:前端的根本,html、css、JavaScript、JavaScript是重中之重。 庫工具:對於庫工具而言我們常用的有 JQuery、underScore、zepto、Moment 等 JQuery: 降低開發者操作 DOM 的複雜度 UnderScore: 提供實用的函數 Zep ...
  • 曾經某一刻,筆者也為下載離線地圖和載入地圖,煩惱過很久。在網上找了很多資料,都不全,不符合要求。要麼載入的百度、高德線上地圖;要麼就是沒有完整的離線地圖下載方式或者坐標體系對不上。搞了很久,也痛苦了很久,在這裡記錄一個完整的Demo。 離線地圖下載 以谷歌衛星地圖為例,下載某小區地圖。先用線條工具框 ...
  • 為什麼那麼多自學web前端的後來都放棄了,總結起來是這些原因 目前信息化產業發展勢頭很好,互聯網就成為了很多普通人想要涉及的行業,因為相比於傳統行業,互聯網行業漲薪幅度大,機會也多,所以就會有大批的人想要轉行從事這個行業,來學習web前端開發,目前來講市場上需要的web前端人員非常多,而且按照現在的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...