主頁 > 企業開發 > SpringBoot + Vue + ElementUI 實作后臺管理系統模板 -- 前端篇(六):使用 vue-router 進行動態加載選單

SpringBoot + Vue + ElementUI 實作后臺管理系統模板 -- 前端篇(六):使用 vue-router 進行動態加載選單

2020-09-18 12:28:47 企業開發

前提:

(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
SpringBoot + Vue + ElementUI 實作后臺管理系統模板 -- 前端篇(四):引入 vuex 進行狀態管理、引入 vue-i18n 進行國際化管理:https://www.cnblogs.com/l-y-h/p/12963576.html
SpringBoot + Vue + ElementUI 實作后臺管理系統模板 -- 前端篇(五):引入 vue-router 進行路由管理、模塊化封裝 axios 請求、使用 iframe 標簽嵌套頁面:https://www.cnblogs.com/l-y-h/p/12973364.html

(2)代碼地址:

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

 

一、獲取動態選單資料

1、介紹

(1)為什么使用動態加載選單?
  在之前的 router 中,選單的顯示都是寫死的,
  而實際業務需求中,可能需要動態的從后臺獲取選單資料并顯示,這就需要動態加載選單了,
比如:
  需要根據用戶權限,去展現不同的選單選項,

(2)專案中如何使用?
  專案中使用時,各個 vue 組件事先定義好,并存放在固定位置,
  用戶登錄成功后,根據后臺回傳的選單資料,動態拼接路由資訊,并顯示相應的選單項(不同權限用戶顯示不同選單),
  點擊選單項,會連接到相應的 vue 組件,跳轉路由并顯示,

2、使用 mock 模擬回傳 動態選單資料

(1)Step1:
  封裝一個 選單請求模塊,并引入,
  之前博客中已介紹使用了 模塊化封裝 axios 請求,這里新建一個 menu.js 用來處理選單相關的請求,

import http from '@/http/httpRequest.js'

export function getMenus() {
    return http({
        url: '/menu/getMenus',
        method: 'get'
    })
}

 

 

 引入 封裝好的模塊,

 

 

 

(2)Step2:
  使用 mock 模擬選單資料,
  封裝一個 menu.js 并引入,

import Mock from 'mockjs'

// 登錄
export function getMenus() {
    return {
        // isOpen: false,
        url: 'api/menu/getMenus',
        type: 'get',
        data: {
            'code': 200,
            'msg': 'success',
            'data': menuList
        }
    }
}

/*
    menuId       表示當前選單項 ID
    parentId     表示父選單項 ID
    name_zh      表示選單名(中文)
    name_en      表示選單名(英語)
    url          表示路由跳轉路徑(自身模塊 或者 外部 url)
    type         表示當前選單項的級別(0: 目錄,1: 選單項,2: 按鈕)
    icon         表示當前選單項的圖示
    orderNum     表示當前選單項的順序
    subMenuList  表示當前選單項的子選單
*/
var menuList = [{
        'menuId': 1,
        'parentId': 0,
        'name_zh': '系統管理',
        'name_en': 'System Control',
        'url': '',
        'type': 0,
        'icon': 'el-icon-setting',
        'orderNum': 0,
        'subMenuList': [{
                'menuId': 2,
                'parentId': 1,
                'name_zh': '管理員串列',
                'name_en': 'Administrator List',
                'url': 'sys/UserList',
                'type': 1,
                'icon': 'el-icon-user',
                'orderNum': 1,
                'subMenuList': []
            },
            {
                'menuId': 3,
                'parentId': 1,
                'name_zh': '角色管理',
                'name_en': 'Role Control',
                'url': 'sys/RoleControl',
                'type': 1,
                'icon': 'el-icon-price-tag',
                'orderNum': 2,
                'subMenuList': []
            },
            {
                'menuId': 4,
                'parentId': 1,
                'name_zh': '選單管理',
                'name_en': 'Menu Control',
                'url': 'sys/MenuControl',
                'type': 1,
                'icon': 'el-icon-menu',
                'orderNum': 3,
                'subMenuList': []
            }
        ]
    },
    {
        'menuId': 5,
        'parentId': 0,
        'name_zh': '幫助',
        'name_en': 'Help',
        'url': '',
        'type': 0,
        'icon': 'el-icon-info',
        'orderNum': 1,
        'subMenuList': [{
            'menuId': 6,
            'parentId': 5,
            'name_zh': '百度',
            'name_en': 'Baidu',
            'url': 'https://www.baidu.com/',
            'type': 1,
            'icon': 'el-icon-menu',
            'orderNum': 1,
            'subMenuList': []
        }, {
            'menuId': 7,
            'parentId': 5,
            'name_zh': '博客',
            'name_en': 'Blog',
            'url': 'https://www.cnblogs.com/l-y-h/',
            'type': 1,
            'icon': 'el-icon-menu',
            'orderNum': 1,
            'subMenuList': []
        }]
    }
]

 

 在 index.js 中宣告,并開啟 mock 攔截,

import * as menu from './modules/menu.js'
fnCreate(menu, true)

 

 

(3)使用
  使用時,通過如下代碼可以訪問到 mock,
  this.$http 是封裝的 axios 請求(詳情可見:https://www.cnblogs.com/l-y-h/p/12973364.html)

// 獲取動態選單
this.$http.menu.getMenus().then(response => {
    console.log(response)
})

 

(4)簡單測驗一下
  在 Login.vue 的登錄方法中,簡單測驗一下,

dataFormSubmit() {
    this.$http.login.getToken().then(response => {
        // 獲取動態選單
        this.$http.menu.getMenus().then(response => {
            console.log(response)
        })
        this.$message({
            message: this.$t("login.signInSuccess"),
            type: 'success'
        })
        // 保存 token
        setToken(response.data.token)
        this.updateName(this.dataForm.userName)
        console.log(response)
        this.$router.push({
            name: 'Home'
        })
    })
}

 

 

 

(5)測驗結果

 

 

 

3、動態選單資料國際化問題

(1)問題
  動態選單資料國際化問題,資料都是保存在資料庫中,中文系統下回傳英文資料、或者 英文系統下回傳中文資料,感覺都有些別扭,所以需要進行國際化處理,某語言系統回傳該語言的資料,

(2)解決
思路一:
  從資料庫下手,給表維護欄位,比如:name-zh、name-en,使用時,根據語言選擇對應的欄位顯示即可,修改時修改語言對應的欄位即可,簡單明了,
  當然這個缺點也很明顯,增加了額外的欄位,如果還有其他語言,那么會增加很多額外的欄位,

思路二:
  也從資料庫下手,額外增加一個語言資料表,里面維護了各種語言下對應的資料,使用時,從這張表查對應的資料即可,修改時也只要修改這張表對應的欄位即可,
  缺點也有,畢竟多維護了一張資料表,

思路三:
  資料庫不做國際化處理,由前端或者后端去做,資料庫維護的是 國際化的變數名,即從資料庫取出資料后,根據該資料回傳國際化資料,
  缺點嘛,國際化資料都寫死了,無法直接修改,需要修改國際化的組態檔,

(3)專案中使用
  在這個專案中,我采用第一種方式,此專案就是一個練手的專案,國際化也就 中英文切換,增加一個額外的欄位影響不大,所以 上面 mock 資料中,我使用了 name_zh 表示中文、name_en 表示英語,
  當然,有更好的方式,還望大家不吝賜教,

 

4、修改 router 獲取動態選單資料

(1)思路:
Step1:首先確定獲取動態選單資料的時機,
  一般在登錄成功跳轉到主頁面時,獲取動態選單資料,
  由于涉及到路由的跳轉(登錄頁面 -> 主頁面),所以可以使用 beforeEach 進行路由守衛操作,
  但由于每次路由跳轉均會觸發 beforeEach ,所以為了防止頻繁獲取動態路由值,可以使用一個全域的布爾型變數去控制,比如 false 表示未獲取動態路由,獲取動態路由后,將其置為 true,每次路由跳轉時,判斷該值是否為 true,為 false 就重新獲取動態選單資料并處理,

Step2:其次,處理動態選單資料,將其變化成 route 的形式,
  route 格式一般如下:
其中:
  path:指路由路徑(可以根據路徑定位路由),
  name:指路由名(可以根據路由名定位路由),
  component:指路由所在的組件,
  children:指的是路由組件中嵌套的子路由,
  meta:用于定義路由的一些元資訊,

route = {
    path: '',
    name: '',
    component: null,
    children: [],
    meta: {}
}

簡單舉例:
  如下選單資料轉換成 route:
    url 可以轉換為 path、name,用于定位路由,
    url 也用于定位 component 組件,
    subMenuList 可以轉換為 children,用于設定子路由(當然子路由也進行同樣處理),
    其余的屬性,可以放在 meta 中,

【動態選單資料:】
[{
        'menuId': 1,
        'parentId': 0,
        'name_zh': '系統管理',
        'name_en': 'System Control',
        'url': '',
        'type': 0,
        'icon': 'el-icon-setting',
        'orderNum': 0,
        'subMenuList': [{
                'menuId': 2,
                'parentId': 1,
                'name_zh': '管理員串列',
                'name_en': 'Administrator List',
                'url': 'sys/UserList',
                'type': 1,
                'icon': 'el-icon-user',
                'orderNum': 1,
                'subMenuList': []
            }
        ]
    }
]

【轉換后:】
[
    path: '',
    name: '',
    componment: null,
    children: [{
        path: 'sys-UserList',
        name: 'sys-UserList',
        componment: () => import(/sys/UserList.vue),
        meta: {
            'menuId': 2,
            'parentId': 1,
            'type': 1,
            'icon': 'el-icon-user',
            'orderNum': 1,
            'name_zh': '管理員串列',
            'name_en': 'Administrator List',
        }
    ]],
    meta: {
        'menuId': 1,
        'parentId': 0,
        'type': 0,
        'icon': 'el-icon-setting',
        'orderNum': 0,
        'name_zh': '系統管理',
        'name_en': 'System Control',
    }
]

 

Step3:使用 addRoute 方法,添加動態路由到主路由上,
  對于轉換好的路由資料(route),可以使用 addRoute 將其添加到主路由上,
注(有個小坑,簡單說明一下,后續會演示):
  此處若直接使用 addRoute 添加路由,通過 路由實體的 options 方法會無法看到新添加的路由資訊,也即無法通過 路由實體去獲取動態添加的路由資料,
  若想看到新添加的路由資訊,可以指定一個路由實體上的路由,并在其 children 中系結動態路由,然后通過 addRoute 添加該路由,資料才會被顯示,

 

(2)代碼實作 -- 獲取動態選單資料:
Step1:確定獲取動態選單資料的時機,
  時機:一般在登錄成功并進入主頁面時,獲取動態選單并顯示,
  由于涉及到路由跳轉(登錄頁面 -> 主頁面),所以可以使用 beforeEach 定義一個全域守衛,當從登錄頁面跳轉到主頁面時可以觸發獲取資料的操作,
  當然,有其他方式觸發獲取資料的操作亦可,

Step2:定義一個全域變數,用于控制是否獲取過動態選單資料,
  由于在 beforeEach 內部定義路由,每次路由跳轉均會觸發此操作,為了防止頻繁獲取動態選單,可以定義一個全域布爾變數來控制是否已經獲取過動態選單,
  可以在 router 實體中 添加一個變數用于控制(isAddDynamicMenuRoutes),
  通過 router.options.isAddDynamicMenuRoutes 可以獲取、修改該值,

// 創建一個 router 實體
const router = new VueRouter({
    // routes 用于定義 路由跳轉 規則
    routes,
    // mode 用于去除地址中的 #
    mode: 'history',
    // scrollBehavior 用于定義路由切換時,頁面滾動,
    scrollBehavior: () => ({
        y: 0
    }),
    // isAddDynamicMenuRoutes 表示是否已經添加過動態選單(防止頻繁請求動態選單)
    isAddDynamicMenuRoutes: false
})

 

Step3:獲取動態資料,
  由于之前對主頁面的路由跳轉,已經定義過一個 beforeEach,用于驗證 token 是否存在,
  從登錄頁面跳轉到主頁面會觸發該 beforeEach,且登錄后存在 token,所以可以直接復用,

import http from '@/http/http.js'

router.beforeEach((to, from, next) => {
    // 當開啟導航守衛時,驗證 token 是否存在,
    // to.meta.isRouter 表示是否開啟動態路由
    if (to.meta.isRouter) {
        // console.log(router)
        // 獲取 token 值
        let token = getToken()
        // token 不存在時,跳轉到 登錄頁面
        if (!token || !/\S/.test(token)) {
            next({
                name: 'Login'
            })
        }
        // token 存在時,判斷是否已經獲取過 動態選單,未獲取,即 false 時,需要獲取
        if (!router.options.isAddDynamicMenuRoutes) {
                http.menu.getMenus().then(response => {
                    console.log(response)
                })
        }
    }
}

 

當然,需要對代碼的邏輯進行一些修改(填坑記錄 =_= ),

填坑:
  頁面重繪時,動態路由跳轉不正確,
  之前為了驗證 token,增加了一個 isRouter 屬性值,用于給指定路由增加判斷 token 的邏輯,
  對于動態路由,頁面重繪后,路由會重新創建,即此時動態路由并不存在,也即 isRouter 不存在,從而 if 中的邏輯并不會觸發,動態路由并不會被創建,從而頁面跳轉失敗,
  所以在 if 判斷中,增加了一個條件 isDynamicRoutes,用于判斷是否為動態路由,因為動態路由只存在于主頁面中,所以其與 isRouter 的作用(為主頁面增加 token 驗證邏輯)不矛盾,

  isDynamicRoutes 可以寫在公用的 validate.js 中,使用時需要將其引入,
  由于此專案動態路由路徑中均包含 DynamicRoutes,所以以此進行正則運算式判斷,

/**
 * 判斷是否為 動態路由
 * @param {*} s
 */
export function isDynamicRoutes(s) {
    return /DynamicRoutes/.test(s)
}

 

 

import { isDynamicRoutes } from '@/utils/validate.js'

// 添加全域路由導航守衛
router.beforeEach((to, from, next) => {
    // 當開啟導航守衛時,驗證 token 是否存在,
    // to.meta.isRouter 表示是否開啟動態路由
    // isDynamicRoutes 判斷該路由是否為動態路由(頁面重繪時,動態路由沒有 isRouter 值,此時 to.meta.isRouter 條件不成立,即動態路由拼接邏輯不能執行)
    if (to.meta.isRouter || isDynamicRoutes(to.path)) {
        // 獲取 token 值
        let token = getToken()
        // token 不存在時,跳轉到 登錄頁面
        if (!token || !/\S/.test(token)) {
            next({
                name: 'Login'
            })
        }
        // token 存在時,判斷是否已經獲取過 動態選單,未獲取,即 false 時,需要獲取
        if (!router.options.isAddDynamicMenuRoutes) {
                http.menu.getMenus().then(response => {
                    console.log(response)
                })
        }
    }
}

 

(3)代碼實作 -- 處理動態選單資料
Step1:
  設定全域布爾變數為 true,上面已經定義了一個 isAddDynamicMenuRoutes 變數,當獲取動態選單成功后,將其值置為 true(表示獲取動態選單成功),用于防止頻繁獲取選單,
  然后,再去處理動態選單資料,此處將處理邏輯抽成一個方法 fnAddDynamicMenuRoutes,

import { isDynamicRoutes } from '

// 添加全域路由導航守衛
router.beforeEach((to, from, next) => {
    // 當開啟導航守衛時,驗證 token 是否存在,
    // to.meta.isRouter 表示是否開啟動態路由
    // isDynamicRoutes 判斷該路由是否為動態路由(頁面重繪時,動態路由沒有 isRouter 值,此時 to.meta.isRouter 條件不成立,即動態路由拼接邏輯不能執行)
    if (to.meta.isRouter || isDynamicRoutes(to.path)) {
        // 獲取 token 值
        let token = getToken()
        // token 不存在時,跳轉到 登錄頁面
        if (!token || !/\S/.test(token)) {
            next({
                name: 'Login'
            })
        }
        // token 存在時,判斷是否已經獲取過 動態選單,未獲取,即 false 時,需要獲取
        if (!router.options.isAddDynamicMenuRoutes) {
                http.menu.getMenus().then(response => {
                    // 資料回傳成功時
                if (response && response.data.code === 200) {
                    // 設定動態選單為 true,表示不用再次獲取
                    router.options.isAddDynamicMenuRoutes = true
                    // 獲取動態選單資料
                    let results = fnAddDynamicMenuRoutes(response.data.data)
                    console.log(results)
                }
                })
        }
    }
}

 

Step2:
  fnAddDynamicMenuRoutes 用于遞回選單資料,
  getRoute 用于回傳某個資料轉換的 路由格式,
下面注釋寫的應該算是很詳細了,主要講一下思路:
  對資料進行遍歷,
  定義兩個陣列(temp,route),temp 用于保存沒有子路由的路由,route 用于保存存在子路由的路由,
  如果某個資料存在 子路由,則對子路由進行遍歷,并將其回傳結果作為當前資料的 children,并使用 route 保存路由,
  如果某個資料不存在子路由,則直接使用 temp 保存路由,
  最后,回傳兩者拼接的結果,即為轉換后的資料,

// 用于處理動態選單資料,將其轉為 route 形式
function fnAddDynamicMenuRoutes(menuList = [], routes = []) {
    // 用于保存普通路由資料
    let temp = []
    // 用于保存存在子路由的路由資料
    let route = []
    // 遍歷資料
    for (let i = 0; i < menuList.length; i++) {
        // 存在子路由,則遞回遍歷,并回傳資料作為 children 保存
        if (menuList[i].subMenuList && menuList[i].subMenuList.length > 0) {
            // 獲取路由的基本格式
            route = getRoute(menuList[i])
            // 遞回處理子路由資料,并回傳,將其作為路由的 children 保存
            route.children = fnAddDynamicMenuRoutes(menuList[i].subMenuList)
            // 保存存在子路由的路由
            routes.push(route)
        } else {
            // 保存普通路由
            temp.push(getRoute(menuList[i]))
        }
    }
    // 回傳路由結果
    return routes.concat(temp)
}

// 回傳路由的基本格式
function getRoute(item) {
    // 路由基本格式
    let route = {
        // 路由的路徑
        path: '',
        // 路由名
        name: '',
        // 路由所在組件
        component: null,
        meta: {
            // 開啟路由守衛標志
            isRouter: true,
            // 開啟標簽頁顯示標志
            isTab: true,
            // iframe 標簽指向的地址(資料以 http 或者 https 開頭時,使用 iframe 標簽顯示)
            iframeUrl: '',
            // 開啟動態路由標志
            isDynamic: true,
            // 動態選單名稱(nameZH 顯示中文, nameEN 顯示英文)
            name_zh: item.name_zh,
            name_en: item.name_en,
            // 動態選單項的圖示
            icon: item.icon,
            // 選單項的 ID
            menuId: item.menuId,
            // 選單項的父選單 ID
            parentId: item.parentId,
            // 選單項排序依據
            orderNum: item.orderNum,
            // 選單項型別(0: 目錄,1: 選單項,2: 按鈕)
            type: item.type
        },
        // 路由的子路由
        children: []
    }
    // 如果存在 url,則根據 url 進行相關處理(判斷是 iframe 型別還是 普通型別)
    if (item.url && /\S/.test(item.url)) {
        // 若 url 有前綴 /,則將其去除,方便后續操作,
        item.url = item.url.replace(/^\//, '')
        // 定義基本路由規則,將 / 使用 - 代替
        route.path = item.url.replace('/', '-')
        route.name = item.url.replace('/', '-')
        // 如果是 外部 url,使用 iframe 標簽展示,不用指定 component,重新指定 path、name 以及 iframeUrl,
        if (isURL(item.url)) {
            route['path'] = `iframe-${item.menuId}`
            route['name'] = `iframe-${item.menuId}`
            route['meta']['iframeUrl'] = item.url
        } else {
            // 如果是專案模塊,使用 route-view 標簽展示,需要指定 component(加載指定目錄下的 vue 組件)
            route.component = () => import(`@/views/dynamic/${item.url}.vue`) || null
        }
    }
    // 回傳 route
    return route
}

可以查看當前輸出結果:

 

 

 

[
    {
        "path": "",
        "name": "",
        "component": null,
        "meta": {
            "isRouter": true,
            "isTab": true,
            "iframeUrl": "",
            "isDynamic": true,
            "name_zh": "系統管理",
            "name_en": "System Control",
            "icon": "el-icon-setting",
            "menuId": 1,
            "parentId": 0,
            "orderNum": 0,
            "type": 0
        },
        "children": [
            {
                "path": "sys-UserList",
                "name": "sys-UserList",
                "meta": {
                    "isRouter": true,
                    "isTab": true,
                    "iframeUrl": "",
                    "isDynamic": true,
                    "name_zh": "管理員串列",
                    "name_en": "Administrator List",
                    "icon": "el-icon-user",
                    "menuId": 2,
                    "parentId": 1,
                    "orderNum": 1,
                    "type": 1
                },
                "children": []
            },
            {
                "path": "sys-RoleControl",
                "name": "sys-RoleControl",
                "meta": {
                    "isRouter": true,
                    "isTab": true,
                    "iframeUrl": "",
                    "isDynamic": true,
                    "name_zh": "角色管理",
                    "name_en": "Role Control",
                    "icon": "el-icon-price-tag",
                    "menuId": 3,
                    "parentId": 1,
                    "orderNum": 2,
                    "type": 1
                },
                "children": []
            },
            {
                "path": "sys-MenuControl",
                "name": "sys-MenuControl",
                "meta": {
                    "isRouter": true,
                    "isTab": true,
                    "iframeUrl": "",
                    "isDynamic": true,
                    "name_zh": "選單管理",
                    "name_en": "Menu Control",
                    "icon": "el-icon-menu",
                    "menuId": 4,
                    "parentId": 1,
                    "orderNum": 3,
                    "type": 1
                },
                "children": []
            }
        ]
    },
    {
        "path": "",
        "name": "",
        "component": null,
        "meta": {
            "isRouter": true,
            "isTab": true,
            "iframeUrl": "",
            "isDynamic": true,
            "name_zh": "幫助",
            "name_en": "Help",
            "icon": "el-icon-info",
            "menuId": 5,
            "parentId": 0,
            "orderNum": 1,
            "type": 0
        },
        "children": [
            {
                "path": "iframe-6",
                "name": "iframe-6",
                "component": null,
                "meta": {
                    "isRouter": true,
                    "isTab": true,
                    "iframeUrl": "https://www.baidu.com/",
                    "isDynamic": true,
                    "name_zh": "百度",
                    "name_en": "Baidu",
                    "icon": "el-icon-menu",
                    "menuId": 6,
                    "parentId": 5,
                    "orderNum": 1,
                    "type": 1
                },
                "children": []
            },
            {
                "path": "iframe-7",
                "name": "iframe-7",
                "component": null,
                "meta": {
                    "isRouter": true,
                    "isTab": true,
                    "iframeUrl": "https://www.cnblogs.com/l-y-h/",
                    "isDynamic": true,
                    "name_zh": "博客",
                    "name_en": "Blog",
                    "icon": "el-icon-menu",
                    "menuId": 7,
                    "parentId": 5,
                    "orderNum": 1,
                    "type": 1
                },
                "children": []
            }
        ]
    }
]

 

5、使用 addRoute 添加路由:

  通過上面的步驟,已經獲取到動態選單資料,并將其轉為路由的格式,
  現在只需要使用 addRoute 將其添加到路由上,即可使用該動態路由,
有個小坑,下面有演示以及解決方法,

 

(1)添加三個基本組件頁面,用于測驗,
  基本組件頁面的位置,要與上面 component 指定的位置相一致,

【MenuControl.vue】
<template>
    <div>
        <h1>Menu Control</h1>
    </div>
</template>

<script>
</script>

<style>
</style>

【RoleControl.vue】
<template>
    <div>
        <h1>Role Control</h1>
    </div>
</template>

<script>
</script>

<style>
</style>

【UserList.vue】
<template>
    <div>
        <h1>User List</h1>
    </div>
</template>

<script>
</script>

<style>
</style>

 

 

 

(2)使用 addRoute 添加路由 -- 基本版
  根據專案需求,自行處理相關顯示邏輯(此處只是最簡單的版本),
  由于此處的選單顯示,只是二級選單,且選單內容,均顯示在 Home.vue 組件中,所以最終路由的格式應該如下所示,所有的路由均顯示在 children 中,

{
    path: '/DynamicRoutes',
    name: 'DynamicHome',
    component: () => import('@/views/Home.vue'),
    children: [{
     }]
}

 

想要實作這個效果,得對轉換后的動態資料進行進一步的處理,對于第一層選單資料,需要指定相應的組件,此處為 Home.vue,

// 獲取動態選單資料
let results = fnAddDynamicMenuRoutes(response.data.data)
// 如果動態選單資料存在,對其進行處理
if (results && results.length > 0) {
    // 遍歷第一層資料
    results.map(value =https://www.cnblogs.com/l-y-h/p/> {
        // 如果 path 值不存在,則對其賦值,并指定 component 為 Home.vue
        if (!value.path) {
            value.path = `/DynamicRoutes-${value.meta.menuId}`
            value.name = `DynamicHome-${value.meta.menuId}`
            value.component = () => import('@/views/Home.vue')
        }
    })
}
console.log(results)

 

 

 

可以看到路由資訊都已完善,此時,即可使用 addRoute 方法添加路由,

// 使用 router 實體的 addRoutes 方法,給當前 router 實體添加一個動態路由
router.addRoutes(results)
// 查看當前路由資訊
console.log(router.options)
// 測驗一下路由是否能正常跳轉,能跳轉,則顯示路由添加成功
setTimeout(()=> {
    router.push({name : "sys-UserList"})
}, 3000)

 

 

 

  如上圖所示,動態路由添加成功,且能夠成功跳轉,
  但是有一個問題,如何獲取到動態路由的值呢?

  一般通過 router 實體物件的 options 方法可以查看到當前路由的資訊,(使用 router.options 或者 this.$router.options 可以查看),

  但是從上圖可以看到,動態添加的資料并沒有在 options 中顯示出來,但路由確實添加成功了,沒有看源代碼,所以不太清除里面的實作邏輯(有人知道的話,還望不吝賜教),谷歌搜索了一下,沒有找到滿意的答案,基本都是說如何在 options 中顯示路由資訊(=_=),所以此處省略原理,以后有機會再補充,此處直接上解決辦法,

 

獲取動態路由值一般有兩種方式,
  第一種:通過 router 實體物件的 options 方式,
    動態路由使用靜態路由的 children 展開,即事先定義好靜態路由,添加動態路由時,將該動態路由添加到靜態路由 children 上,即可通過 options 顯示, 

  第二種:通過 vuex 方式,
    通過 vuex 保存當前動態路由的資訊,即單獨保存一份動態路由的資訊,使用時從 vuex 中獲取即可,當然,使用 localStorage、sessionStorage 保存亦可,

 

(3)獲取動態路由值 -- 方式一
  通過靜態路由的 children 的形式,添加動態路由資訊,
Step1:
  定義一個靜態路由,當然,router 實體物件中的 routes 也需要修改,

// 用于保存動態路由
const dynamicRoutes = [{
    path: '/DynamicRoutes',
    component: () => import('@/views/Home.vue'),
    children: []
}]

// 創建一個 router 實體
const router = new VueRouter({
    // routes 用于定義 路由跳轉 規則
    routes: dynamicRoutes.concat(routes),
    // routes,
    // mode 用于去除地址中的 #
    mode: 'history',
    // scrollBehavior 用于定義路由切換時,頁面滾動,
    scrollBehavior: () => ({
        y: 0
    }),
    // isAddDynamicMenuRoutes 表示是否已經添加過動態選單(防止頻繁請求動態選單)
    isAddDynamicMenuRoutes: false
})

 

Step2:
  修改 addRoutes 規則,
  首先需要將 動態路由添加到 靜態路由的 children 方法中,
  然后使用 addRoutes 添加靜態路由,

// 如果動態選單資料存在,對其進行處理
if (results && results.length > 0) {
    // 遍歷第一層資料
    results.map(value =https://www.cnblogs.com/l-y-h/p/> {
        // 如果 path 值不存在,則對其賦值,并指定 component 為 Home.vue
        if (!value.path) {
            value.path = `/DynamicRoutes-${value.meta.menuId}`
            value.name = `DynamicHome-${value.meta.menuId}`
            value.component = () => import('@/views/Home.vue')
        }
    })
}
// 將動態路由資訊添加到靜態路由的 children 中
dynamicRoutes[0].children = results
dynamicRoutes[0].name = 'DynamicRoutes'
// 使用 router 實體的 addRoutes 方法,給當前 router 實體添加一個動態路由
router.addRoutes(dynamicRoutes)
// 查看當前路由資訊
console.log(router.options)
// 測驗一下路由是否能正常跳轉,能跳轉,則顯示路由添加成功
router.push({name : "sys-UserList"})

 

 

 

  通過上圖,可以看到,使用 addRoutes 添加資料到靜態路由的 children 后,可以使用 router 實體物件的 options 查看到相應的動態資料,

  但是還是出現了一些小問題,由于靜態路由指定了 Home.vue 組件,所以其 children 不應該再出現 Home.vue 組件,否則出現了會出現上圖那樣的情況(Home.vue 組件中套 Home.vue 組件),

 

  感覺沒有什么好的解決辦法,只能粗暴的舍棄一些資料,
  比如舍棄第一層的資料,將其余資料拼接成一個陣列,然后添加到靜態路由的 children 上,

// 保存動態選單資料
let temp = []
// 如果動態選單資料存在,對其進行處理
if (results && results.length > 0) {
    // 舍棄第一層資料
    results.map(value =https://www.cnblogs.com/l-y-h/p/> {
        if (!value.path) {
            temp = temp.concat(value.children)
        }
    })
}
// 將動態路由資訊添加到靜態路由的 children 中
dynamicRoutes[0].children = temp
dynamicRoutes[0].name = 'DynamicRoutes'
// 使用 router 實體的 addRoutes 方法,給當前 router 實體添加一個動態路由
router.addRoutes(dynamicRoutes)
// 查看當前路由資訊
console.log(router.options)
// 測驗一下路由是否能正常跳轉,能跳轉,則顯示路由添加成功
router.push({name : "sys-UserList"})

 

 

 

當然,不舍棄第一層資料,將其全部拼接成一個陣列亦可,

// 保存動態選單資料
let temp = []
// 如果動態選單資料存在,對其進行處理
if (results && results.length > 0) {
    // 將全部資料拼接成一個陣列
    results.map(value =https://www.cnblogs.com/l-y-h/p/> {
        if (!value.path) {
            temp = temp.concat(value.children)
            value.children = []
            value.path = `/DynamicRoutes-${value.meta.menuId}`
            value.name = `DynamicHome-${value.meta.menuId}`
            temp = temp.concat(value)
        }
    })
}
// 將動態路由資訊添加到靜態路由的 children 中
dynamicRoutes[0].children = temp
dynamicRoutes[0].name = 'DynamicRoutes'
// 使用 router 實體的 addRoutes 方法,給當前 router 實體添加一個動態路由
router.addRoutes(dynamicRoutes)
// 查看當前路由資訊
console.log(router.options)
// 測驗一下路由是否能正常跳轉,能跳轉,則顯示路由添加成功
router.push({name : "sys-UserList"})

 

 

 

(4)獲取動態路由值 -- 方式二
  使用 vuex 保存動態路由的值,使用時從 vuex 中獲取,
  如下,在 common.js 中定義相關路由處理操作,

【進行相關處理:】
export default {
    state: {
        dynamicRoutes: []
    },
    // 更改 state(同步)
    mutations: {
        updateDynamicRoutes(state, routes) {
            state.dynamicRoutes = routes
        },
        resetState(state) {
            let stateTemp = {
                dynamicRoutes: []
            }
            Object.assign(state, stateTemp)
        }
    },
    // 異步觸發 mutations
    actions: {
        updateDynamicRoutes({commit, state}, routes) {
            commit("updateDynamicRoutes", routes)
        },
        resetState({commit, state}) {
            commit("resetState")
        }
    }
}


【完整 common.js】
export default {
    // 開啟命名空間(防止各模塊間命名沖突),訪問時需要使用 模塊名 + 方法名
    namespaced: true,
    // 管理資料(狀態)
    state: {
        // 用于保存語言設定(國際化),默認為中文
        language: 'zh',
        // 表示側邊欄選中的選單項的名
        menuActiveName: '',
        // 表示標簽頁資料,陣列
        mainTabs: [],
        // 表示標簽頁中選中的標簽名
        mainTabsActiveName: '',
        // 用于保存動態路由的資料
        dynamicRoutes: []
    },
    // 更改 state(同步)
    mutations: {
        updateLanguage(state, data) {
            state.language = data
        },
        updateMenuActiveName(state, name) {
            state.menuActiveName = name
        },
        updateMainTabs(state, tabs) {
            state.mainTabs = tabs
        },
        updateMainTabsActiveName(state, name) {
            state.mainTabsActiveName = name
        },
        updateDynamicRoutes(state, routes) {
            state.dynamicRoutes = routes
        },
        resetState(state) {
            let stateTemp = {
                language: 'zh',
                menuActiveName: '',
                mainTabs: [],
                mainTabsActiveName: '',
                dynamicRoutes: []
            }
            Object.assign(state, stateTemp)
        }
    },
    // 異步觸發 mutations
    actions: {
        updateLanguage({commit, state}, data) {
            commit("updateLanguage", data)
        },
        updateMenuActiveName({commit, state}, name) {
            commit("updateMenuActiveName", name)
        },
        updateMainTabs({commit, state}, tabs) {
            commit("updateMainTabs", tabs)
        },
        updateMainTabsActiveName({commit, state}, name) {
            commit("updateMainTabsActiveName", name)
        },
        updateDynamicRoutes({commit, state}, routes) {
            commit("updateDynamicRoutes", routes)
        },
        resetState({commit, state}) {
            commit("resetState")
        }
    }
}

 

 

 

如何在 router 中使用 vuex 呢?
  通過 router.app.$options.store 可以獲取到 vuex 內容,

【獲取 state 資料:】
    router.app.$options.store.state
    
【調用 vuex 方法:】
    router.app.$options.store.dispatch()
 
【使用:】 
let results = fnAddDynamicMenuRoutes(response.data.data)
// 如果動態選單資料存在,對其進行處理
if (results && results.length > 0) {
    // 遍歷第一層資料
    results.map(value =https://www.cnblogs.com/l-y-h/p/> {
        // 如果 path 值不存在,則對其賦值,并指定 component 為 Home.vue
        if (!value.path) {
            value.path = `/DynamicRoutes-${value.meta.menuId}`
            value.name = `DynamicHome-${value.meta.menuId}`
            value.component = () => import('@/views/Home.vue')
        }
    })
}
// 使用 vuex 管理動態路由資料
router.app.$options.store.dispatch('common/updateDynamicRoutes', results)
console.log(JSON.stringify(router.app.$options.store.state.common.dynamicRoutes))

// 使用 router 實體的 addRoutes 方法,給當前 router 實體添加一個動態路由
router.addRoutes(results)
// 查看當前路由資訊
console.log(router.options)
// 測驗一下路由是否能正常跳轉,能跳轉,則顯示路由添加成功
router.push({name : "sys-UserList"})

 

 

 

(5)使用 addRoute 添加路由 -- 最終版
  此專案中,使用 vuex 管理動態選單資料,而不在 options 中顯示(當然兩種方式結合一起亦可),
完整 router 的 index.js 邏輯如下,

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import {
    getToken
} from '@/http/auth.js'
import http from '@/http/http.js'
import {
    isURL,
    isDynamicRoutes
} from '@/utils/validate.js'

Vue.use(VueRouter)

// 定義路由跳轉規則
// component 采用 路由懶加載形式
// 此專案中,均采用 name 方式指定路由進行跳轉
/* 
    meta 用于定義路由元資訊,
其中 
    isRouter 用于指示是否開啟路由守衛(true 表示開啟),
    isTab 用于表示是否顯示為標簽頁(true 表示顯示)
    iframeUrl 用于表示 url,使用 http 或者 https 開頭的 url 使用 iframe 標簽展示
*/
const routes = [{
        path: '/',
        redirect: {
            name: "Login"
        }
    },
    {
        path: '/404',
        name: '404',
        component: () => import('@/components/common/404.vue')
    },
    {
        path: '/Login',
        name: 'Login',
        component: () => import('@/components/common/Login.vue')
    },
    {
        path: '/Home',
        name: 'Home',
        component: () => import('@/views/Home.vue'),
        redirect: {
            name: 'HomePage'
        },
        children: [{
                path: '/Home/Page',
                name: 'HomePage',
                component: () => import('@/views/menu/HomePage.vue'),
                meta: {
                    isRouter: true
                }
            },
            {
                path: '/Home/Demo/Echarts',
                name: 'Echarts',
                component: () => import('@/views/menu/Echarts.vue'),
                meta: {
                    isRouter: true,
                    isTab: true
                }
            },
            {
                path: '/Home/Demo/Ueditor',
                name: 'Ueditor',
                component: () => import('@/views/menu/Ueditor.vue'),
                meta: {
                    isRouter: true,
                    isTab: true
                }
            },
            {
                path: '/Home/Demo/Baidu',
                name: 'Baidu',
                meta: {
                    isRouter: true,
                    isTab: true,
                    iframeUrl: '@/test.html'
                }
            },
            // 路由匹配失敗時,跳轉到 404 頁面
            {
                path: "*",
                redirect: {
                    name: '404'
                }
            }
        ]
    }
]

// 創建一個 router 實體
const router = new VueRouter({
    // routes 用于定義 路由跳轉 規則
    routes,
    // mode 用于去除地址中的 #
    mode: 'history',
    // scrollBehavior 用于定義路由切換時,頁面滾動,
    scrollBehavior: () => ({
        y: 0
    }),
    // isAddDynamicMenuRoutes 表示是否已經添加過動態選單(防止頻繁請求動態選單)
    isAddDynamicMenuRoutes: false
})

// 添加全域路由導航守衛
router.beforeEach((to, from, next) => {
    // 當開啟導航守衛時,驗證 token 是否存在,
    // to.meta.isRouter 表示是否開啟動態路由
    // isDynamicRoutes 判斷該路由是否為動態路由(頁面重繪時,動態路由沒有 isRouter 值,此時 to.meta.isRouter 條件不成立,即動態路由拼接邏輯不能執行)
    if (to.meta.isRouter || isDynamicRoutes(to.path)) {
        // console.log(router)
        // 獲取 token 值
        let token = getToken()
        // token 不存在時,跳轉到 登錄頁面
        if (!token || !/\S/.test(token)) {
            next({
                name: 'Login'
            })
        }
        // token 存在時,判斷是否已經獲取過 動態選單,未獲取,即 false 時,需要獲取
        if (!router.options.isAddDynamicMenuRoutes) {
            http.menu.getMenus().then((response => {
                // 資料回傳成功時
                if (response && response.data.code === 200) {
                    // 設定動態選單為 true,表示不用再次獲取
                    router.options.isAddDynamicMenuRoutes = true
                    // 獲取動態選單資料
                    let results = fnAddDynamicMenuRoutes(response.data.data)
                    // 如果動態選單資料存在,對其進行處理
                    if (results && results.length > 0) {
                        // 遍歷第一層資料
                        results.map(value =https://www.cnblogs.com/l-y-h/p/> {
                            // 如果 path 值不存在,則對其賦值,并指定 component 為 Home.vue
                            if (!value.path) {
                                value.path = `/DynamicRoutes-${value.meta.menuId}`
                                value.name = `DynamicHome-${value.meta.menuId}`
                                value.component = () => import('@/views/Home.vue')
                            }
                        })
                    }
                    // 使用 vuex 管理動態路由資料
                    router.app.$options.store.dispatch('common/updateDynamicRoutes', results)
                    // 使用 router 實體的 addRoutes 方法,給當前 router 實體添加一個動態路由
                    router.addRoutes(results)
                }
            }))
        }
    }
    next()
})

// 用于處理動態選單資料,將其轉為 route 形式
function fnAddDynamicMenuRoutes(menuList = [], routes = []) {
    // 用于保存普通路由資料
    let temp = []
    // 用于保存存在子路由的路由資料
    let route = []
    // 遍歷資料
    for (let i = 0; i < menuList.length; i++) {
        // 存在子路由,則遞回遍歷,并回傳資料作為 children 保存
        if (menuList[i].subMenuList && menuList[i].subMenuList.length > 0) {
            // 獲取路由的基本格式
            route = getRoute(menuList[i])
            // 遞回處理子路由資料,并回傳,將其作為路由的 children 保存
            route.children = fnAddDynamicMenuRoutes(menuList[i].subMenuList)
            // 保存存在子路由的路由
            routes.push(route)
        } else {
            // 保存普通路由
            temp.push(getRoute(menuList[i]))
        }
    }
    // 回傳路由結果
    return routes.concat(temp)
}

// 回傳路由的基本格式
function getRoute(item) {
    // 路由基本格式
    let route = {
        // 路由的路徑
        path: '',
        // 路由名
        name: '',
        // 路由所在組件
        component: null,
        meta: {
            // 開啟路由守衛標志
            isRouter: true,
            // 開啟標簽頁顯示標志
            isTab: true,
            // iframe 標簽指向的地址(資料以 http 或者 https 開頭時,使用 iframe 標簽顯示)
            iframeUrl: '',
            // 開啟動態路由標志
            isDynamic: true,
            // 動態選單名稱(nameZH 顯示中文, nameEN 顯示英文)
            name_zh: item.name_zh,
            name_en: item.name_en,
            // 動態選單項的圖示
            icon: item.icon,
            // 選單項的 ID
            menuId: item.menuId,
            // 選單項的父選單 ID
            parentId: item.parentId,
            // 選單項排序依據
            orderNum: item.orderNum,
            // 選單項型別(0: 目錄,1: 選單項,2: 按鈕)
            type: item.type
        },
        // 路由的子路由
        children: []
    }
    // 如果存在 url,則根據 url 進行相關處理(判斷是 iframe 型別還是 普通型別)
    if (item.url && /\S/.test(item.url)) {
        // 若 url 有前綴 /,則將其去除,方便后續操作,
        item.url = item.url.replace(/^\//, '')
        // 定義基本路由規則,將 / 使用 - 代替
        route.path = item.url.replace('/', '-')
        route.name = item.url.replace('/', '-')
        // 如果是 外部 url,使用 iframe 標簽展示,不用指定 component,重新指定 path、name 以及 iframeUrl,
        if (isURL(item.url)) {
            route['path'] = `iframe-${item.menuId}`
            route['name'] = `iframe-${item.menuId}`
            route['meta']['iframeUrl'] = item.url
        } else {
            // 如果是專案模塊,使用 route-view 標簽展示,需要指定 component(加載指定目錄下的 vue 組件)
            route.component = () => import(`@/views/dynamic/${item.url}.vue`) || null
        }
    }
    // 回傳 route
    return route
}

// 解決相同路徑跳轉報錯
const routerPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location, onResolve, onReject) {
    if (onResolve || onReject)
        return routerPush.call(this, location, onResolve, onReject)
    return routerPush.call(this, location).catch(error => error)
}

export default router

 

二、動態選單的展示

  前面已經獲取到了動態選單資料,并使用 vuex 對其進行了管理,
  現在只需要將資料進行展示即可,

1、定義一個 DynamicMenu.vue 組件

  定義一個 DynamicMenu.vue 組件,用于展示動態選單,
  與之前寫 Aside.vue 代碼類似,將其 copy 過來直接修改即可,
其中:
  menu 表示選單項資料,是由父組件通過 props 傳遞進來的,
  index 顯示選單選中項,通過 menu.name 系結,
  icon 顯示選單項圖示,通過 menu.meta.icon 系結,
  選單資料國際化通過 menu.meta.name_zh、menu.meta.name_en 系結,
  subMenu 表示當前選單的子選單選項,需要系結路由跳轉事件,
  而路由跳轉事件的相關處理,在之前的一篇博客中已經做了處理,此處直接使用即可,
路由跳轉、iframe 嵌套參考:https://www.cnblogs.com/l-y-h/p/12973364.html

<template>
    <el-submenu :index="menu.name">
        <template slot="title">
            <i :></i>
            <span>{{$i18n.locale === 'zh' ? menu.meta.name_zh : menu.meta.name_en}}</span>
        </template>
        <el-menu-item v-for="subMenu in menu.children" :key="subMenu.meta.menuId" :index="subMenu.name" @click="$router.push({ name: subMenu.name })">
            <i :></i>
            <span slot="title">{{$i18n.locale === 'zh' ? subMenu.meta.name_zh : subMenu.meta.name_en}}</span>
        </el-menu-item>
    </el-submenu>
</template>

<script>
    export default {
        name: 'DynamicMenu',
        props: {
            menu: {
                type: Object,
                required: true
            }
        }
    }
</script>

<style>
</style>

 

 

2、在 Aside.vue 中引入 DynamicMenu.vue

  組件定義好后,就需要引入了,
(1)引入動態選單組件:
Step1:import 引入相關組件,
Step2:components 宣告相關組件,
Step3:使用組件,

<template>
    <DynamicMenu v-for="menu in dynamicRoutes" :key="menu.meta.menuId" :menu="menu"></DynamicMenu>
</template>        
<script>
    import DynamicMenu from '@/views/dynamic/DynamicMenu.vue'
    export default {
        name: 'Aside',
        components: {
            DynamicMenu
        }
    }
</script>

 

 

(2)引入 vuex 管理的動態選單資料
  由于之前已經引入過 vuex,此處直接引入 動態選單資料 dynamicRoutes 即可,

computed: {
    ...mapState('common', ['menuActiveName', 'mainTabs', 'dynamicRoutes'])
}

 

 

3、完整效果

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/73770.html

標籤:JavaScript

上一篇:斐波那契列數JS的三種實作

下一篇:JavaScript連載15-return、函式宣告方式差異化、回呼函式

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more