1. 程式人生 > >vue,vuex的後臺管理專案架子structure-admin,後端服務nodejs,前端vue頁面

vue,vuex的後臺管理專案架子structure-admin,後端服務nodejs,前端vue頁面

1、vuex來實現狀態管理2、靜態頁面,未引入後端服務3、元件是用的是element-ui4、頁面佈局是上左右,左右佈局使用的彈性和佈局flex,左邊定寬,右邊計算寬度5、左右的滾動條是相互獨立的,去掉body上的滾動條6、沒有業務程式碼,僅僅是一個靜態的vuex的架子說明:之前使用左側menu的fixed佈局,發現element-ui的彈窗元件不能正常顯示,考慮換成flex佈局

接下來,針對structure-admin-web的不足,進行了補充,開發了具有登陸的structure-admin專案,技術站:主要是node+vue+redis+mysql+es6

接下來:

一、後端服務nodejs,thinjs的redis配置,操作資料庫

二、前端vue+vuex全域性路由守衛,axios請求攔截

三、專案啟動必讀

一、後端服務nodejs,thinjs的redis配置,操作資料庫

我使用的是thinkjs,一個nodejs的框架。

1、首先介紹登陸的控制

邏輯:

(1)已經登陸的,直接根據的路由跳到相應頁面;

(2)已經登陸的,不能跳到登陸頁面,跳到首頁;

(3)沒有登陸的,輸入url必須跳到登陸頁;

(4)退出系統的,必須回到登陸頁,狀態是未登入

1.1 thinkjs的redis的配置檔案adapter.js

exports.session = {
    type: 'redis',
    common: {
        cookie: {
            name: 
'thinkjs', keys: ['werwer', 'werwer'], signed: true } }, redis: { handle: redisSession, host: '127.0.0.1', port: 6379, password: 'a123456' } };

1.2  在每一次前端請求的路由的時候,都會去redis伺服器中去取userInfo的資訊

如果為空,返回前端data為空,前端在路由過濾中,跳到登陸頁,如果有值就正常返回。 

async __before() {
        let user = await this.session("userInfo");
        if(user) {
            this.user = user;
        } else {
            this.user = '';
        }
    }

這個在nodejs的控制器controller裡,在每一次前端的請求傳送到後端的時候,都會去redis的取userInfo的值,

let user = await this.session("userInfo");

這個userInfo的值也是自己在登陸的時候,把登陸成功之後的個人資訊加入到redis服務中

1.3 在登陸成功的時候講個人資訊加到redis服務中

async loginAction() {
        let {username, password} = this.post();try {
            let user = await this.model('user').where({
                username,
            }).find();
            if(user.password && user.password == password) {
                // login success
                await this.session('userInfo',{username, userId:user.id});
                return this.success("登陸成功");
            } else {
                return this.fail("使用者名稱或密碼錯誤")
            }
        }
        catch(e) {
            console.log(e);
            return this.fail("登入失敗")
        }

這個就是將個人資訊加入到redis中

await this.session('userInfo',{username, userId:user.id});

WEB 請求中經常通過 session 來維持會話的,框架通過 think-session 和 Adapter 來支援 session 功能。

2、介紹登出(退出)的控制

 async logoutAction() {
        try {
            await this.session(null);
            return this.success("登出成功");
        } catch(e) {
            return this.fail(`登出失敗${e}`)
        }
    }

這個就是前端發的請求登出,直接將redis的置空,根據前端路由跳轉到登陸頁,這時候redis的服務中沒有值,就不會跳轉到其他頁面

3、資料庫的配置adapter.js

exports.model = {
  type: 'mysql',
  common: {
    logConnect: true,
    logSql: true,
    logger: msg => think.logger.info(msg)
  },
  mysql: {
    handle: mysql,
    database: 'example',
    prefix: 'example_',
    encoding: 'utf8',
    host: '127.0.0.1',
    port: '3306',
    user: 'root',
    password: '123456',
    dateStrings: true
  }
};

common部分是配置是否將sql的語句的操作日誌打出來,這樣便於我們在開發的時候的除錯和修改bug

4、操作資料庫

專案開發中,經常需要操作資料庫(如:增刪改查等功能),手工拼寫 SQL 語句非常麻煩,同時還要注意 SQL 注入等安全問題。為此框架提供了模型功能,方便操作資料庫。

Mysql 的 Adapter 為 think-model-mysql,底層基於 mysql 庫實現,使用連線池的方式連線資料庫,預設連線數為 1。

登陸的介面來說明:this.model說明使用封裝好的model,find是查詢單條資料,在user的這張表中查詢username值為前端傳來的username的值,返回的值賦給user中。

async loginAction() {
        let {username, password} = this.post();
        try {
            let user = await this.model('user').where({
                username,
            }).find();
            if(user.password && user.password == password) {
                // login success
                await this.session('userInfo',{username, userId:user.id});
                return this.success("登陸成功");
            } else {
                return this.fail("使用者名稱或密碼錯誤")
            }
        }
        catch(e) {
            console.log(e);
            return this.fail("登入失敗")
        }

think.Model 基類提供了豐富的方法進行 CRUD 操作,下面來一一介紹。

查詢資料

模型提供了多種方法來查詢資料,如:

  • find 查詢單條資料
  • count 查詢總條數
  • max 查詢欄位的最大值
  • avg 查詢欄位的平均值
  • min 查詢欄位的最小值
  • sum 對欄位值進行求和

同時模型支援通過下面的方法指定 SQL 語句中的特定條件,如:

  • where 指定 SQL 語句中的 where 條件
  • limit / page 指定 SQL 語句中的 limit
  • field / fieldReverse 指定 SQL 語句中的 field
  • order 指定 SQL 語句中的 order
  • group 指定 SQL 語句中的 group
  • join 指定 SQL 語句中的 join
  • union 指定 SQL 語句中的 union
  • having 指定 SQL 語句中的 having
  • cache 設定查詢快取

新增資料

模型提供了下列的方法來新增資料:

  • add 新增單條資料
  • thenAdd where 條件不存在時新增

更新資料

模型提供了下列的方法來更新資料:

刪除資料

模型提供了下列的方法來刪除資料:

用專案的程式碼舉栗子:

(1)查詢單條資料,用find(),條件為:工號(usernum)為180909,使用者名稱(username)為saucxs ,並且填寫時間(time)為這周的時間範圍的時間戳,返回的是物件object

 let weekly = await this.model('week').where({
     usernum: '180909',    username: 'saucxs',    time: {'>': startWeekStamp, '<': endWeekStamp}
 }).find();

解讀:model('week')的意思,取得是week的資料表

(2)查詢多條資料,用select(),條件:公司id(company_id)為data的資料,返回的是陣列array

let department = await this.model('department').where({company_id: 'data'}).select();

(3)查詢表中的具體的列資料,用field()

departmentMemberList = await this.model('user').field('id, company_id, company_name, department_id, department_name, email, role, role_name, username, usernum,telephone').where({
          company_id: this.user.company_id,
          role: {'>=': this.user.role}
        }).find();

解讀:this.user.company_id取的是登陸使用者的公司id,{'>=': this.user.role}為比登陸使用者的角色

(4)分頁查詢,用page(page, pagesize)和countSelect(),返回的資料是物件

departmentMemberList = await this.model('user').field('id, company_id, company_name, department_id, department_name, email, role, role_name, username, usernum,telephone').where({
          company_id: this.user.company_id,
          role: {'>=': this.user.role}
        }).order("department_id asc , role asc").page(page, pagesize).countSelect();

解讀:返回的物件,如下圖所示:(count是總條數,currentPage為當前頁,data是資料的陣列,pageSize為每一頁展示幾條,totalPages為總共有多少頁)

(5)排序,倒序(desc)和正序(asc),用order("引數1 asc,引數2 desc”)

departmentMemberList = await this.model('user').field('id, company_id, company_name, department_id, department_name, email, role, role_name, username, usernum,telephone').where({
          company_id: this.user.company_id,
          role: {'>=': this.user.role}
        }).order("department_id asc , role asc").page(page, pagesize).countSelect();

(6)刪除,用delete(),條件用where

 await this.model('department').where({company_id, department_id}).delete();

(7)新增,用add(),沒有where

  await this.model('department').add({
          company_id: this.user.company_id, company_name: this.user.company_name, department_id, department_name
        });

(8)改,用update(),條件where

 await this.model('user').where({id}).update({
                usernum, username, telephone, role, role_name,email, company_id, company_name, department_id, department_name
              });

手動執行 SQL 語句

有時候模型包裝的方法不能滿足所有的情況,這時候需要手工指定 SQL 語句,可以通過下面的方法進行:

  • query 手寫 SQL 語句查詢

二、前端vue+vuex全域性路由守衛,axios請求攔截

剛才簡單的說了一下nodejs的後端啟動的服務,封裝的介面,而前端呼叫這個介面使用的是url是:模組名/控制器名/方法名,這個可以在配置檔案中修改定義的方法

1、全域性路由守衛

全域性路由守衛是每一次都會判斷是否登陸(也就是判斷redis服務中是否有值)。已經登陸(後端返回的使用者許可權資訊),則判斷當前要跳轉的路由,使用者是否有許可權訪問,可以考慮在使用者登陸之後將使用者許可權把路由過濾一遍生成選單,選單儲存到vuex中。

/*路由處理*/
router.beforeEach((to, from, next) => {
  let menuId;
  let auditResult;
  let applicationVerifyFlag;
  let key = to.meta.key;
  if (key) {
    store.dispatch("getUserInfo", {}).then(response => {if(!response.data){
        if (to.path !== '/login') {
          return next('/login');
        }
        next();
      }else{
        if (to.path == '/login') {
          return next('/writeWeekly');
        }
        store.commit("USER_INFO", response.data);
        next();
      }
    });
  } else {
   next();
  }
});

根據這個key來判斷是否有許可權,取得是路由中meta的key的值。

  routes: [
    {
      path: '/login',
      name: 'login',
      meta: {
        key: '0'
      },
      component: login
    },
    {
      path: '/',
      name: 'home',
      component: home,
      children: [{
        path: '/writeWeekly',
        name: 'writeWeekly',
        meta: {
          key: '1'
        },
        component: writeWeekly
      }]
    }
  ]

2、axios請求攔截

統一處理所有的http請求和響應的,通過配置http request interceptors為http頭部增加Authorization欄位,其內容為Token,通過配置http response interceptors,當後端介面返回401 Unauthorized(未授權),讓使用者重新登入。

// 開發環境除錯使用者資訊
axios.interceptors.request.use(config => {
    if (process.env.NODE_ENV === 'development') {
      config.headers["username"] = "189090909";
    }
    return config;
});

axios.interceptors.response.use(
  response => {
    let data = response.data;
    console.log(data, 'data');
    if (!data.data) {
      //   登陸成功的回撥地址
      return data;
    } else {
      return data;
    }
  },
  error => ({
    code: -1,
    msg: "網路異常"
  })
);

對所有的請求進行了封裝。

// get請求配置
let getConfig = {
    url: '',
    baseURL: serveUrl,
    headers: {
    'X-Requested-With': 'XMLHttpRequest'
  },
  paramsSerializer(params) {
    return Qs.stringify(params, {
      arrayFormat: 'brackets'
    })
  },
  timeout: 5000
}

// post請求配置
let postConfig = {
    url: '',
    baseURL: serveUrl,
    headers: {
      'Content-Type': 'application/json',
      'X-Requested-With': 'XMLHttpRequest'
    },
    transformRequest: [function (data) {
      return JSON.stringify(data.params || {})
    }],
    timeout: 5000
}

export {
    serveUrl,
    getConfig,
    postConfig,
  }

三、專案啟動必讀

2、clone下來專案

git clone https://github.com/saucxs/structure-admin.git
npm install

4、安裝redis(可以考慮安裝RedisDesktopManager)

5、安裝mysql,這個就不贅述

6、修改nodejs的後端的配置檔案adapter.js,config.js這兩個檔案中

adapter.js

exports.cache = {
    type: 'redis',
    common: {
        timeout: 24 * 60 * 60 * 1000 // millisecond
    },
    redis: {
        handle: redisCache,
        host: '127.0.0.1',
        port: 6379,
        password: 'a123456'  //redis安裝時候設定的祕密
    }
};////
exports.model = {  type: 'mysql',  common: {    logConnect: true,    logSql: true,    logger: msg => think.logger.info(msg)  },  mysql: {    handle: mysql,    database: 'weekly',    prefix: 'week_',    encoding: 'utf8',    host: '127.0.0.1',   //本地資料庫    port: '3306',     //資料庫埠    user: 'root',    //資料庫的使用者名稱    password: '123456',    //資料庫該使用者名稱的密碼    dateStrings: true  }};

7、分別對前後端分離的專案啟動

npm run dev
npm start

8、這樣就可以啟動

(1)登陸頁

(2)寫週報頁面

 9、歡迎fork和start該專案

 不懂的地方可以提issue,歡迎提出來共同探討

10、該專案架子搭的週報企業管理系統