日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

本地数据库IndexedDB - 学员管理系统之登录(一)

發布時間:2024/1/18 windows 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 本地数据库IndexedDB - 学员管理系统之登录(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

????????IndexedDB是瀏覽器提供的本地數據庫,它可以被網頁腳本創建和操作。IndexedDB允許存儲大量數據,提供查找接口,還能建立索引。這些都是LocalStorage或Cookie不具備的。就數據庫類型而言,IndexedDB不屬于關系型數據庫(不支持SQL查詢語句),更接近NoSQL數據庫。

IndexedDB具有以下特點:

  • 鍵值對存儲:IndexedDB內部采用對象倉庫(object store)存放數據。所有類型的數據都可以直接存入,包括JS對象。對象倉庫中,數據以”鍵值對“的形式保存,每一個數據記錄都對應的主鍵,主鍵是獨一無二的,不能有重復,否則會拋出錯誤。
  • 異步:IndexedDB操作時不會鎖死瀏覽器,用戶依然可以進行其他操作,這與LocalStorage形成對比,后者的操作是同步的。異步設計是為了防止大量數據的讀寫,拖慢網頁的表現。
  • 支持事務:IndexedDB支持事務(transaction),這意味著一系列操作步驟之中,只要有一步失敗,整個事務就都取消,數據庫回滾到事務發生之前的狀態,不存在只改寫一部分數據的情況。
  • 同源限制:IndexedDB受到同源限制,每一個數據庫對應創建它的域名。網頁只能訪問自身域名下的數據庫,而不能訪問跨域的數據庫。
  • 存儲空間大:IndexedDB的存儲空間比LocalStore大很多,一般來說不少于250MB,甚至沒有上限。
  • 支持二進制存儲:IndexedDB不僅可以存儲字符串,還可以存儲二進制數據(ArrayBuffer對象和Blob對象)。
  • ????????

    ? ? ? ? 通過上篇講解,大家已經熟悉了IndexedDB的一些基本用法。這里將使用IndexedDB寫個簡單的”學員管理系統(本地版)“,如對IndexedDB基礎還不了解的,請翻看上一篇,地址如下:

    本地數據庫IndexedDB - 初學者(一)_覺醒法師的博客-CSDN博客

    ? ? ? ? 使用的技術框架是Vue.js +?element-ui + vuex + axios + sass + indexedDB,系統欄目分類有:管理員列表、年級管理、班級管理、教師列表和學員列表。

    ? ? ? ? 如下圖所示:

    一、搭建項目

    ? ? ? ? Vue項目的搭建這里也不再闡述,如有對vue全家桶不了解的同學,可以先閱讀以下幾篇文章:

    Vue.js快速入門之一:安裝和配置_覺醒法師的博客-CSDN博客_vue.js配置

    Vue.js快速入門之二:使用狀態管理工具Vuex_覺醒法師的博客-CSDN博客????????

    Vue.js快速入門之三:Vue-Router路由_覺醒法師的博客-CSDN博客

    Vue.js快速入門之四:axios安裝和使用_覺醒法師的博客-CSDN博客_axios怎么安裝

    二、數據庫創建

    ? ? ? ? Vue項目搭建好后,需要在src目錄下創建db目錄,用來存儲操作IndexedDB數據庫文件。比如數據庫操作文件、對應表封裝類等。
    ? ? ? ?

    2.1 定義表

    ? ? ? ? 雖然IndexedDB不屬性于“關系性數據庫”,這里還是按“關系性數據庫”進行表的定義和關聯。

    系統管理員表:

    字段名類型描述
    idintID
    namevarchar管理員賬號
    passwordvarchar密碼
    phonevarchar手機號
    createtimedatetime創建日期
    updatetimedatetime更新日期

    年級表:

    字段名類型描述
    idintID
    namevarchar年級名稱
    createtimedatetime創建日期
    updatetimedatetime更新日期

    班級表:

    字段名類型描述
    idintID
    namevarchar班級名稱
    midvarchar班主任
    gidint關聯年級
    tsArray關聯授權教師
    createtimedatetime創建日期
    updatetimedatetime更新日期

    教師表:

    字段名類型描述
    idintID
    namevarchar教師姓名
    registrationvarchar戶籍
    addressvarchar現居住地
    phonevarchar手機號
    cidschar關聯班級ID,通過逗號分隔
    birthdaydatetime出生年月日
    createtimedatetime創建日期
    updatetimedatetime更新日期

    學員表:

    字段名類型描述
    idintID
    namevarchar學員姓名
    registrationvarchar戶籍
    addressvarchar現居住地
    phonevarchar手機號
    cidint關聯班級ID
    birthdaydatetime出生年月日
    createtimedatetime創建日期
    updatetimedatetime更新日期

    2.2 MD5加密

    ? ? ? ? 由于IndexedDB是瀏覽器中數據庫,當項目運行后可直接查看數據庫中的數據,這時相關私密性的內容就需要通過加密進行處理了。所以在創建數據庫表前,我們先在src目錄下創建utils工具類目錄,添加md5.js文件,用來給登錄密碼進行加密處理。

    md5.js代碼如下:

    /** Configurable variables. You may need to tweak these to be compatible with* the server-side, but the defaults work in most cases.*/ var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode *//** These are the functions you'll usually want to call* They take string arguments and return either hex or base-64 encoded strings*/ function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));} function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));} function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));} function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); } function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); } function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }/** Perform a simple self-test to see if the VM is working*/ function md5_vm_test() {return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72"; }/** Calculate the MD5 of an array of little-endian words, and a bit length*/ function core_md5(x, len) {/* append padding */x[len >> 5] |= 0x80 << ((len) % 32);x[(((len + 64) >>> 9) << 4) + 14] = len;var a = 1732584193;var b = -271733879;var c = -1732584194;var d = 271733878;for(var i = 0; i < x.length; i += 16){var olda = a;var oldb = b;var oldc = c;var oldd = d;a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);c = md5_ff(c, d, a, b, x[i+10], 17, -42063);b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);a = safe_add(a, olda);b = safe_add(b, oldb);c = safe_add(c, oldc);d = safe_add(d, oldd);}return Array(a, b, c, d);}/** These functions implement the four basic operations the algorithm uses.*/ function md5_cmn(q, a, b, x, s, t) {return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); } function md5_ff(a, b, c, d, x, s, t) {return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); } function md5_gg(a, b, c, d, x, s, t) {return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); } function md5_hh(a, b, c, d, x, s, t) {return md5_cmn(b ^ c ^ d, a, b, x, s, t); } function md5_ii(a, b, c, d, x, s, t) {return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); }/** Calculate the HMAC-MD5, of a key and some data*/ function core_hmac_md5(key, data) {var bkey = str2binl(key);if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);var ipad = Array(16), opad = Array(16);for(var i = 0; i < 16; i++){ipad[i] = bkey[i] ^ 0x36363636;opad[i] = bkey[i] ^ 0x5C5C5C5C;}var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);return core_md5(opad.concat(hash), 512 + 128); }/** Add integers, wrapping at 2^32. This uses 16-bit operations internally* to work around bugs in some JS interpreters.*/ function safe_add(x, y) {var lsw = (x & 0xFFFF) + (y & 0xFFFF);var msw = (x >> 16) + (y >> 16) + (lsw >> 16);return (msw << 16) | (lsw & 0xFFFF); }/** Bitwise rotate a 32-bit number to the left.*/ function bit_rol(num, cnt) {return (num << cnt) | (num >>> (32 - cnt)); }/** Convert a string to an array of little-endian words* If chrsz is ASCII, characters >255 have their hi-byte silently ignored.*/ function str2binl(str) {var bin = Array();var mask = (1 << chrsz) - 1;for(var i = 0; i < str.length * chrsz; i += chrsz)bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);return bin; }/** Convert an array of little-endian words to a string*/ function binl2str(bin) {var str = "";var mask = (1 << chrsz) - 1;for(var i = 0; i < bin.length * 32; i += chrsz)str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);return str; }/** Convert an array of little-endian words to a hex string.*/ function binl2hex(binarray) {var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";var str = "";for(var i = 0; i < binarray.length * 4; i++){str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);}return str; }/** Convert an array of little-endian words to a base-64 string*/ function binl2b64(binarray) {var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var str = "";for(var i = 0; i < binarray.length * 4; i += 3){var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)| (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )| ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);for(var j = 0; j < 4; j++){if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);}}return str; }export {hex_md5,b64_md5,str_md5 }

    ? ? ? ? 這里除了直接引用MD5文件外,也可以通過npm或yarn等一些包管理工具,使用命令進行安裝,這部分自行選擇適合自己項目的方法即可。

    2.2 打開數據庫

    ? ? ? ? 以上將所有表結構定義好后,咱們可以封裝操作數據庫文件了。在src目錄下創建db目錄,然后新建index.js文件,用來定義數據庫打開及表結構創建等等。

    ? ? ? ? 在index.js文件中,我們封裝一個DBIsLoadSuccess函數,用來判斷IndexedDB是否打開成功,因為open請求不會立即打開數據庫或者開始一個事務,而是在onsuccess執行時;并且結合Promise,來實現此功能。具體代碼如下:

    let Database; let Request = window.indexedDB.open("TestDatabase", 2); let isError = false;// 數據庫打開失敗 Request.onerror = function(event) {isError = true;throw "Why didn't you allow my web app to use IndexedDB?!"; };// 數據庫打開成功 Request.onsuccess = function(event) {isError = false;Database = event.target.result; };let handle = null;/*** 數據庫增刪改查 封裝*/ export const DBIsLoadSuccess = () => {return new Promise((resolve, reject) => {//如果數據庫打開成功,直接回調resolve函數,并返回實例對象if(Database){resolve(Database);}//如果數據庫還未打開,添加定時器 進行查詢,打開成功后,else{handle = setInterval(() => {if(Database){clearInterval(handle);resolve(Database);}//如果數據庫打開失敗,執行reject函數else if(isError){clearInterval(handle);reject();}}, 200);}}); }

    2.3 表結構創建

    ????????在onupgradeneeded事件中,允許你在處理函數中更新數據庫模式;所以我們在onupgradeneeded執行時,來創建對應的表及表結構。

    ? ? ? ? 在管理員表創建時,需要默認添加一個管理員賬號,并且通過hex_md5對密碼進行處理后再存儲到用戶表中。創建管理員賬號時,無須添加accesstoken鍵值對,因為此時用戶還未登錄,無token信息;如默認添加空值,則索引accesstoken中則會有相應關聯值;這里accesstoken索引是用來判斷用戶是否登錄的,是唯一的,只有在登錄狀態下才會賦值。

    ????????在index.js文件中增加以下代碼:

    import { hex_md5 } from '@/utils/md5'//數據庫表名 let usersName = "users"; //用戶表 let gradeName = "grade"; //年級表 let classifyName = "classify"; //班級表 let studentName = "student"; //學生表 let teacherName = "teacher"; //老師表 let buildingName = "building"; //樓棟// 執行success前執行函數 Request.onupgradeneeded = function(e){let db = e.target.result;//用戶表if(!db.objectStoreNames.contains(usersName)){let store = db.createObjectStore(usersName, {keyPath: "id", autoIncrement: true});//創建索引 - 用戶名store.createIndex('name', 'name', {unique: true});//創建索引 - 登錄tokenstore.createIndex('accesstoken', 'accesstoken', {unique: true});//添加默認賬號store.add({name: "admin",password: hex_md5('123456'),phone: "13233332222",cratetime: new Date().getTime(),updatetime: new Date().getTime()});}//年級表if(!db.objectStoreNames.contains(gradeName)){let store = db.createObjectStore(gradeName, {keyPath: "id", autoIncrement: true});//創建索引 - 年級名稱(年級為唯一,故不能重復)store.createIndex('name', 'name', {unique: true});}//班級表if(!db.objectStoreNames.contains(classifyName)){let store = db.createObjectStore(classifyName, {keyPath: "id", autoIncrement: true});//創建索引 - 班級名稱store.createIndex('name', 'name', {unique: false});//創建索引 - 年級名稱store.createIndex('grade', 'grade', {unique: true});}//學生表if(!db.objectStoreNames.contains(studentName)){let store = db.createObjectStore(studentName, {keyPath: "id", autoIncrement: true});//創建索引 - 學生姓名store.createIndex('name', 'name', {unique: false});}//老師表if(!db.objectStoreNames.contains(teacherName)){let store = db.createObjectStore(teacherName, {keyPath: "id", autoIncrement: true});//創建索引 - 老師姓名store.createIndex('name', 'name', {unique: false});}//樓棟表if(!db.objectStoreNames.contains(buildingName)){let store = db.createObjectStore(buildingName, {keyPath: "id", autoIncrement: true});//創建索引 - 樓棟名稱store.createIndex('name', 'name', {unique: true, multiEntry: true});}//if end }

    注意:onupgradeneeded只會在第一次打開數據庫時觸發,后期想要觸onupgradeneeded監聽事件,則需要修改open中第二位參數的版本號。

    ? ? ? ? 我們在項目中引用index.js文件并執行,此時我們F12顯示控制臺,切換到“Application”,在IndexedDB中,則可以看到TestDatabase數據庫了,以及創建相應的表及索引。

    ? ? ? ? 如下圖:

    三、項目結構?

    ? ? ? ? 在項目中,如下圖在src目錄下,創建相應的項目文件。

    ? ? ? ? ?在router以上部分,會在后面會詳細講解,這里我們先講下如果定義路由和狀態管理倉庫。

    3.1 vue-ls

    ????????Vue-ls用來控制數據存儲在localStorage或者sessionStorage中,并且可以控制其存儲時效性。

    安裝:

    npm install vue-ls --save

    main.js中引入:

    import Storage from 'vue-ls'Vue.use(Storage, {namespace: 'system_',name: 'ls',storeage: 'local' });

    基本用法如下:

    //存儲數據 Vue.ls.set('foo', 'boo', 60 * 60 * 1000); //緩存1小時 Vue.ls.get('foo'); Vue.ls.get('boo', 10); //如果沒有獲取到boo數據,默認返回10Vue.ls.on('foo', callback) //監聽foo值變化,觸發Callback回調函數 Vue.ls.off('foo', callback) //卸載foo監聽事件//移除foo對應緩存數據 Vue.ls.remove('foo');

    3.2?store狀態管理

    ? ? ? ??Vuex是一個專為Vue.js應用程序開發的狀態管理模式 + 庫。它采用集中式存儲管理應用的所有組件狀態,并在相應的規則保證狀態以一種可預測的方式發生變化。

    ????????什么情況下會使用到Vuex:比如登錄的用戶信息,接口的統一訪問令牌等,會頻繁使用和全站通用的數據,可以寄存在狀態管理器中。

    ? ? ? ? 現在在store目錄創建相應文件:

    3.2.1 創建state.js文件

    代碼如下:

    /*** 狀態,變量庫*/ const state = {/*** 訪問令牌*/token: "",/*** 用戶信息*/userInfo: null }export default state;

    3.2.2 創建mutationsType.js文件

    代碼如下:

    /*** 用戶信息*/ export const USERINFO = "USERINFO";/*** 訪問令牌*/ export const TOKEN = "TOKEN";

    3.2.3 創建mutations.js文件

    代碼如下:

    import { USERINFO, TOKEN } from './mutationsType'const mutations = {/*** 修改訪問令牌信息*/[TOKEN](state, param){state.token = param;},/*** 修改用戶信息*/[USERINFO](state, param){state.userInfo = param;} } export default mutations;

    3.2.4 創建getters.js文件

    代碼如下:

    const getters = {/*** 用戶信息*/userInfo(state){return state.userInfo;},/*** 訪問令牌*/accessToken(state){return state.token;} } export default getters;

    3.2.5 創建actions.js文件

    代碼如下:

    import Vue from 'vue' import { USERINFO, TOKEN } from './mutationsType'/*** 業務層*/ const actions = {/*** 檢查是否登錄*/checkIsLogin(){let token = Vue.ls.get(TOKEN);return new Promise((resolve, reject) => {if(token){resolve();}else{reject();}});},/*** 保存登錄信息*/saveLoginInfo({commit}, param){if(param['token']) {commit(TOKEN, param.token);Vue.ls.set(TOKEN, param.token);}if(param['userinfo']) {commit(USERINFO, param.userinfo);Vue.ls.set(USERINFO, param.userinfo);}},/*** 退出登錄*/exitLogin({commit}, param){commit(TOKEN, '');commit(USERINFO, '');Vue.ls.remove(TOKEN);Vue.ls.remove(USERINFO);} }export default actions;

    3.2.6 創建index.js文件

    代碼如下:

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

    3.2.7 main.js中引入?

    代碼如下:

    import Vue from 'vue' import App from './App' import elementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' import 'element-ui/lib/theme-chalk/base.css' import Storage from 'vue-ls' import store from '@/store/index'Vue.use(elementUI);Vue.use(Storage, {namespace: 'system_',name: 'ls',storeage: 'local' });Vue.config.productionTip = false/* eslint-disable no-new */ new Vue({el: '#app',store,components: { App },template: '<App/>' })

    ? ? ? ? 這里store本地狀態管理器中文件就創建和定義完了,后續可直接使用this.$store進行調用即可。

    3.3?router路由定義

    ? ? ? ? 在定義路由前,先在pages目錄下,對應創建所有頁面文件,如管理員列表頁、年級列表頁、班級列表頁、教師列表頁、學員列表頁,以及錯誤頁和登錄頁。

    ????????另外,我們需要在components組件中,新建Layout.vue文件,用來定義菜單和內容區域,作為公共部分。代碼如下:

    html代碼部分:

    <template><div class="layout-wrap"><el-container class="container"><el-aside width="200px" class="aside-box"><div class="title"><h3>學員管理系統</h3></div><el-menu default-active="1" class="el-menu-vertical" :router="true"><el-menu-item index="1" :route="{path: '/sys/mange'}"><i class="el-icon-s-custom"></i><span slot="title">管理員列表</span></el-menu-item><el-menu-item index="2" :route="{path: '/sys/grade'}"><i class="el-icon-house"></i><span slot="title">年級管理</span></el-menu-item><el-menu-item index="3" :route="{path: '/sys/classify'}"><i class="el-icon-collection-tag"></i><span slot="title">班級管理</span></el-menu-item><el-menu-item index="4" :route="{path: '/sys/teacher'}"><i class="el-icon-user"></i><span slot="title">教師列表</span></el-menu-item><el-menu-item index="5" :route="{path: '/sys/student'}"><i class="el-icon-user"></i><span slot="title">學員列表</span></el-menu-item></el-menu><div class="btn-box"><el-button type="danger" size="mini" @click="logoutEvent">退出登錄</el-button></div></el-aside><el-main><transition name="el-zoom-in-center"><router-view /></transition></el-main></el-container></div> </template>

    JS部分:

    <script> export default {name: 'Layout',data () {return { }},methods: {/*** 退出登錄*/logoutEvent(){}} } </script>

    樣式部分:

    <style lang="scss" scoped>.container{ height: 100vh; }.el-menu{ border-right: 0; }.el-menu-vertical{ height: 100%; }.aside-box{position: relative;padding-top: 80px;border-right: 1px solid #e6e6e6;.title{width: 100%;padding: 30px 0;text-align: center;position: absolute;left: 0;top: 0;z-index: 10;h3{font-size: 20px;color: #409EFF;}}} </style>

    ????????待這些頁頁創建成功后,我們將在router/index.js中引入這些模塊,用來定義頁面跳轉路由路徑,代碼如下:

    import Vue from 'vue' import Router from 'vue-router' import Layout from '@/components/Layout' import Error404 from '@/pages/Error/err404' import Index from '@/pages/index' import Student from '@/pages/student' import Mange from '@/pages/mange' import Grade from '@/pages/grade' import Classify from '@/pages/classify' import Teacher from '@/pages/teacher' import Login from '@/pages/login' import store from '@/store'Vue.use(Router);let _router = new Router({routes: [{path: '/',name: "Home",component: Layout,redirect: '/sys/index',children: [{path: '/sys/index',name: 'Index',component: Index,},{path: '/sys/student',name: 'Student',component: Student,},{path: '/sys/mange',name: 'Mange',component: Mange,},{path: '/sys/grade',name: 'Grade',component: Grade,},{path: '/sys/classify',name: 'Classify',component: Classify,},{path: '/sys/teacher',name: 'Teacher',component: Teacher,}]},{path: '/login',name: 'Login',component: Login,},{path: '*',name: 'Error404',component: Error404,},] });_router.beforeEach((toRoute, fromRoute, next) => {next(); });export default _router;

    ? ? ? ? 此時,我們可以點擊頁面左側菜單,進行頁面跳轉了。關于登錄功能,和登錄頁面跳轉,將會在后面繼續講解。

    四、登錄功能

    4.1 權限校驗

    ? ? ? ? 在前面我們定義好了路由相關跳轉路徑,那我們如何跳轉到登錄頁呢。此時大家可以往前翻看“3.2.5 創建actions.js文件”中,定義了checkIsLogin函數,用來判斷用戶是否登錄了。在里我們則可以對router/index.js中的 路由衛士進行 稍微調整即可,代碼如下:

    _router.beforeEach((toRoute, fromRoute, next) => {store.dispatch('checkIsLogin').then(() => {next();}).catch(() => {if(toRoute.path=='/login'){next();}else{next('/login');}}); });

    ? ? ? ? 通過執行checkIsLogin函數,來判斷用戶是否已登錄,如登錄則直接跳轉到下一路由頁面中,否則跳轉到登錄頁,完成登錄后,則可以正常訪問系統頁面。為什么要在此判斷呢,因為所有頁面的訪問都要經過“路由衛士”,系統登錄是有時效性的,一旦超時token則會自動失效;所以當token失敗時,用戶點擊下一步操作,則會自動跳轉到登錄頁面。

    ? ? ? ? 另外,由于這里引用了Promise,所以會出現以下錯誤:

    vue-router Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current? ? ? ??

    ? ? ? ? 這時按以下方法修改即可:

    //存儲push let originPush=Router.prototype.push let originReplace=Router.prototype.replace//重寫 Router.prototype.push=function(location,resole,reject){if(resole&&reject){originPush.call(this,location,resole,reject)}else{originPush.call(this,location,()=>{},()=>{})} } Router.prototype.replace=function(location,resole,reject){if(resole&&reject){originReplace.call(this,location,resole,reject)}else{originReplace.call(this,location,()=>{},()=>{})} }_router.beforeEach((toRoute, fromRoute, next) => {store.dispatch('checkIsLogin').then(() => {next();}).catch(() => {if(toRoute.path=='/login'){next();}else{next('/login');}}); });

    ? ??

    4.2 登錄頁面

    ? ? ? ? 此時開始著手完成登錄頁部分的代碼,這里直接貼代碼了。

    html部分:

    <template><div class="login-box"><h3>學員管理系統</h3><h4><span>———</span> <span class="tit">安全登錄</span> <span>———</span></h4><el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm"><el-form-item label="用戶名" prop="username"><el-input type="text" v-model="ruleForm.username" autocomplete="off"></el-input></el-form-item><el-form-item label="密碼" prop="password"><el-input type="password" v-model="ruleForm.password" autocomplete="off"></el-input></el-form-item><el-form-item><el-button type="primary" :disabled="disabledButton" @click="submitForm('ruleForm')">登錄</el-button><el-button :disabled="disabledButton" @click="resetForm('ruleForm')">重置</el-button></el-form-item></el-form></div> </template>

    js部分:

    <script>export default {data(){var validateUsername = (rule, value, callback) => {if (value === '') {callback(new Error('請輸入用戶名'));} else {callback();}};var validatePass = (rule, value, callback) => {if (value === '') {callback(new Error('請輸入密碼'));} else {if (this.ruleForm.checkPass !== '') {this.$refs.ruleForm.validateField('checkPass');}callback();}};return {disabledButton: false, //是否禁用按鈕ruleForm: {username: '',password: '',},rules: {username: [{ validator: validateUsername, trigger: 'blur' }],password: [{ validator: validatePass, trigger: 'blur' }]},}},methods: {/*** 提交數據* @param {Object} formName*/submitForm(formName) {this.$refs[formName].validate((valid) => {if (valid) {console.log(this.ruleForm);} else {console.log('error submit!!');return false;}});},/*** 重置表單* @param {Object} formName*/resetForm(formName) {this.$refs[formName].resetFields();}}} </script>

    樣式部分:

    .login-box{width: 600px;height: 390px;padding: 50px 70px;box-sizing: border-box;box-shadow: 0 0 10px rgba(0, 0, 0, .1);border-radius: 10px;overflow: hidden;position: absolute;left: 50%;top: 50%;margin-left: -300px;margin-top: -200px;z-index: 10;h3, h4{font-family: "Microsoft YaHei","微軟雅黑",Arial,sans-serif;text-align: center;}h3{font-size: 26px;color: #409eff;}h4{font-size: 14px;color: #999999;font-weight: normal;padding: 10px 0 40px;span{display: inline-block;vertical-align: middle;&.tit{padding: 0 26px;}}} }

    登錄頁界面如下圖:

    4.3 添加打開事務功能

    ? ? ? ? 在2.2中我們完成了“數據庫打開”操作文件的代碼,此時需要在內部添加兩個打開事務的執行函數,以及一個讀寫的常量值(IndexedDB中有對應常量值,但已廢棄,這里自己定義即可),在db/index.js文件中增加代碼如下:

    /*** 返回是只讀或讀寫模式*/ export const CONST_READ = {READONLY: "readonly",READWRITE: "readwrite" }/*** 打開索引用戶名游標*/ export const openTransactionIndex = (storeName, indexName, mode) => {if(!storeName){throw '請指定打開事務的表名!';}if(!indexName){throw '請指定需查詢索引名!';}mode = mode || CONST_READ.READONLY;//開啟事務let transaction = Database.transaction([storeName], mode);//連接對象倉庫let store = transaction.objectStore(storeName);//獲取對應索引let index = store.index(indexName);//返回游標let cursor = index.openCursor();return {store,index,cursor} }

    4.4 api請求定義

    ? ? ? ? 在src目錄下新建api/index.js文件,用來定義接口請求功能函數。我們先在db/model目錄下創建user.js,用來操作管理員表的增刪改查;創建好后,先定義好login和ogout函數,用來處理登錄和退出功能,代碼如下:

    /*** 通過索引獲取對應數據*/ export const login = data => {}/*** 退出登錄*/ export const logout = token => {}

    ? ? ? ? 在db/model/user.js創建好后,我們可以在api/index.js中定義登錄和退出接口請求了,代碼如下:

    import { login, logout } from '@/db/model/user'/*** 登錄*/ export const loginInfo = (params) => {return login(params); }/*** 退出*/ export const logoutInfo = params => {return logout(params); }

    4.5 登錄和退出業務功能

    ? ? ? ? 在4.3中,我們在db/index.js中添加了添加了處理索引事務函數,我們將其引入到db/model/user.js表中,用來實現登錄用戶查詢和accesstoken信息查詢。

    ? ? ? ? 另外,用戶密碼保存時是通過md5加密處理的,所以登錄時匹配用戶密碼時,也需要使用到md5處理函數,這里也需要引入。

    ? ? ? ? 用戶登錄成功時候,需要生成accesstoken隨機字符串,這里咱們把這類功能函數放在utils/utils.js工具類中;還有在所有數據請求成功后,需要返回一個統一的數據格式JSON文件,我們也在工具類文件中定義個rJson函數,工具類文件代碼如下:

    /*** 隨機生成字符串* @param {*} _len*/ export const randomStrName = _len => {let _string = 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ1234567890',_returnStr = '';_len = 'number'===typeof _len ? _len : 50;for(var i = 0; i < _len; i++){_returnStr += _string.charAt( Math.floor(Math.random() * (_string.length-2)) )}return _returnStr; }/*** 返回數據對象* @param code 狀態碼, 0 表示錯誤, 1表示正常* @param data 返回數據集* @param msg 返回提示信息*/ export const rJson = (code, data, msg) => {code = code || '0';data = data || null;msg = msg || '';return {code,data,msg} }

    ? ? ? ? 以上準備工具做好后,我們還要在user.js文件中定義storeName,指定當前文件操作的表。login和logout功能函數實現上,使用Promise來異步操作,這樣使回調數據更為靈活點。

    ? ? ? ? 代碼如下:

    import { openTransactionIndex, CONST_READ } from '@/db' import { rJson, randomStrName } from '@/utils/utils' import { hex_md5 } from '@/utils/md5'let storeName = 'users';/*** 通過索引獲取對應數據*/ export const login = data => {return new Promise((resolve, reject) => {}); }/*** 退出登錄*/ export const logout = token => {return new Promise((resolve, reject) => {}); }

    4.6 實現登錄功能

    ? ? ? ? 現在各項關系的文件都創建好了,并且已關聯上,咱們可以找到“4.2 登錄頁面”的js部分,引入api/index.js中的loginInfo,來實現登錄數據上傳并校驗。另外,之前在“3.2.5 創建actions.js文件”中,定義了登錄保存用戶信息業務函數saveLoginInfo,在登錄成功后,需調用此函數將用戶信息和accesstoken保存為全局變量中,以及緩存本地存儲中。打開pages/login/index.vue文件,代碼如下:

    <script>import { loginInfo } from '@/api'export default {data(){// ...return {disabledButton: false, //是否禁用按鈕ruleForm: {username: '',password: '',},rules: {username: [{ validator: validateUsername, trigger: 'blur' }],password: [{ validator: validatePass, trigger: 'blur' }]},}},methods: {/*** 提交數據*/submitForm(formName) {this.$refs[formName].validate((valid) => {if (valid) {loginInfo(this.ruleForm).then(res => {if(res.code==1){//緩存數據this.$store.dispatch('saveLoginInfo', {token: res.data.accesstoken,userinfo: res.data.userinfo})}this.$message.success(res.msg);this.$router.push('/');}).catch(e => {this.$message.error(e.msg);this.resetForm('ruleForm');});} else {return false;}});},// ...}} </script>

    ? ? ? ? 這里輸入用戶名和密碼進行登錄,是沒有返回數據的,因為我們還沒實現數據庫查詢功能,現在大家打開db/model/user.js文件,實現login函數部分代碼。

    ? ? ? ? 在前面“2.3 表結構創建”中,有創建name和accesstoken兩個索引;又在“4.3 添加事務功能”中新增了事務創建相關功能函數,這里咱們就可以用上了。首先登錄用戶名是唯一的,這里通過name索引進行匹配查詢,這里openTransactionIndex函數獲取相應處理對象。

    ? ? ? ? 代碼如下:

    export const login = data => {return new Promise((resolve, reject) => {//打開事務let { index, store } = openTransactionIndex(storeName, 'name', CONST_READ.READWRITE);//通過用戶名,獲取用戶數據let indexData = index.get(data.username);indexData.onerror = function(e){reject(rJson(0, e, '查詢出錯了~'));}indexData.onsuccess = function(e){let result = e.target.result;if(result){//判斷密碼是否一致if(result.password==hex_md5(data.password)){let accesstoken = randomStrName(80);//記錄token值result['accesstoken'] = accesstoken;//保存tokenstore.put(result);//返回結果數據resolve(rJson(1, {accesstoken,userinfo: {id: result.id,username: result.name,phone: result.phone}}, '登錄成功~'))}else{reject(rJson(0, null, '密碼錯誤~'))}}else{reject(rJson(0, null, '用戶名錯誤~'))}//if end}}); }

    ? ? ? ? 以上功能完成后,我們就可以實現登錄了,這時我們在登錄界面輸入用戶名和密碼,則可以跳轉到首頁了。在“2.3 表結構創建”中,默認添加了admin賬號,暫時可以通過這個賬號登錄系統,登錄后界面如下:

    4.7 實現退出功能

    ? ? ? ? 在“3.3 router路由定義”中的JS部分,我們調用下退出接口,完成退出功能,并通過“3.2.5 創建actions.js文件”中定義的exitLogin函數,清除本地緩存數據。代碼如下:

    <script> import { logoutInfo } from '@/api' import { TOKEN } from '@/store/mutationsType' export default {name: 'Layout',data () {return { }},methods: {/*** 退出登錄*/logoutEvent(){this.$confirm('確認好退出登錄嗎?', '提示', {confirmButtonText: '退出',cancelButtonText: '取消',type: 'warning'}).then(() => {logoutInfo(this.$ls.get(TOKEN)).then(res => {this.$store.dispatch('exitLogin');this.$message.success(res.msg);this.$router.push('/login');}).catch(e => {this.$message.error(e.msg);this.$router.push('/login');})}).catch(() => {this.$message({type: 'info',message: '已取消退出'})})}} } </script>

    ? ? ? ? 同樣,現在點擊退出按鈕不會得到響應,需要將db/model/user.js中的logout功能實現后,才能真正實現退出功能。

    ? ? ? ? 代碼如下:

    export const logout = token => {return new Promise((resolve, reject) => {//打開事務let {index} = openTransactionIndex(storeName, 'accesstoken', CONST_READ.READWRITE);//通過token,獲取對應用戶數據let indexData = index.get(token);indexData.onerror = function(e){reject(rJson(0, e, '查詢出錯了~'));}indexData.onsuccess = function(e){let result = e.target.result,source = e.target.source;if(result){let store = source.objectStore;//判斷token信息是否匹配if(result.accesstoken == token){//刪除accesstokendelete result['accesstoken'];//更新數據store.put(result);resolve(rJson(1, null, '退出成功~'));}else{reject(rJson(0, null, '登錄失效~'))}} else{reject(rJson(0, null, '未查詢到登錄信息~'))}}}); }

    ? ? ? ? 這里我們在登錄狀態下,點擊退出按鈕,退出成功后就會直接跳轉到登錄界面了。

    4.8 校驗緩存數據中的登錄信息

    ? ? ? ? 我們又要回到 “?3.2.5 創建actions.js文件 ”中,為什么當初在定里定義函數判斷token失效,而不是直接放到 路由衛生中獲取token,直接進行判斷呢;因為這里除了要校驗本地是否緩存token信息,同時也要判斷數據庫中token是否存在或失效,話不多說,直接上代碼。

    ? ? ? ? 在db/model/user.js中添加checkToken函數,代碼如下:

    /*** 校驗token是否存在*/ export const checkToken = token => {}

    ? ? ? ? 在api/index.js中添加tokenIsFailure函數,代碼如下:

    import { login, logout, checkToken } from '@/db/model/user'/*** 判斷數據庫中token是否失效*/ export const tokenIsFailure = token => {return checkToken(token); }

    ? ? ? ? store/actions.js文件中的checkIsLogin進行改造,代碼如下:

    checkIsLogin(){let token = Vue.ls.get(TOKEN);return new Promise((resolve, reject) => {if(token){tokenIsFailure(token).then(() => {resolve();}).catch(() => {commit(TOKEN, '');Vue.ls.remove(TOKEN);reject();});}else{reject();}});}

    ? ? ? ? 當讀取到本地緩存的token信息后,還需要進行數據庫校驗;如果數據庫中不存在,則會直接跳轉到登錄頁,并清除本地緩存的token信息。

    ? ? ? ? 另外,我們之前在“2.2 打開數據庫”中定義過DBIsLoadSuccess函數,因為IndexedDB的open()函數不是執行后就返回實例對象的,所有剛進入系統,很有可能會遇到實例對象為空的情況。這時我們需要特別小心,在調用事務前,先判斷數據庫實例對象是否存在。

    ????????接下來讓我們完成db/model/user.js中的checkToken函數功能,代碼如下:

    export const checkToken = token => {return new Promise((resolve, reject) => {//判斷數據庫是否打開DBIsLoadSuccess().then(() => {//打開事務let {index} = openTransactionIndex(storeName, 'accesstoken');//通過token,查詢數據let indexKey = index.getKey(token);indexKey.onerror = function(e){reject(rJson(0, e, '查詢出錯了~'));}indexKey.onsuccess = function(e){let result = e.target.result;if(result){resolve(rJson(1, e, '校驗成功~'))}else{reject(rJson(0, e, '登錄失效~'));}}}).catch(() => {reject(rJson(0, e, '數據庫打開失敗~'));})}); }

    ? ? ? ? 這里我們先手動清除數據表中的accesstoken,然后點擊任意頁面跳轉,此時會發現頁面直接跳轉到登錄頁了。

    ? ? ? ? 正常情況下,在登錄時創建的token,是帶有時效的,這塊這里就不細講,大家可以通過自己的理解,去完善此部分。

    ? ? ? ? 至此,該篇內容已講解完了,該系統其他功能完善請看后續篇幅。

    下一篇地址:本地數據庫IndexedDB - 學員管理系統之列表管理(二)_覺醒法師的博客-CSDN博客

    總結

    以上是生活随笔為你收集整理的本地数据库IndexedDB - 学员管理系统之登录(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。