從零開始打造 Mock 平臺 - 功能模塊篇

前言

二月初想一想這個月還得搗鼓一篇文章,也沒啥好的想法那仍是記錄一下畢設的一些思路吧。前端

重要功能

一些擴展的重要功能將在這裏一點點從零開始進行思考。git

項目導入導出功能

項目導入導出的構想是在設定特點功能時候想到的,主要是用於不一樣服務器上若是部署了平臺,若是想要本身私下部署測試,那麼從新創建項目,而後再一個個配置接口路徑,配置返回數據是一件很麻煩的事情。爲了之後用(自)戶(己)用的順暢,想了一鍵各類版本的功能,其中就包括項目的導入導出。github

所以在設計數據結構的時候,我將最終返回結果數據設計成以下形式json

{
  project:{
       projectId:,
       ...
    interfaceList: []
  }
}

也就是前端本地緩存的數據就能夠直接導出。redux

固然導出能夠把一些信息先過濾處理一遍,好比項目Id ,接口 Id。後端

因而導出能夠爲一個純 Json 文本。api

而後倒入時候須要驗證 Json 格式,其實就是須要作個遍歷,看看該有的屬性有沒有,沒有的話就報錯不進行數據導入。數組

並且導入的時候須要作個分類,是導入到項目示例仍是我的/團隊項目。緩存

常見的是打包下載 zip。這個大概須要後端處理,所以先找找有沒有前端處理的。服務器

搜了一下方案,再結合 github 上一些項目的源碼,能夠簡單的寫出一個導出模塊

export function exportFile(data: string, filename: string, type: string) {
    var typeList = {
      json: 'application/json;charset=utf-8',
      markdown: 'text/markdown;charset=utf-8',
      doc: 'application/mswordcharset=utf-8',
    }
    // 建立隱藏的可下載連接 
    var eleLink = document.createElement('a');
    eleLink.download = filename;
    eleLink.style.display = 'none'; // 字符內容轉變成blob地址 
    var blob ;
    blob= new Blob(['\uFEFF' + data],{type: typeList[type]});

    eleLink.href = URL.createObjectURL(blob); 
    document.body.appendChild(eleLink);
    eleLink.click();
    document.body.removeChild(eleLink);
}

調用方式很簡單,就是傳入三個參數:

exportFile(JSON.stringify(this.state.currentProjectData), 'default.md', 'markdwon')

imgn

固然咱們如今得到的數據是沒有進行處理的,咱們須要對數據作個過濾,剔除一些關鍵信息以及不必的數據。

假設如今的數據是這樣的

{
  "_id": "project001",
  "projectName": "演示項目 - REST接口示例超長字符串測試asd123",
  "projectUrl": "/project001",
  "projectDesc": "項目描述",
  "version": "v1.0",
  "transferUrl": "http://haoqiao.me/api/project",
  "status": "transfer",
  "type": "demo",
  "teamMember": [
    {
      "_id": "user001",
      "username": "2333",
      "role": "前端工程師",
      "avatar": "https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
    },
    {
      "_id": "user002",
      "username": "宋青樹",
      "role": "後端工程師",
      "avatar": "https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
    }
  ],
  "interfaceList": [
    {
      "_id": "interface001",
      "interfaceName": "獲取",
      "url": "/getAll",
      "method": "get",
      "desc": "接口描述",
      "mode": "{data: 1 || 2}"
    },
    {
      "_id": "interface002",
      "interfaceName": "增長",
      "url": "/add",
      "method": "post",
      "desc": "接口描述",
      "mode": "{data: 1 || 2}"
    },
    {
      "_id": "interface003",
      "interfaceName": "刪除",
      "url": "/delete",
      "method": "delete",
      "desc": "接口描述",
      "mode": "{data: 1 || 2}"
    },
    {
      "_id": "interface004",
      "interfaceName": "更新",
      "url": "/update",
      "method": "put",
      "desc": "接口描述",
      "mode": "{data: 1 || 2}"
    }
  ]
}

咱們須要將裏面全部的 _id(字段), teamMember(數組) 去除。這裏你們想一想若是是本身要怎麼處理?

其實很是簡單,只要你對原生的 JSON.stringify 比較熟悉,你就知道它的完整定義以下

JSON.stringify(value, replacer?, space?)

replacer 是一個過濾函數或則一個數組包含要被 stringify 的屬性名。若是沒有定義,默認全部屬性都被 stringify

能夠作一個遍歷器,遍歷Json裏的屬性名。而後內部作個剔除。

好比這樣

filterData = (json: any) =>{
    console.log(json)
    let expectArr = ['_id', 'teamMember']
    let filterArr = []
    let result = ''
    for( let key in json){
      if (expectArr.indexOf(key) === -1){
      filterArr.push(key)
    }
    result = JSON.stringify(this.state.currentProjectData, filterArr)
    console.log(result)
    
    return result

  }

但這樣只能拿到第一層的屬性名,如何拿到嵌套的數組裏的Json的屬性呢?

咱們只須要再作個判斷就能夠了

filterData = (json: any) =>{
    console.log(json)
    let expectArr = ['_id', 'teamMember']
    let filterArr = []
    let result = ''
    for( let key in json){
      if (expectArr.indexOf(key) === -1){
        filterArr.push(key)
        // 若是是嵌套數組,並且數組內有數據
        if(Object.prototype.toString.call(json[key]) == "[object Array]" && json[key].length > 0){
          for( let item in json[key][0]){
            // 一樣對裏面的json數據進行屬性字段過濾
            if (expectArr.indexOf(item) === -1){
              filterArr.push(item)
            }
          }
        }
      }
    }
    result = JSON.stringify(this.state.currentProjectData, filterArr)
    console.log(result)
    
    return result

  }

這樣就把該有的屬性篩選出來了。而後作個轉換就能過濾只剩須要的數據。

數據清理後就變成以下格式

{
  "_id": "project001",
  "projectName": "演示項目 - REST接口示例超長字符串測試asd123",
  "projectUrl": "/project001",
  "projectDesc": "項目描述",
  "version": "v1.0",
  "transferUrl": "http://haoqiao.me/api/project",
  "status": "transfer",
  "type": "demo",
  "teamMember": [
    {
      "_id": "user001",
      "username": "2333",
      "role": "前端工程師",
      "avatar": "https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
    },
    {
      "_id": "user002",
      "username": "宋青樹",
      "role": "後端工程師",
      "avatar": "https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
    }
  ],
  "interfaceList": [
    {
      "_id": "interface001",
      "interfaceName": "獲取",
      "url": "/getAll",
      "method": "get",
      "desc": "接口描述",
      "mode": "{data: 1 || 2}"
    },
    {
      "_id": "interface002",
      "interfaceName": "增長",
      "url": "/add",
      "method": "post",
      "desc": "接口描述",
      "mode": "{data: 1 || 2}"
    },
    {
      "_id": "interface003",
      "interfaceName": "刪除",
      "url": "/delete",
      "method": "delete",
      "desc": "接口描述",
      "mode": "{data: 1 || 2}"
    },
    {
      "_id": "interface004",
      "interfaceName": "更新",
      "url": "/update",
      "method": "put",
      "desc": "接口描述",
      "mode": "{data: 1 || 2}"
    }
  ]
}

以後是要考慮導入項目,我首先想到點擊上傳 JSON 格式文件而後讀取裏面的內容,而後驗證數據再讓後臺將其導入到指定表中。

這裏面發生了一些事情,好比我確定不但願用戶真的把json文件上傳到服務器上,我以爲這玩意前端確定能解析和解決,可是咱們確定仍是須要一個上傳的按鈕和UI交互。

這裏我直接用了 antdupload 組件,它裏面有個方法就是 beforeUpload , 只要咱們在這個函數裏直接返回 false 那麼是不會真正觸發上傳動做的,可是咱們又能夠拿到本地的 File,能夠用 HTML5 的新方法 FileReader 來幫助讀取內容。

咱們的組件能夠這麼修改

const uploadProps = {
      name: 'file',
      action: '',
      showUploadList: false,
      beforeUpload: (file: any) => {
        const isJSON = file.type === 'application/json';
        if (!isJSON) {
          Message.error('只容許上傳JSON格式文件!');
        }
        const isLt2M = file.size / 1024 / 1024 < 2;
        if (!isLt2M) {
          Message.error('JSON文件大小必須小於 2MB!');
        }
        var reader = new FileReader(); // 讀取操做都是由FileReader完成的
        var that = this
        reader.readAsText(file);
        reader.onload = function(){//讀取完畢從中取值
          const json = this.result
          if(isJson(json) && that.state.uploadJsonData.length === 0){
            that.setState({
              uploadProject:true,
              uploadJsonData: json
            })
            Message.success('Json文件上傳識別成功!');
          }
        }
        return false;
      }},
      
      onChange: (info: any) => {
      },
    };
    
   <Dragger {...uploadProps}>
      <p className="ant-upload-drag-icon">
      <Icon type="inbox" />
      </p>
      <p className="ant-upload-text">點擊上傳JSON文件或者拖拽上傳JSON文件</p>
              
  </Dragger>

來看下實際的交互效果

imgn

這樣咱們就打通了項目的導入導出功能的交互。以後就是接口對接一下就好了。

項目克隆 / 接口克隆

這兩個功能其實很相似,主要是用於幫助用戶可以複製已經存在的接口或者項目。
好比我已經以前創建了一套系統的接口,包括了增刪減改。我下一個系統和這套系統很相似,可能只需改幾個字段就能夠用了。
咱們固然能夠利用導入和導入功能,可是在系統內部咱們最好有一鍵遷移的方式,那就是克隆。

克隆咱們須要注意,首先是接口克隆,假設咱們接口定義的格式以下:

_id(pin): "interface005"
interfaceName(pin): "註冊"
url(pin): "/reg"
method(pin): "post"
desc(pin): "接口描述"
mode(pin): "{data: 1 || 2}"

而後我爲了方便定義的項目 Model 裏面包含了接口 Model。

也就是我只需把 接口 Id,和 項目 Id 傳給後臺,讓後臺作一個查詢接口內容,而後新建接口把查詢到的內容插入到指定 Id 就能夠了。

這很簡單。主要部分是 UI 這塊,不過經過對數據流的管理也是花時間就能解決的事情。以下圖:

imgn

以後是克隆項目這塊,
咱們首先已經知道項目 Model 裏面包含了 接口 Model,所以咱們克隆整個項目實際上是須要將整個接口提取出來,團隊成員是須要剔除的,由於新克隆項目應該是隻有建立者,所以咱們須要把 用戶 Id 也從前端傳過去,固然也可不傳,經過 Jwt 對 token 解析也能識別用戶信息。

主要是後端拿到信息以後它的思路應該是先查詢這個項目的信息,而後提取部分信息建立新項目, 而後遍歷原有項目的接口列表,批量建立接口。

我的信息的更改

基本上中後臺的應用都會有我的信息管理這項,有的用表單,有的拆分。

其他數據都好搞定,無非是傳參的問題,先後端約定的問題。
固然比較麻煩的實際上是頭像的更改。
假設你註冊的時候默認分配給一個用戶頭像,而後再我的信息裏用戶想要更改。
這時候問題來了。更改頭像實際上是一個交互問題,你確定不能讓用戶一步步操做。而是一步到位,符合要求的圖片上傳以後,拿到上傳後的圖片地址。而後更改本地的數據。還須要在後臺自動更新數據。

imgn

這裏是用 redux 維護了一個本地的前端數據層,全部顯示的變動顯示操做須要對其進行更新。

而後是 reducer 裏監聽了動做, 定義爲 UPDATE_LOCALXXX 的動做是用於提交數據後等待後臺處理完畢後返回成功,而後將本地的數據進行更新。

顯而易見這個動做是異步操做。若是不少個相似操做須要管理咱們代碼寫起來確定會很亂。所以我在技術評估階段引入了 rxjs

經過其特色時間線的管理,就很容易了。如下是簡單的示例代碼

export const userUpdate = (action$: any) =>
  action$.ofType(UPDATE_USER)
    .mergeMap((action: any) => {
      return fetch.post(updateUser, action.data)
        // 登陸驗證狀況
        .map((response: any) => {
          console.log(response);
          if (response.state.code === 1) {
            updateUserSuccess(response.state.msg);
            return updateLocalUser(action.data);
          } else {
            console.log('token error')
            updateUserError(response.state.msg);
            return nothing();
          }
        })
        // 只有服務器崩潰才捕捉錯誤
        .catch((e: any): any => {
          // console.log(e)
          return Observable.of(({ type: USER_LOGINERROR })).startWith(loadingError())
        })

    });

結尾

還有一些功能須要等後端開發的時候再記錄思路,所以這部分先到這裏。

相關文章
相關標籤/搜索