從零開始搞后台管理系統(1)——shin-admin


  

  shin 的讀音是[ʃɪn],諧音就是行,寓意可行的后台管理系統,shin-admin 的特點是:

  • 站在巨人的肩膀上,依托Umi 2Dva 2Ant Design 3React 16.8搭建的定制化后台。
  • 介於半成品和成品之間,有很強的可塑性,短期內你就能把控全局。
  • 借助模板組件可快速交付90%以上的頁面。
  • 多樣的權限粒度,大到菜單,小到接口。
  • 容易擴展,例如引入統計用的圖表或富文本編輯器等。

  當然它還有一些不友好的地方:

  • 大流程絕對能跑起來,但仍潛伏着很多細節BUG有待解決。
  • 有一定的學習成本,需要學習Umi配置,Dva數據流方案,Ant Design組件以及React、ES6+等語法。

准備工作

1)安裝

  在將項目下載下來后,來到其根目錄,運行安裝命令,自動將依賴包下載到本地。

$ npm install

2)啟動

  啟動開發服務器,默認會進入登錄頁(下圖),由於會調用本地的Mock數據,所以即使沒有后端服務器,項目也能運行。若要與后端配合,可參考 shin-server

$ npm start

  

  用戶名密碼可以隨意輸入,提交后進入系統主頁,目前是空白的,可自定義。

  

3)構建

  在開發完成后調用構建命令,可自動生成dist目錄,將該目錄上傳到服務器上用於部署。

$ npm run build

  在 package.json 的 scripts 字段中,還提供了其他命令,例如 lint、test 等。

4)運行流程

  管理系統運行的大致流程,如下圖所示,其中賬號的登錄態認證,基於JWT的方式

  

目錄

├── shin-admin
│   ├── docs ----------------------------------- 說明文檔
│   ├── mock ----------------------------------- MOCK 數據
│   ├── src ------------------------------------ 源碼
│   ├───└─── api ------------------------------- 接口API聲明
│   ├───└─── assets ---------------------------- 靜態資源
│   ├───└─── components ------------------------ 全局通用組件
│   └───└────└──── Common ---------------------- 功能組件
│   └───└────└──── Layout ---------------------- 結構組件
│   └───└──── layouts -------------------------- 頁面整體結構
│   └───└──── models --------------------------- 全局 model(數據處理)
│   └───└──── pages ---------------------------- 頁面
│   └───└────└──── path ------------------------ 頁面路徑(任意名稱)
│   └───└────└────└──── index.js --------------- 視圖邏輯
│   └───└────└────└──── model.js --------------- 頁面 model
│   └───└──── services ------------------------- 與后端通信的服務(可選)
│   └───└──── utils ---------------------------- 各類工具輔助代碼
│   └───└──── app.js --------------------------- 運行時配置,處理40X狀態碼
│   └───└──── routes.js ------------------------ 路由
│   └───└──── authority.js --------------------- 權限
│   └───└──── global.less ---------------------- 全局樣式
│   ├── .env ----------------------------------- 環境變量
│   ├── .umirc.js ------------------------------ umi 配置
└───└── package.json --------------------------- 命令和依賴包

1)api

  api目錄下可包含多個文件,默認只有一個 index.js,聲明了與后端通信的 API 地址,例如。

export default {
  templateCreate: "template/create",    //模板示例中的創建和編輯
  templateQuery: "template/query",      //模板示例中的查詢
  templateHandle: "template/handle",    //模板示例中的數據處理
}

2)components

  功能組件包括重置密碼、拖動列表、上傳按鈕和模板組件,具體用法可參考此處

  結構組件包括頂部導航、側邊菜單欄、面包屑導航和快速搜索,在上面的主頁圖中已體現。

3)models

  model 文件是 Dva 中的概念,用於處理組件中的數據(下面是數據流向圖),典型事例參考此處

app.model({
  namespace: 'app',  //命名空間,同時也是他在全局 state 上的屬性
  state: {},         //初始值
  //處理同步操作,唯一可以修改 state 的地方,由 action 觸發
  reducers: {
    add(state, { payload: todo }) {
      return [...state, todo];  // 保存數據到 state
    },
  },
  //處理異步操作和業務邏輯(和服務器交互),不直接修改 state,由 action 觸發
  effects: {
    *save({ payload: todo }, { put, call }) {
      // 調用 saveTodoToServer,成功后觸發 `add` action 保存到 state
      yield call(saveTodoToServer, todo);
      yield put({ type: 'add', payload: todo });
    },
  },
  //用於訂閱一個數據源,然后根據需要 dispatch 相應的 action
  subscriptions: {
    setup({ history, dispatch }) {
      // 監聽 history 變化,當進入 `/` 時觸發 `load` action
      return history.listen(({ pathname }) => {
        if (pathname === '/') {
          dispatch({ type: 'load' });
        }
      });
    },
  },
});

  

4)pages

  所有頁面的邏輯都放在此目錄下,例如訪問 http://localhost:8000/template/list ,那么就需要先創建 template 目錄,然后創建其子目錄 list,即路徑為 pages/template/list。

  在子目錄中會包含 index.js 和 model.js,偶爾也會創建 less 樣式文件。

  由於采用了 Dva 數據流方案,因此在 index.js 中就不能直接修改內部狀態(state),只能 dispatch 相應的 action,然后在 model.js 文件中更新狀態。

  下面是 index.js 的一個示例,App 組件中的 id 參數是 model 文件中的狀態,dispatch()函數是Dva的庫函數,用於觸發 action。

  底部的 connect() 函數用於連接 model 和 component。app 是 model.js 文件中的命名空間,App 是組件名稱。

import React from 'react';
import { connect } from 'dva';
import { Button } from 'antd';
const App = ({ id, dispatch }) => {
  const onCreate = () => {
    dispatch({
      type: 'app/save',
      payload: {
        id
      },
    });
  };
  return <Button type="primary" onClick={onCreate}>新建</Button>;
};
export default connect(data => data.app)(App);

5)services

  用來與后端通信,但在使用過程中發現經常只是做一層中轉,內部並沒有很多特定的邏輯,例如下面的登錄函數。

  其實就是聲明一個請求地址,要傳遞的數據以及請求方法。

import request from '../utils/request';
export async function login(data) {
  return request('/api/user/login', {
    method: 'POST',
    data,
  });
}

  完全可以提煉出來,直接在 model.js 文件中直接發起請求,例如先在 api 處聲明好地址(代碼中的 url 參數),redirect、get 和 post 是封裝的三種請求方式。

import { redirect, get, post } from 'utils/request';
export default {
    namespace: 'template',
    state: {},
    effects: {
      //查詢
      *query({ payload }, { call, put }) {
        const { url, params } = payload;
        const { data } = yield call(get, url, params);
      },
      //Excel導出
      *export({ payload }, { call, put }) {
        yield call(redirect, payload.url, payload.params);
      },
      //處理數據,增刪改
      *handle({ payload }, { call, put, select }) {
        const { url, params } = payload;
        const { data } = yield call(post, url, params);
      },
    },
  };

6)utils

  utils 目錄中的文件如下:

  • config.js:全局配置參數
  • constants.js:全局常量
  • menu.js:菜單處理
  • request.js:基於 axios 封裝的通信庫
  • tools.js:雜七雜八的工具函數

7)app.js

  app.js 在處理各種異常響應時會給出不同的提示,在401時會跳轉到登錄頁。

export const dva = {
  config: {
    onError(error) {
      if (error.status) {
        switch (error.status) {
          case 401:
            window.location = '/login';
            break;
          case 403:
            message.error('403 : 沒有權限');
            break;
          case 404:
            message.error('404 : 對象不存在');
            break;
          case 409:
            message.error('409 : 服務升級,請重新登錄');
            break;
          case 504:
            message.error('504 : 網絡有點問題');
            break;
          default:
            Modal.error({ content: `${error.status} : ${error.response.data.error}` });
        }
      } else {
        Modal.error({ content: error.message });
      }
    },
  },
};

8)routes.js

  routes.js會聲明組件和路由之間的映射關系,其實 pages 目錄下的各個頁面就是一個個的組件。

module.exports = [
  {
    path: '/',
    component: '../layouts/index',    //component 相對於 src/pages 目錄
    routes: [
      { path: '/', component: 'index' },
      { path: '/login', component: 'login/', exact: true },
      { path: '/template/list', component: 'template/list/', exact: true },
      { path: '/*', component: '404', exact: true },
    ]
  }
];

9)authority.js

  authority.js 中的權限會形成一棵樹形結構,當 type 為 1 時,會在左側菜單欄中展示,為 2 時就僅做一個接口權限。圖標的選擇可參考此處

/**
 * 權限列表
 * @param id      {string} 權限id
 * @param pid     {string} 父級權限id
 * @param status  {number} 是否開啟 1 開啟 2 關閉
 * @param type    {string} 權限類型 1 菜單 2 接口
 * @param name    {string} 權限名稱
 * @param desc    {string} 權限描述
 * @param routers {string} 權限相關路由
 * @param icon    {string} 菜單圖標
 */
export default [
    {
      id: 'backend',
      pid: '',
      status: 1,
      type: 1,
      name: '管理后台',
      desc: '',
      routers: '/',
      icon: 'desktop',
    },
    {
      id: 'backend.template',
      pid: 'backend',
      status: 1,
      type: 1,
      name: '全局模板',
      desc: '',
      routers: '/template',
      icon: 'file-text',
    },
    {
      id: 'backend.template.list',
      pid: 'backend.template',
      status: 1,
      type: 1,
      name: '列表模板',
      desc: '',
      routers: '/template/list',
    }
]

10).umirc.js

  在 .umirc.js 中可引入路由信息,配置路徑別名,開啟代理服務器。

  當配置了路由別名時,就不需要寫相對路徑了,但是無法使用IDE工具的代碼導航了。

import request from 'utils/request';

搭建

1)常規流程

  在 pages 下的 login 和 user 兩個目錄中,采用了常規的搭建流程。

  • 在 index.js 文件中編寫視圖的各類邏輯,將幾個特定組件抽象到當前的 components 子目錄中。
  • 在 model.js 文件中處理各類組件狀態,並且引用 serveices 中聲明的函數。

  其實很多后台頁面所需的狀態(例如Loading、列表、數量等)和幾個特定組件都差不多,例如過濾條件、列表、模態窗口等,沒必要每次寫頁面都重新聲明一下。

  在此背景下,提煉出了通用的模板組件(用法文檔),位於 components/Common 的 Template 和 Upload 兩個目錄中,效果如下面兩張圖所示。

  

  

2)高速流程

  模板組件(用法文檔)就是將一些頁面交互和數據處理封裝起來,調用的時候只需要定義各類參數,就能快速搭建出一套完整的邏輯,並且能大大減少BUG數量。

  以往搭建下面這樣的一張頁面(包括列表、分頁、創建、查詢、模態窗口等部分),熟練的話也得兩三個小時以上,而采用模板組件的話,最多半小時就能完成。

  

  在 template 目錄中演示了三種類型的模板頁面:列表、表單和照片牆。

  在 tool 目錄中完成了對模板組件的實踐。

3)開發步驟

  1. 在 pages 目錄中創建頁面模塊,分別新建 index.js 和 model.js。
  2. 在 api 目錄中聲明路由或在 services 目錄中創建通信服務。
  3. 如果需要新增菜單欄,得需要三步走。
    • 在 src 目錄的 routes.js 路由文件中聲明路徑。保證 path 唯一性,component以 ”/“ 結尾,默認取該文件夾下 index.js。
    • 在 src 目錄的 authority.js 文件中配置權限列表項,routes 屬性的值對應上面的 component 屬性, id 會與后端權限中間件調用的關鍵字保持一致。
    • 在用戶管理 -》 角色管理 -》角色列表中,為當前角色增加該菜單的訪問權限,然后退出登錄重進。
  4. 重啟項目。

其他

1)MOCK數據

  Umi 框架安裝了第三方的Mock.js模擬請求數據甚至邏輯,能夠讓前端開發獨立自主,不會被服務端的開發所阻塞。

  若要關閉,只要在 .env 文件中添加 MOCK=none 或者在 start 命令中將其添加即可。

MOCK=none umi dev

2)ESLint

  在 .eslintrc 中修改默認的配置,無法生效,無奈只能在某個文件頂部顯式地聲明,以此規避ESLint默認的規則。

/* eslint-disable */

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM