寫在開頭:本次項目采用的是vue2.0+iview3.0,最近公司沒啥事,來總結一下開發過程中遇到的問題。
1、Modal關閉問題
需求背景:modal框里面是個form表單,點擊確定之后,先驗證form表單,驗證通過則關閉modal框,驗證不成功則提示用戶,不關閉。
問題描述:本來剛開始想通過modal框v-model綁定的值(true或false)來進行控制,手動改之后,報錯。
解決辦法:
官方iview的modal組件的api里面有個loading屬性,可通過控制loading的值來進行控制modal的顯示。

舉例說明:


注意: refuseLoading剛開始一定要設置為true。

這樣的話就可以解決問題了。
衍生出來的問題:當關閉模態框之后,再次打開時,表單數據沒有重置,還是上一次的數據。
解決辦法:this.$refs[name].resetFields(); 重置表單數據,在關閉模態框的時候調用這個方法可解決。
2、同時驗證多個表單問題
需求背景:一個頁面有多個表單,提交的時候需要驗證多個表單,都驗證成功才能進行下一步操作
解決辦法:用一個數組來存放每個表單的驗證結果(true驗證通過,false驗證不通過),最后循環這個數組如果值都為true,說明驗證通過。
舉例說明:頁面有3個表單,需要同時驗證,主要代碼如下:
<template>
<div class="hello">
<Form ref="formValidate1" :model="formValidate1" :rules="ruleValidate">
<FormItem label="Name" prop="name">
<Input v-model="formValidate1.name" placeholder="Enter your name"></Input>
</FormItem>
</Form>
<Form ref="formValidate2" :model="formValidate2" :rules="ruleValidate">
<FormItem label="Name" prop="name">
<Input v-model="formValidate2.name" placeholder="Enter your name"></Input>
</FormItem>
</Form>
<Form ref="formValidate3" :model="formValidate3" :rules="ruleValidate">
<FormItem label="Name" prop="name">
<Input v-model="formValidate3.name" placeholder="Enter your name"></Input>
</FormItem>
</Form>
<Button @click="submit">提交</Button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data() {
return {
formValidate1: {
name: ''
},
formValidate2: {
name: ''
},
formValidate3: {
name: ''
},
ruleValidate: {
name: [
{ required: true, message: 'The name cannot be empty', trigger: 'blur' }
]
},
arr: []
}
},
methods: {
check(name){ // 驗證表單是否通過
this.$refs[name].validate((valid) => {
if(valid) {
this.arr.push(true); // arr 這個數組是用來存放單個表單的驗證狀態
} else {
this.arr.push(false);
}
})
},
submit(){ // 提交
this.arr = []; // 重置數組
// 同時驗證多個表單
this.check('formValidate1');
this.check('formValidate2');
this.check('formValidate3');
var flag = null;
for(var i = 0; i < this.arr.length; i++) {
if(this.arr[i]) { // 單個表單驗證通過,繼續驗證下個表單
flag = true;
continue;
} else { // 單個表單驗證不通過,結束
flag = false;
break;
}
}
if(flag){ // 驗證表單成功
alert("驗證成功");
}else{
alert("驗證失敗")
}
}
}
}
</script>
<style scoped></style>
3、Select 內的 Option 動態改變時,有時選中值未更新的問題
需求背景:Select的下拉數組是由后台返回的,選中的值也是后台返回的。正確賦值之后,select選中的值未更新。
解決辦法:剛開始一直在不停的調試,有時候可能正確顯示,有時候又不行。這個隨機事件真的。。。。最后查閱官方文檔,好吧,這是官方的坑,更新到iview最新版本后,問題得以解決。
這也給我以后很好的警示,有時候一些異常情況,可以先看哈官方的更新日志,因為我們剛開始做項目的時候,版本只是當時的最新版,一些問題可能官方后面已經修復了,所以應及時更新版本。

4、Table相關問題
(1)render函數的運用
參數解讀:
h: vue Render函數的別名(全名 createElement)即 Render函數
params: table 該行內容的對象
props:設置創建的標簽對象的屬性
style:設置創建的標簽對象的樣式
on:為創建的標簽綁定事件
scopedSlots:顯示作用域插槽
a、Switch 開關
{ title: "可控開關", key: "isOpen", align: "center", width: 100, render:(h, params) => { return h('i-switch', { props: { value: params.row.isOpen ? params.row.isOpen : false, // 指定當前是否選中 Boolean類型 (isOpen后端返回字段,根據自己接口返回數據,自行修改) }, scopedSlots:{ open: () => h('span', 'on'), // 自定義顯示打開時的內容 close: () => h('span', 'off') // 自定義顯示關閉時的內容 }, on: { /* * 觸發事件是on-change * 參數value是回調值 Boolean類型 */ 'on-change': (value) => { this.data[params.index].isOpen = value; // 賦值 data:表格數據 } } }) } }
b、Button按鈕
{ title: '操作', key: 'action', width: 150, align: 'center', render: (h, params) => { // 按鈕操作 return h('div', [ h('Button', { props: { type: 'primary', size: 'small' }, style: { // 自定義樣式 marginRight: '5px' }, on: { // 自定義事件 click: () => { this.show(params.index) // params.index是拿到table的行序列,可以取到對應的表格值 } } }, '查看'), h('Button', { props: { type: 'error', size: 'small' }, on: { click: () => { this.remove(params.index) } } }, '刪除') ]); } }
c、Input 輸入框
{ title: "input輸入框", key: "inputText", align: "center", render:(h, params) => { return h('Input', { props: { value: params.row.inputText ? params.row.inputText : '', size: 'small' }, on: { 'on-blur': (event) => { // 輸入框失去焦點時觸發 this.data[params.index].inputText = event.target.value; // 賦值 data:表格數據 } } }); } }
d、Select 下拉框
{ title: 'select下拉框', key: 'selectText', align: 'center', render: (h, params) => { return h('Select',{ props:{ value: params.row.selectText ? params.row.selectText : '', size: 'small' }, on: { 'on-change':(value) => { // 下拉框選定的值 this.data[params.index].selectText = value; } } }, /** * this.selectAction 下拉框Option數組 * selectAction:[ { value: '01', name:'select_1' }, { value: '02', name:'select_2' } ] */ this.selectAction.map((item) =>{ // 下拉選項 return h('Option', { props: { value: item.value, label: item.name } }) }) ) } }
e、Rate評分
{ title: "評分", key: "rate", align: "center", render:(h, params) => { return h('Rate', { props: { value: Number(params.row.rate), // 當前 star 數 Number類型 'allow-half': true, // 可以選中半星 disabled: false // 是否只讀 }, on: { 'on-change': (value) => { // 評分改變時觸發 this.data[params.index].rate = value; // 賦值 data:表格數據 } } }) } }
f、Img圖片
{ title: "頭像", key: "avatar", align: "center", width: 100, render:(h, params) => { return h('Avatar', { // 也可用原生img標簽代替 style: { width: '30px', height: '30px', 'border-radius': '50%' }, attrs: { src: 'https://i.loli.net/2017/08/21/599a521472424.jpg' } }) } }
g、DatePicker時間選擇器
{ title: "時間選擇器", key: "date", align: "center", render:(h, params) => { return h('DatePicker', { props: { value: params.row.date, size: 'small', type: 'datetime' }, on: { 'on-change': (value) => { // 輸入框失去焦點時觸發 this.data[params.index].date = value; // 賦值 data:表格數據 } } }); } }
h、對數據進行處理
比如,后端返回時間是時間戳格式,展示給用戶看的肯定不能是時間戳,這時候就需要我們對數據進行處理
{ title: "申請年份", align: "center", key: "applyDate", render: (h, params) => { return h('span', { }, new Date(params.row.applyDate).getFullYear()) // 對后端返回的時間戳進行處理,返回頁面需要展示的格式 } }
差不多就總結了這幾個,寫多了就發現,是一樣的模板,直接套到render函數里面就是了。想要更多的學習render函數相關的,可以自己前往官網學習。
完整代碼:
<template>
<div class="hello">
<Table border :columns="columns" :data="data"></Table>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data() {
return {
columns: [ // 表格列的配置描述
{
title: "頭像",
key: "avatar",
align: "center",
width: 100,
render:(h, params) => {
return h('Avatar', { // 也可用原生img標簽代替
style: {
width: '30px',
height: '30px',
'border-radius': '50%'
},
attrs: {
src: 'https://i.loli.net/2017/08/21/599a521472424.jpg'
}
})
}
},
{
title: "時間選擇器",
key: "date",
align: "center",
render:(h, params) => {
return h('DatePicker', {
props: {
value: params.row.date,
size: 'small',
type: 'datetime'
},
on: {
'on-change': (value) => { // 輸入框失去焦點時觸發
this.data[params.index].date = value; // 賦值 data:表格數據
}
}
});
}
},
{
title: "input輸入框",
key: "inputText",
align: "center",
render:(h, params) => {
return h('Input', {
props: {
value: params.row.inputText ? params.row.inputText : '',
size: 'small'
},
on: {
'on-blur': (event) => { // 輸入框失去焦點時觸發
this.data[params.index].inputText = event.target.value; // 賦值 data:表格數據
}
}
});
}
},
{
title: 'select下拉框',
key: 'selectText',
align: 'center',
render: (h, params) => {
return h('Select',{
props:{
value: params.row.selectText ? params.row.selectText : '',
size: 'small'
},
on: {
'on-change':(value) => { // 下拉框選定的值
this.data[params.index].selectText = value;
}
}
},
/**
* this.selectAction 下拉框Option數組
* selectAction:[
{
value: '01',
name:'select_1'
},
{
value: '02',
name:'select_2'
}
]
*/
this.selectAction.map((item) =>{ // 下拉選項
return h('Option', {
props: {
value: item.value,
label: item.name
}
})
})
)
}
},
{
title: "申請年份",
align: "center",
key: "applyDate",
render: (h, params) => {
return h('span', {
}, new Date(params.row.applyDate).getFullYear()) // 對后端返回的時間戳進行處理,返回頁面需要展示的格式
}
},
{
title: "可控開關",
key: "isOpen",
align: "center",
width: 100,
render:(h, params) => {
return h('i-switch', {
props: {
value: params.row.isOpen ? params.row.isOpen : false, // 指定當前是否選中 Boolean類型 (isOpen后端返回字段,根據自己接口返回數據,自行修改)
},
scopedSlots:{
open: () => h('span', 'on'), // 自定義顯示打開時的內容
close: () => h('span', 'off') // 自定義顯示關閉時的內容
},
on: {
/*
* 觸發事件是on-change
* 參數value是回調值 Boolean類型
*/
'on-change': (value) => {
this.data[params.index].isOpen = value; // 賦值 data:表格數據
}
}
})
}
},
{
title: "評分",
key: "rate",
align: "center",
render:(h, params) => {
return h('Rate', {
props: {
value: Number(params.row.rate), // 當前 star 數 Number類型
'allow-half': true, // 可以選中半星
disabled: false // 是否只讀
},
on: {
'on-change': (value) => { // 評分改變時觸發
this.data[params.index].rate = value; // 賦值 data:表格數據
}
}
})
}
},
{
title: '操作',
key: 'action',
width: 150,
align: 'center',
render: (h, params) => { // 按鈕操作
return h('div', [
h('Button', {
props: {
type: 'primary',
size: 'small'
},
style: { // 自定義樣式
marginRight: '5px'
},
on: { // 自定義事件
click: () => {
this.show(params.index) // params.index是拿到table的行序列,可以取到對應的表格值
}
}
}, '查看'),
h('Button', {
props: {
type: 'error',
size: 'small'
},
on: {
click: () => {
this.remove(params.index)
}
}
}, '刪除')
]);
}
}
],
data: [ // 表格數據
{
inputText: '18',
isOpen: false,
selectText : '02',
rate: 4,
date: '2019-02-03 00:08:45',
applyDate: 1551835636920
},
{
inputText: '',
isOpen: true,
selectText : '01',
rate: 1.5,
date: '',
applyDate: 1506124800000
}
],
selectAction:[
{
value: '01',
name:'select_1'
},
{
value: '02',
name:'select_2'
}
]
}
},
methods: {
show (index) { // 查看
this.$Modal.info({
title: '查看',
content: '查看詳情'
})
},
remove (index) { // 刪除
this.data.splice(index, 1);
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.hello{
width: 90%;
margin: 0 auto;
padding: 20px 50px 0;
}
</style>
(2)selection的多選來做單選:
通過給 columns 數據設置一項,指定 type: 'selection',即可自動開啟多選功能,但是有些產品覺得iview的單選效果不好,非要用selection的來做單選。以下是解決方案:
{ title: '選擇', align:'center', key: 'checkBox', width: 80, render:(h,params)=>{ return h('div',[ h('Checkbox',{ props:{ value: params.row.checkBox }, on:{ 'on-change':(e)=>{ this.data.forEach((item)=>{ // 先取消所有對象的勾選,checkBox設置為false this.$set(item, 'checkBox', false); }); this.data[params.index].checkBox = e; // 再將勾選的對象的checkBox設置為true } } }) ]) } }
(3)結合Page分頁組件一起使用
一般來講,在表格數據比較多的情況下,會對表格進行分頁展示
<Table border :columns="columns" :data="data"></Table> <Page style="float: right;margin-top:20px" :total="page.total" :current="page.current" :page-size="page.size" @on-change="changePage" @on-page-size-change="changePageSize" show-total show-elevator show-sizer />
分頁一般有2種:前端分頁、后端分頁。前端分頁就是前端一次性拿到所有數據,再對拿到的數據進行分頁展示。后端分頁就是前端一次只拿一頁的數據展示,分頁的時候再次請求后端。
mounted(){ this.changeTableData(); }, methods: { changePage(current){ // 頁碼改變的回調,返回改變后的頁碼 this.page.current = current; this.changeTableData(); }, changePageSize(size){ // 切換每頁條數時的回調,返回切換后的每頁條數 this.page.current = 1; this.page.size = size; this.changeTableData(); }, changeTableData(){ /** * page定義 * page: { total: 0, 總數 current: 1, // 當前頁碼 size: 10 // 每頁個數 } * * * data: 表格展示數據 * tableData: 后端返回的所有數據 * */ // 前端分頁 let _start, _end; let page = this.page; _start = (page.current - 1) * page.size; _end = page.current * page.size; this.data = this.tableData.slice(_start, _end); page.total = this.tableData.length; // 后端分頁 // ajax請求后端接口,重新獲取數據 } }
使用了type=index,分頁之后,索引還是從1開始,如何實現累加呢?
使用render函數可解決:
{ title: '序號', align:'center', type: 'index2', width: 80, render: (h, params) => { return h('span', params.index + (this.page.current - 1) * this.page.size + 1); } }
在翻頁之后,如何記住checkbox多選的選中和未選中狀態?
用一個數組checkData來裝選中的數據,在每次表格數據改變時,通過比對數據來進行回填選中狀態。
<Table border :columns="columns" :data="data" @on-select="changeSelect" @on-select-cancel="changeSelectCancel" @on-select-all="changeSelectAll" @on-select-all-cancel="changeSelectAllCancel"></Table>
methods: { changeSelect(data, val){ // 選中某個數據 /** * checkData 多選框選中的數組 * id 每條數據的唯一標識 */ for(let i in this.checkData){ if(val.id === this.checkData[i].id){ this.checkData.splice(i, 1); break; } } this.checkData.push(val); }, changeSelectCancel(data, val){ // 取消某個數據 for(let i in this.checkData){ if(val.id === this.checkData[i].id){ this.checkData.splice(i, 1); break; } } }, changeSelectAll(data){ // 多選選中 let arr = []; for(let i in data){ let flag = true; for(let y in this.checkData){ if(data[i].id === this.checkData[y].id){ // 已有數據 flag = false; break; }else{ continue; } } if(flag){ // 添加新數據 arr.push(data[i]); } } this.checkData = this.checkData.concat(arr); }, changeSelectAllCancel(data){ // 多選取消 for(let i in this.data){ // 取消當前頁的內容 for(let y in this.checkData){ if(this.data[i].id === this.checkData[y].id){ this.checkData.splice(y, 1); break; } } } }, }, watch:{ 'data':{ handler:function(newValue, oldValue){ this.data = newValue; for(let i in this.data){ let flag = false; for(let y in this.checkData){ if(this.data[i].id === this.checkData[y].id){ flag = true; break; }else{ continue; } } if(flag){ // 回填選中狀態 this.data[i]._checked = true; }else{ this.data[i]._checked = false; } } }, deep:true } }
5、定制iview主題

按照官網,一步一步做的,最后卻報錯:

那是因為less的版本過高,重新卸載,安裝3.0以下的版本(比如2.7.3版本),即可解決問題。
六、合並表格單元行
官網已經提供了表頭分組的api。可是我們想合並單元行列怎么辦呢,雖然官網沒提供api,但是好在我們可以用render函數自定義渲染行列。
1、合並行
{ title: '合並行', align:'center', key: 'content', render: (h, params) => { let a = ['數學','語文'];
return this.renderData(h, a); } } // 下面為渲染方法 renderData(h, a){ let b = []; a.map((val, index) => { b.push(h("p", { style: { height: "40px", lineHeight: "40px", "width": "100%", "display": "block", "padding": "0 10px", borderBottom: a.length !== index + 1 ? "1px solid #e8eaec" : "none" }}, val) ) }) return b; }
2、合並列
{ title: '姓名,昵稱', align:'center', key: 'name', renderHeader: (h, params) => { // 自定義表頭 let a = ['姓名', '昵稱']; return this.renderColumnData(h, a); }, render: (h, params) => { let a = ['張三', '李四']; if(params.row.same){ // 名字姓名相同 a = ['王二麻子']; } return this.renderColumnData(h, a); } }, // 下面為渲染的方法 renderColumnData(h, a){ // 渲染合並列 let b = []; let percent = a.length === 0 ? 100 : (Math.floor(100 / a.length * 100) / 100); a.map((val, index) => { b.push(h("p", { class: a.length !== index + 1 ? "has-border" : "", style: { width: `${percent}%`, padding:'4px 8px', 'display': 'flex', 'align-items': 'center', 'justify-content': 'center' }}, val) ) }) let header = h('div',{ class: 'custom-header' }, b); return header; }
還需添加css樣式
.ivu-table-cell{ padding: 0 !important; width: 100%; } .ivu-table-cell .has-border{ position: relative; } .ivu-table-cell .has-border::after{ content: ''; position: absolute; width: 1px; background-color: #e8eaec; top: 0; bottom: 0; right: 0; } .custom-header{ width: 100%; height: 100%; display: flex; }
需要注意的是 iview 里面的 ivu-table-cell class樣式 沒添加height:100%。但是由於自定義列的時候,右邊框的高度要100%,所以只能根據子元素手動改父節點 ivu-table-cell,添加樣式。
在初始化,和改變表格數據的時候都需執行一次
changeCustomHeight(){ let customArr = document.getElementsByClassName('custom-header'); //custom-header render函數里面自定義的class for(let i in customArr){ if(customArr[i].parentNode){ customArr[i].parentNode.style.height = "100%"; } } },
頁面效果預覽:

說明:此方法主要還是通過改變樣式來實現效果,對於一些特殊的比較復雜的合並行列,可能無法實現。
