1. 程式人生 > >ivew後端管理系統的思考③

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方式  解決方法

:URLSearchParams函式包裝後傳遞給介面     * 這個是原生的函式,相容性並不是貼別好,如果是內部使用無礙(強制限制使用谷歌瀏覽器)      * 這個專案是我第一次遇到這種情況,以前多數用json的方式傳送或者formdata的方式,這種處理方法並沒有多大意義,只是後端框架僅僅接受這種方式      * URLSearchParams函式並不會生成實際的queryString,但是後端同樣獲取queryStirng的方法可以獲取到 3.關於import函式  SPA路由經常如此寫component()=>import("../view/page/home.vue")  tab需要非同步元件的時候也是import("../view/page/home.vue")這樣使用,  這次專案需要動態路由的時候踩了個坑 我之前一直是這樣使用的ivew動態路由和許可權也沒有遇到問題,但是這次卻不行,下面這一行程式碼報錯  

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 取消或者關閉操作的回撥,它需要執行以下內容:

  1. 如果包含表單,就重置表單校驗
  2. 通知邏輯單頁面請求選中的行資料(執行之後,邏輯單頁會通過props把資料傳遞給當前Modal,當前的Modal會清空依賴的資料)
  3. 關閉當前Modal
  4. 關閉當前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,當出現子孫元件都需要訪問某個資料,例如主題這樣的東西是可以使用它 本篇結束的時候發現自己寫的特別的亂,但至少把要注意的地方有些了出來,後續可能還會整理一遍