二月初想一想這個月還得搗鼓一篇文章,也沒啥好的想法那仍是記錄一下畢設的一些思路吧。前端
一些擴展的重要功能將在這裏一點點從零開始進行思考。git
項目導入導出的構想是在設定特點功能時候想到的,主要是用於不一樣服務器上若是部署了平臺,若是想要本身私下部署測試,那麼從新創建項目,而後再一個個配置接口路徑,配置返回數據是一件很麻煩的事情。爲了之後用(自)戶(己)用的順暢,想了一鍵各類版本的功能,其中就包括項目的導入導出。github
所以在設計數據結構的時候,我將最終返回結果數據設計成以下形式json
{
project:{
projectId:,
...
interfaceList: []
}
}
複製代碼
也就是前端本地緩存的數據就能夠直接導出。redux
固然導出能夠把一些信息先過濾處理一遍,好比項目Id ,接口 Id。後端
因而導出能夠爲一個純 Json 文本。api
而後倒入時候須要驗證 Json 格式,其實就是須要作個遍歷,看看該有的屬性有沒有,沒有的話就報錯不進行數據導入。數組
並且導入的時候須要作個分類,是導入到項目示例仍是我的/團隊項目。緩存
常見的是打包下載 zip。這個大概須要後端處理,所以先找找有沒有前端處理的。bash
搜了一下方案,再結合 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')
固然咱們如今得到的數據是沒有進行處理的,咱們須要對數據作個過濾,剔除一些關鍵信息以及不必的數據。
假設如今的數據是這樣的
{
"_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交互。
這裏我直接用了 antd
的 upload
組件,它裏面有個方法就是 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>
複製代碼
來看下實際的交互效果
這樣咱們就打通了項目的導入導出功能的交互。以後就是接口對接一下就好了。
這兩個功能其實很相似,主要是用於幫助用戶可以複製已經存在的接口或者項目。 好比我已經以前創建了一套系統的接口,包括了增刪減改。我下一個系統和這套系統很相似,可能只需改幾個字段就能夠用了。 咱們固然能夠利用導入和導入功能,可是在系統內部咱們最好有一鍵遷移的方式,那就是克隆。
克隆咱們須要注意,首先是接口克隆,假設咱們接口定義的格式以下:
_id(pin): "interface005"
interfaceName(pin): "註冊"
url(pin): "/reg"
method(pin): "post"
desc(pin): "接口描述"
mode(pin): "{data: 1 || 2}"
複製代碼
而後我爲了方便定義的項目 Model 裏面包含了接口 Model。
也就是我只需把 接口 Id,和 項目 Id 傳給後臺,讓後臺作一個查詢接口內容,而後新建接口把查詢到的內容插入到指定 Id 就能夠了。
這很簡單。主要部分是 UI 這塊,不過經過對數據流的管理也是花時間就能解決的事情。以下圖:
以後是克隆項目這塊, 咱們首先已經知道項目 Model 裏面包含了 接口 Model,所以咱們克隆整個項目實際上是須要將整個接口提取出來,團隊成員是須要剔除的,由於新克隆項目應該是隻有建立者,所以咱們須要把 用戶 Id 也從前端傳過去,固然也可不傳,經過 Jwt 對 token 解析也能識別用戶信息。
主要是後端拿到信息以後它的思路應該是先查詢這個項目的信息,而後提取部分信息建立新項目, 而後遍歷原有項目的接口列表,批量建立接口。
基本上中後臺的應用都會有我的信息管理這項,有的用表單,有的拆分。
其他數據都好搞定,無非是傳參的問題,先後端約定的問題。 固然比較麻煩的實際上是頭像的更改。 假設你註冊的時候默認分配給一個用戶頭像,而後再我的信息裏用戶想要更改。 這時候問題來了。更改頭像實際上是一個交互問題,你確定不能讓用戶一步步操做。而是一步到位,符合要求的圖片上傳以後,拿到上傳後的圖片地址。而後更改本地的數據。還須要在後臺自動更新數據。
這裏是用 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())
})
});
複製代碼
還有一些功能須要等後端開發的時候再記錄思路,所以這部分先到這裏。