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);
},
}
注意事項:
-
由於資料是動態獲取的,但vue 無法監聽動態新增的屬性的變化,需要用 $set 來為這些屬性賦值。否則選擇框的選擇選項後,當前值的顯示不會改變。
//使用者型別選擇 selectUserType(item){ console.log(item); this.$set(this.formData.queryInfo,'userType',parseInt(item.itemKey)); this.$set(this.formData,'userTypeLabel',item.itemValue); },
-
為了使得選擇框的選擇能夠生效,
<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。
-
在data部分,定義了使用者型別的列表和字典物件。其中使用者型別列表用於選擇框,字典用於表格資料列欄位轉義。其中,使用者型別列表設定了初始項,表示全部型別。(也可以約定由後端的系統參數列來統一定義,這樣前端無需設定初始項)。
//使用者型別參照表,構造初始資料項 userTypeList : [ { itemKey : "0", itemValue : "所有型別" } ], //使用者型別字典 userTypeMap : new Map(),
-
系統引數的獲取方法,一般在頁面載入時獲取:
created() { // ========================================== // 獲取需要的系統引數,注意:getParameterClass方法是非同步載入資料的。 // 如需要列印觀察,需要通過watch來處理 // 獲取使用者型別的引數類別 this.commonFuncs.getParameterClass(this,"user_type","userTypeList","userTypeMap"); },
呼叫公共方法getParameterClass,可以一次性獲取某個引數類別的列表和字典資料,允許獲取某一種型別資料,只需將另一個引數設為空字串即可。
列表和字典的引數值,必須在data中宣告的屬性名,並且型別要匹配。
從程式碼量看,獲取系統引數的呼叫是相當簡潔的。
-
在系統引數獲取成功後的處理,一般在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事件中,完成初始化即可。
這種情況下,資料列轉義仍然有效。