Vue 利用後端的數據字典和Map對象實現表格列欄位動態轉義的處理方案

1、前言

  Vue中,使用el-table組件,經常遇到列欄位轉義的問題。常規處理方法有以下兩種:

  • 方法1:在模板中使用v-if,直接轉義。如:
          <el-table-column label="是否學員" prop="isStudent" min-width="7%">
            <template slot-scope="scope">
               <span v-if="scope.row.participantType == 0">N</span>
               <span v-if="scope.row.participantType == 1">Y</span>
            </template>            
          </el-table-column>
  • 方法2:使用formatter,進行轉義處理,如:

  在模板中指明使用格式轉換器:

<el-table-column label="證件類型" prop="idType" :formatter="idFormatter" min-width="10%"></el-table-column>

  在Javascript中,實現指定的格式轉換器:

  data() {
    return {
      // 證件類型列表
      idTypeList: [
          {idType:1,idTypeName:"身份證"},
          {idType:2,idTypeName:"社保卡"},
          {idType:3,idTypeName:"駕駛證"},
          {idType:4,idTypeName:"護照"},
          {idType:5,idTypeName:"臨時身份證"},
          {idType:6,idTypeName:"工作證"}
      ],
	  
	  //其它屬性
	  //...
	}
  },
  methods: {
    // 證件類型欄位翻譯
    idFormatter(row, column) {
      var value = "";
      for (var i = 0; i < this.idTypeList.length; i++){
        var item = idTypeList[i];
        if (row.idType == item.idType) {
          value = item.idTypeName;
          break;
        }
      }
      return value;
    },
  }

  這兩種處理方法都有效,但感覺不是很好。

  • 方法1的問題,是需要枚舉各種可能性,如果枚舉項很多,程式碼固化,書寫是個體力活,且程式碼很不簡潔。另外,靈活性不高,如果後端對該欄位增加枚舉項,前端也需要修改。

  • 方法2的問題,如果需要欄位轉義的列較多時,需要定義較多的格式轉換器方法。

  因此,推薦使用下面的方案。

2、動態欄位轉義處理方案

2.1、後端使用系統參數表並提供查詢介面

  首先,後端對欄位的枚舉類型,均使用系統參數表來存儲,這樣,前後端統一使用同一份數據字典。參見之前的文章:《使用系統參數表,提升系統的靈活性 》。

  然後,後端提供相應的介面,供前端獲取指定類別的參數項(枚舉項)列表。介面定義如下:

Path: /sysParam/getParameterClass
Method: POST

介面描述:
請求參數:
Headers
參數名稱		參數值		  是否必須	示例	備註
Content-Type	application/json  是		
Authorization	token		  是			token值
Body
名稱		類型		  是否必須	默認值		備註			其他資訊
classKey	string		  必須				參數類別key	

返回數據:
名稱	     類型	         是否必須    默認值  備註	    其他資訊
data	     object []	         非必須		    返回數據	
item   類型: object
├─ SysParameter類型 各欄位,略
code	integer	                 必須		    返回碼	
message	string	                 必須		    提示資訊	
additional	object	        非必須		    附加資訊,Additional類型,略

2.2、前端獲取系統參數的常規方法

  頁面中獲取系統參數的常規處理方法,如下:

  data() {
    return {
      // 證件類型列表
      idTypeList: [],
	  
	  //其它屬性
	  //...
	}
  },
  created() {
    this.getIdTypeList();
  },
  methods: {
    // 證件類型欄位翻譯
    idFormatter(row, column) {
      var value = "";
      for (var i = 0; i < this.idTypeList.length; i++){
        var item = idTypeList[i];
        if (row.idType == item.idType) {
          value = item.idTypeName;
          break;
        }
      }
      return value;
    },

    // 獲取證件類型列表數據
    getIdTypeList() {
      let _this = this;
      this.instance.getParameterClass(this.$baseUrl,{"classKey":"id_type"}).then((response) => {
        _this.idTypeList = response.data.data;
      });
    },
  }

  api/index.js中定義instance的介面:

  //獲取類別資訊列表
  getParameterClass (baseurl, data) {
    var url = baseurl + '/sysParam/getParameterClass';
    return instance.post(url, data);
  },

  現在的問題,如果獲取每個參數類型,都要用一個方法來實現,顯得太繁瑣,程式碼不優雅。另外,列欄位轉義還是使用了格式轉換器,因為列表數據只能使用遍歷。

2.3、前端開發公共方法來獲取系統參數

  現在的方案,欄位轉義的數據字典由後端定義,這樣一來,前端各個頁面將會大量調用獲取系統參數的介面。因此有必要開發公共方法來獲取系統參數。

  參數類別的數據,頁面需要兩種類型的數據:

  • 列表類型,用於選擇框,如查詢條件,此時往往需要在列表中增加一項類似「全部類型」的選項,表示忽略此條件。
  • 字典類型,用於表格列欄位轉義。

  在/src/common/commonFuncs.js中,實現獲取系統參數的方法,程式碼如下:

  /**
   * 獲取參數類別資訊列表及字典
   * @param {容器對象} parent 
   * @param {參數類別key} classKey 
   * @param {列表的屬性名} listObjPropName 
   * @param {字典的屬性名} mapObjPropName 
   * @param {欄位數據類型} fieldDatatype 
   */
  getParameterClass(parent, classKey, listObjPropName, mapObjPropName, fieldDatatype="int"){
    parent.instance.getParameterClass(
      parent.$baseUrl, {"classKey" : classKey}
    ).then(res => {
      //console.log(res.data);
      if (res.data.code == parent.global.SucessRequstCode){
        //如果查詢成功
        //console.log(res.data.data);
        if (listObjPropName != undefined && listObjPropName != ""){
          //需要輸出列表數據
          for(var i = 0; i < res.data.data.length; i++){
            var item = res.data.data[i];
            //往後添加數據,不破壞列表原有數據
            parent[listObjPropName].push(item);
          }          
        }
        if(mapObjPropName != undefined && mapObjPropName != ""){
          //需要輸出字典數據
          //字典的key要匹配欄位類型,由於itemKey為類型為字元串,而欄位數據類型一般為整型(枚舉值)
          //可能需要進行類型轉換
          //遍歷列表數據
          for(var i = 0; i < res.data.data.length; i++){
            var item = res.data.data[i];
            var mapKey;
            if (fieldDatatype == "int"){
              //字元串轉int
              mapKey = parseInt(item.itemKey);
            }else{
              mapKey =item.itemKey;
            }
            //加入字典
            parent[mapObjPropName].set(mapKey,item);
          }
        }
      }else{
        alert(res.data.message);
      }
    }).catch(error => {
      alert('查詢系統參數失敗!');            
      console.log(error);
    });
  }

2.4、Vue文件中獲取系統參數的用法

  樣例Vue文件,模板程式碼如下:

<template>
  <div id="contentwrapper">
    <el-form ref="form" :model="formData" label-width="80px">
      <el-card>
        <el-row>
          <!--占整行-->
          <el-col :span="24">  
            <h5 class="heading" align=left>用戶管理 / 用戶管理</h5>
            <!-- 分隔線 -->
            <el-divider></el-divider>
          </el-col>              
        </el-row>
        <el-row>
          <el-col align="left" :span="6">
            <el-button type="primary" size="small" @click="addUser">
              <i class="el-icon-circle-plus"></i>添加用戶
            </el-button>
          </el-col>

          <!-- 查詢條件 -->
          <el-col align="left" :span="12">
            <el-form-item label="用戶類型:" label-width="100px">
              <el-select v-model="formData.userTypeLabel" size="small" @change="selectUserType">
                <el-option
                    v-for="(item,index) in userTypeList"
                    :key="index"
                    :label="item.itemValue"
                    :value="item"
                />
              </el-select>                  
            </el-form-item> 
          </el-col> 
          <el-col align="right" :span="6">
            <el-button type="primary" size="small" @click="queryUsers">
              <i class="el-icon-search"></i>查詢
            </el-button>
          </el-col>                
        </el-row>

        <!-- 用戶列表數據 -->
        <el-table :data="userInfoList" border stripe :row-style="{height:'30px'}" :cell-style="{padding:'0px'}" style="font-size: 10px">
          <el-table-column label="用戶ID" prop="userId"></el-table-column>
          <el-table-column label="用戶類型" width="80px" prop="userType">
            <template slot-scope="scope">
               <span>{{userTypeMap.get(scope.row.userType).itemValue}}</span>
            </template>           
          </el-table-column>
          <el-table-column label="登錄名" prop="loginName"></el-table-column>
          <el-table-column label="真實名稱" prop="userName"></el-table-column>
          <el-table-column label="手機號碼" prop="phoneNumber" width="80px"></el-table-column>
          <el-table-column label="EMail" prop="email" width="80px"></el-table-column>
          <el-table-column label="操作" width="60px">
              <template slot-scope="scope">
                <el-tooltip class="item" effect="dark" content="編輯" placement="left-start">
                  <el-button size="mini" type="primary" icon="el-icon-edit" circle @click="editUser(scope.row)"></el-button>
                </el-tooltip>                             
              </template>
          </el-table-column>
        </el-table>
      </el-card> 
    </el-form>    
  </div>    
</template>

  模板程式碼中,有一個用戶類型的選擇框,還有表格中對用戶類型數據列進行轉義處理。注意數據列轉義處理的處理程式碼:

          <el-table-column label="用戶類型" width="80px" prop="userType">
            <template slot-scope="scope">
               <span>{{userTypeMap.get(scope.row.userType).itemValue}}</span>
            </template>           
          </el-table-column>

  這個程式碼相當簡潔。

  下面是javascript中與系統參數獲取與設置相關的程式碼:

  data() {
    return {
      formData : {
        //查詢資訊
        queryInfo:{
          userType  : 0,
          deleteFlag: 0,
          pagenum   : 1,
          pagesize  : 10      
        },
          
        //用戶類型選擇框當前選擇項的顯示值
        userTypeLabel : "所有類型"
      },

      //用戶類型參照表,構造初始數據項
      userTypeList  : [
        {
          itemKey   : "0",
          itemValue : "所有類型"
        }
      ],

      //用戶類型字典
      userTypeMap : new Map(),

      //查詢到的用戶資訊列表
      userInfoList:[],

      //新增編輯對話框可見標記
      editVisible:false,

      show:false
    }
  },
  created() {
    // ==========================================
    // 獲取需要的系統參數,注意:getParameterClass方法是非同步載入數據的。
    // 如需要列印觀察,需要通過watch來處理

    // 獲取用戶類型的參數類別
    this.commonFuncs.getParameterClass(this,"user_type","userTypeList","userTypeMap");
  },
  watch:  {
    userTypeList  : {
      handler(newValue, oldValue){
        //獲取數據後,設置選擇框的初始值;
        this.$set(this.formData,'userTypeLabel',this.userTypeList[0].itemValue);
      },
      immediate: true
    },
    userTypeMap  : {
      handler(newValue, oldValue){
        console.log(newValue);
      },
      immediate: true
    }    
  }, 
  methods: {
    //查詢用戶資訊列表
    queryUsers(){
      let _this = this;
      this.instance.queryUsers(
        this.$baseUrl,this.formData.queryInfo
      ).then(res => {
        console.log(res.data);
        if (res.data.code == this.global.SucessRequstCode){
          //如果查詢成功
          _this.formData.pageInfo.total = res.data.data.length;
          _this.userInfoList = res.data.data;
        }else{
          alert(res.data.message);
        }
      }).catch(error => {
        alert('查詢失敗!');            
        console.log(error);
      });
    },

    //用戶類型選擇
    selectUserType(item){
      console.log(item);
      this.$set(this.formData.queryInfo,'userType',parseInt(item.itemKey));
      this.$set(this.formData,'userTypeLabel',item.itemValue);
    },
  }

  注意事項:

  1. 由於數據是動態獲取的,但vue 無法監聽動態新增的屬性的變化,需要用 $set 來為這些屬性賦值。否則選擇框的選擇選項後,當前值的顯示不會改變。

        //用戶類型選擇
        selectUserType(item){
          console.log(item);
          this.$set(this.formData.queryInfo,'userType',parseInt(item.itemKey));
          this.$set(this.formData,'userTypeLabel',item.itemValue);
        },
    
  2. 為了使得選擇框的選擇能夠生效,

    <el-form ref="form" :model="formData" label-width="80px">
    

    的model設置必須包含選擇框的當前選擇項的顯示值,即:

                <el-form-item label="用戶類型:" label-width="100px">
                  <el-select v-model="formData.userTypeLabel" size="small" @change="selectUserType">
                    <el-option
                        v-for="(item,index) in userTypeList"
                        :key="index"
                        :label="item.itemValue"
                        :value="item"
                    />
                  </el-select>                  
                </el-form-item> 
    

    el-select的v-model即userTypeLabel必須在form的model中,也就是說formData必須包含userTypeLabel。

  3. 在data部分,定義了用戶類型的列表和字典對象。其中用戶類型列表用於選擇框,字典用於表格數據列欄位轉義。其中,用戶類型列表設置了初始項,表示全部類型。(也可以約定由後端的系統參數表來統一定義,這樣前端無需設置初始項)。

          //用戶類型參照表,構造初始數據項
          userTypeList  : [
            {
              itemKey   : "0",
              itemValue : "所有類型"
            }
          ],
    
          //用戶類型字典
          userTypeMap : new Map(),
    
  4. 系統參數的獲取方法,一般在頁面載入時獲取:

      created() {
        // ==========================================
        // 獲取需要的系統參數,注意:getParameterClass方法是非同步載入數據的。
        // 如需要列印觀察,需要通過watch來處理
    
        // 獲取用戶類型的參數類別
        this.commonFuncs.getParameterClass(this,"user_type","userTypeList","userTypeMap");
      },
    

    調用公共方法getParameterClass,可以一次性獲取某個參數類別的列表和字典數據,允許獲取某一種類型數據,只需將參數設為空字元串即可。

    列表和字典的參數值,必須在data中聲明的屬性名,並且類型要匹配。

    從程式碼量看,獲取系統參數的調用是相當簡潔的。

  5. 在系統參數獲取成功後的處理,一般在watch中實現。

      watch:  {
        userTypeList  : {
          handler(newValue, oldValue){
            //獲取數據後,設置選擇框的初始值;
            this.$set(this.formData,'userTypeLabel',this.userTypeList[0].itemValue);
          },
          immediate: true
        },  
      }, 
    

    監視到userTypeList數據載入完畢後,設置用戶類型選擇框的初始選擇項。

2.5、效果圖

  運行Vue,在瀏覽器輸入相應url,頁面顯示如下:

  可以看到列表中用戶類型數據列已經轉義顯示。

2.6、其它

  如果數據字典不是由後台提供,而是前端固化,則只需在data中聲明userTypeMap為字典類型,然後在created事件中,完成初始化即可。

  這種情況下,數據列轉義仍然有效。

Tags: