ivew後端管理系統的思考③
這是常見的一個後端管理的UI頁面 和傳統的管理系統不一樣的是,這是用vue寫的,完全前後端分離 和之前一樣使用的是iview框架作為開發的基礎
本篇主要是講述之前遺留下來的“①每個單頁面都存在太多必要的資料和方法”的問題 以及對各個元件的設計以及通訊進行記錄
圖上資料來之node上的mockjs生成,介面也是真實請求後響應返回。 解決問題①,其實並不難,vue提供了相應的方法,下面一一道來
目錄
1.坑點 2.重新整理 3.元件設計
坑點: 1.原生xmlhttprequest;物件唄mockjs覆蓋,跨域的時候導致設定withCredentials無效,導致無法攜帶cookie,這個特別坑人,特 別是mockjs是別人匯入自己不知情的情況,這種場景我發生在使用iviewAdmin專案裡面,導致直接浪費一個下午的時間
首先我懷疑的是不是後端在跨域的時候是否有相應獲取cookie的方法,後端在排除後,我開始嘗試前端除錯 但是並沒有結果,於是再次反覆多次的檢視阮老師的CORS的博文,發現我們的配置並沒有和他描述的有差別 我嘗試我用提供模擬資料的nodejs後臺設定cookie和獲取cookie但是發現前端的確沒有攜帶cookie過去 定位到時前端的問題後,我仍舊無法確認問題在哪裡。 後來再次看到這串程式碼的時候
var xhr = new XMLHttpRequest(); xhr.open('GET', 'http://www.xxx.com/api'); xhr.withCredentials = true; xhr.async=false; xhr.onload = onLoadHandler; xhr.send();
突然想到去瀏覽器控制檯列印下XMLHttpRequest,雖然我不是很明確為什麼要這麼做,但是隱約中開始有點懷疑問題是否出現在這個物件上面。
果然.....發現被mockJS替換掉了。
因為我並沒有使用到mockJS來模擬資料,我幾乎把他忽略掉了,同時我並沒有踩過這個坑(坑,要一個個的踩;疼,才不會忘記)warning:“如果發現自己的行為或者程式碼合乎規範,並且多次確認找不到問題所在的時候,就應該橫向拓展”
2.後端請求介面必須使用“queryString”的方式,例如"http://baidu.com?name=xiaohuang&&pwd=123456"
不管是get方法還是Post方式 解決方法
export default (url) =>()=>import(url)
最終確認是自己給自己挖了個大坑,如果直接使用之前的方式:
export default (url) =>()=>import(`@/views/${url}.vue`)
他是沒有問題的,這裡要記住import()裡面的引數不能完全傳參的,必須存在"靜態"的字串例如以上的"@/views"和".vue"
重新整理 關於單個路由重新整理,我琢磨了很久,但是一直找不到滿意的方案。最近接手的這個專案同樣讓我遇到了這個問題。之前一直被它苦惱的原因在於自己沒有拜託傳統iframe重新整理頁面的思想。總是想著如何重置單個vue例項,從新執行生命週期。
最近在學習java基礎,我記得學過Interface這個東西的,一直也沒有找到需要這個東西的場景。 在研究實現重新整理的時候,我認真翻看了vue的文件,把所喲api都一一再次過了一遍。 mixins這個東西再次進入了我的考慮範圍。 認真考慮和實踐後最終確認使用它。 把“重新整理功能”抽離到一個單獨的檔案,每個邏輯單頁都import 它和mixins“重新整理功能的業務程式碼”:
import {routeEqual } from '@/libs/util'
// 重新整理路由
export const Reflesh = {
watch: {
$REFLESHDATA (newV, oldV) {
let sessionRoute=JSON.parse(sessionStorage.getItem("currentRouter"));
console.log("routeEqual(this.$route,sessionRoute",routeEqual(this.$route,sessionRoute))
if(!routeEqual(this.$route,sessionRoute)){
return false;
}
if (this.$Reflesh && typeof this.$Reflesh === 'function') {
console.log('==重新整理==',this.$route)
this.$Reflesh()
} else {
alert(`頁面或元件${this.$options.name}沒有重置資料方法!導致無法重新整理`)
}
}
},
computed: {
$REFLESHDATA () {
return this.$store.state.app.reflesh
}
}
}
這個事情並不是特別值得細細道來的一個事情,但是能解決了我一直都百思不得騎姐的困惱,暫時記錄下,後面還有繼續關於邏輯單頁面太多重複的配置
例如:
資料:
分頁配置資料
表格列篩選後的資料
初始化列表時候傳送給後端的資料
方法:
分頁資料
分頁回撥
表格行選中回撥
重新整理(對了,這裡不得不提一下,重新整理函式被抽離到重新整理頁面了,所有邏輯單頁面,邏輯彈窗頁面都需要mixins它,)
,表格列顯示篩選回撥
search元件提交資料的回撥
search元件依賴父元件提供的初始化資料(computed依賴)
表格自動高度適應
初始化資料
這些都是必須的,而且所有頁面都是一樣的,變得只是資料。可以吧這些都抽離成JAVA的Interface,讓邏輯單頁面去繼承。當然我們這裡用的是vue的mixins字面上是混合的意思,但是它的執行方式和java的Class的執行方法驚人的一致。對於我個人來說,統一為Interface則更好理解,且符合高階語言程式設計規範,何樂不為。
關於modal:
關於modal同樣有上面重複東西太多,需要抽離成InterFace的內容。不過modal更多偏重的是如何與邏輯單頁面的資料互動。 這裡簡要敘述抽離的內容後,更多的是敘述如何與邏輯單頁面的資料通訊以及互動。modal Interface程式碼:
import {Reflesh} from "_inject/reflesh/index";
import {$paramsFilter } from "@/libs/util"
export const ModalInterFace = {
mixins:[Reflesh],
props:{
//承當modal和單頁面的資料通訊以及modal顯示,【如果需要才使用】
tableSelectedRow:{
default:()=>{
return {};
},
type:[Object,Boolean]
}
},
data(){
return {
//將要在modal修改的資料
SelectedRowData:{},
showModel:false,
loading:false,//預設為true,通過表單驗證&發起請求前改為true
}
},
watch:{
//監聽prop變動
tableSelectedRow(newV,oldV){
if(JSON.stringify(newV)=="{}"){
this.SelectedRowData={};
return;
}
this.SelectedRowData=Object.assign({},newV);
this.showModel=true;
}
},
methods: {
//過濾提交引數
/*
obj {object}
...p {string} 要提交的obj的key
*/
$paramsFilter,
//取消回撥
$onCancel(){
this.FormRefName&&this.$refs[this.FormRefName]?this.$refs[this.FormRefName].resetFields():"";
//清空選中列
this.$emit("update:clearTableSelectedRow",true);
this.showModel=false;
this.loading=false;
},
$Reflesh(){
this.FormRefName&&this.$refs[this.FormRefName]?this.$refs[this.FormRefName].resetFields():"";
//清空選中列
this.$emit("update:clearTableSelectedRow",true);
this.showModel=false;
this.loading=false;
}
}
}
modal元件和邏輯單頁面一樣需要“重新整理介面” Reflesh和實現對於的$Reflesh重新整理方法,它執行的程式和下面的$onCancel一樣
$onCancel是Modal 取消或者關閉操作的回撥,它需要執行以下內容:
- 如果包含表單,就重置表單校驗
- 通知邏輯單頁面請求選中的行資料(執行之後,邏輯單頁會通過props把資料傳遞給當前Modal,當前的Modal會清空依賴的資料)
- 關閉當前Modal
- 關閉當前Modal的loading狀態
$paramsFilter是用來過濾提交給伺服器的引數的,它由until.js檔案提供,邏輯單頁面的Interface一樣包含該方法
data:tableSelectedRow是和邏輯單頁面通訊的唯一渠道,在一個頁面有多個Modal依賴"表格行選中的資料(同一資料來源)"的時候,需要分別新建一個物件來儲存選中後的行資料。
modal的分類:
我目前的專案中我遇到的modal種類大概有兩種:
1.依賴邏輯單頁面的資料&&自身校驗表單資料&&自身提交資料&&通知邏輯單頁面重新獲取列表,例如:修改使用者資訊 2.依賴邏輯單頁面資料&&讓邏輯單頁面提交資料,例如:確認彈窗(公用)
第一種的在邏輯單頁面中的使用原始碼:
<role-admin-chnage
:tableSelectedRow="c_tableSelectedRow"
v-on:update:clearTableSelectedRow="clearTableSelectedRow"
v-on:update:searchData="updateSearchDataByModel"
></role-admin-chnage>
第二種在邏輯單頁面中使用的原始碼:
<confirm-modal
:tableSelectedRow="comfirmData"
:msg="confirmMsg"
confirmType="error"
:confirmCallback="confirmCallback"
v-on:update:clearTableSelectedRow="clearTableSelectedRow"
></confirm-modal>
tableSelectedRow: 他們和邏輯單頁面的通訊資料props都名為“tableSelectedRow” 但是第一種依賴的是邏輯單頁面的InterFace提供的c_tableSelectedRow
而第二種則使用的是comfirmData 實際上他們都是表格選中後的行資料,不同之處在於記憶體儲存地址不一樣,這樣就不會導致在同一行裡面的操作按鈕點選後觸發所有Modal的顯示。
第一種同樣可以使用其他類似於第一個comfirmData的資料,只是這裡使用了邏輯單頁面的interface提供的資料而已。
v-on:update:clearTableSelectedRow:
所有的modal都有取消和關閉的操作以及回撥函式,這個由modal的interace提供的,所有modal有觸發的機會同時所有存在modal子元件的邏輯單頁面都必須對應清空選中行資料(修改存放選中行資料的記憶體地址變數的指向)的方法v-on:update:searchData="updateSearchDataByModel":
是第一個modal修改資料後通知父邏輯單頁面去重新拉取資料的事件
第二種modal裡面可能會還會依賴其他資料,例如上面的原始碼就有:
- msg:提示的文字
- confirmType:確認按鈕的樣式(可能是常規的藍色,也可能是危險操作的紅色)
- confirmCallback:因為改確認model本身不具備提交資料的功能,所以它還依賴父單頁面提供的回撥函式,它的引數是由當前modal的作用域this
modal和邏輯單頁面的資料通訊如下圖所示:
注意:
1.我這裡強調的是替換原來儲存選中行資料變數的記憶體地址,而不是重置原有選中行的物件的元素值,如果只是修改元素值不一定可以讓Modal的watch觸發 2.在彈窗出來後,都必須根據傳過來的引數ID重新獲取最新資料後再執行修改(業務)
其他元件通訊: 搜尋元件(圖一①)、時間選擇元件(圖一②)和邏輯單頁面的通訊方式如下圖:
SearchTimeFilter和Search自己控制自己的狀態和重新整理處理,在確認操作後向上傳遞資料,最終由邏輯單頁面提交資料。 重新整理的時候SearchTimeFilter和邏輯單頁面都從Main.vue的provide提供的時間格式方法裡面獲取時間, main.vue的provide的核心原始碼如下:
export default {
name: 'Main',
provide: {
timeFilterMaps: {
today: {
start: formatDate(dateToday(),'yyyy-MM-dd HH:mm'),
end: formatDate(dateTodayLastTime(),'yyyy-MM-dd HH:mm')
},
yestoday: {
start: formatDate(dateYestoday(),'yyyy-MM-dd HH:mm'),
end: formatDate(dateYestodayLastTime(),'yyyy-MM-dd HH:mm')
},
weekend: {
start: formatDate(dateCurrentWeekendFirstDay(),'yyyy-MM-dd HH:mm'),
end: formatDate(dateCurrentWeekendLastTime(),'yyyy-MM-dd HH:mm')
},
prevWeekend: {
start: formatDate(datePrevWeekendFirstDay(),'yyyy-MM-dd HH:mm'),
end: formatDate(datePrevWeekendLastTime(),'yyyy-MM-dd HH:mm')
},
currMonth: {
start: formatDate(getCurrentMonthFirstDay(),'yyyy-MM-dd HH:mm'),
end: formatDate(getCurrentMonthLastTime(),'yyyy-MM-dd HH:mm')
},
prevMonth: {
start: formatDate(prevMonthFirstDay(),'yyyy-MM-dd HH:mm'),
end: formatDate(prevMonthLastTime(),'yyyy-MM-dd HH:mm')
}
}
},
vue的provide類似react的context,當出現子孫元件都需要訪問某個資料,例如主題這樣的東西是可以使用它 本篇結束的時候發現自己寫的特別的亂,但至少把要注意的地方有些了出來,後續可能還會整理一遍