基於iview-封裝定製化的穿梭框組件
PM那邊需求如下:
** 效果圖如下:**
代碼如下:
<template>
<!-- 穿梭組件 -->
<div class="transferTpl">
<div class="left">
<div class="title">
<span>{{ leftTitle }}</span>
<span>({{ checked }}{{ checked != 0 ? '/' + total : '' }})</span>
</div>
<div class="content">
<Tabs size="small">
<TabPane v-for="(item, index) in chooseList"
:key="item.title"
:label="item.title"
class="repair-cpu">
<Checkbox
:indeterminate="item.indeterminate"
:value="item.checkAll"
:disabled="item.checkAllDisabled"
@click.prevent.native="handleCheckAll(index)">全選</Checkbox>
<CheckboxGroup v-model="item.checkAllGroup" @on-change="checkAllGroupChange(index)">
<Checkbox v-for="(i, j) in item.dataList"
:key="j"
:disabled="i.disabled"
:label="i.name"></Checkbox>
</CheckboxGroup>
</TabPane>
</Tabs>
</div>
<div class="footer" v-if="isShowDesc">
<span>{{ descTitle }}</span>
<Input v-model="desc" :placeholder="descPlace" :disabled="descDisabled" class="desc" />
</div>
</div>
<div class="middle">
<Button class="move"
size="small"
:disabled="disabled"
@click="handleTransfer"
icon="ios-arrow-forward"></Button>
</div>
<div class="right">
<div class="title">
<span>{{ rightTitle }} ({{ alreadyChoose }})</span>
<Button v-if="alreadyChoose !== 0" type="text" @click="delAll" style="float: right; background: #f5f5f5;">清空</Button>
</div>
<div class="content">
<div class="header">
<span>{{ rightName }}</span>
<span>{{ rightDescName }}</span>
</div>
<div class="collapse">
<Collapse simple accordion value="cpu">
<Panel v-for="(item, index) in selectedList" :key="index" :name="item.title">
{{ item.title }}({{ item.dataList.length }})
<Icon type="md-close" @click.stop="delItem(item)" class="item-total-icon" style="line-height: 36px; margin-right: 10px"></Icon>
<div slot="content">
<div class="over-blur" v-for="(i, j) in item.dataList">
<span>{{ i.name }}</span>
<span>{{ i.desc }}</span>
<Icon @click="delnum(i)" type="md-close" class="over-blur-icon"></Icon>
</div>
</div>
</Panel>
</Collapse>
</div>
</div>
</div>
</div>
</template>
<script>
var _lodash = require('lodash');
export default {
name: "transferTpl",
props: [
'chooseDdataList', // 待選項 Array
'leftTitle', // 左側標題
'isShowDesc', // 是否顯示描述
'descTitle', // 描述標題
'rightTitle', // 右側標題
'rightName', // 右側 item name
'rightDescName' // 右側 item-desc name
],
data() {
return {
chooseList: this.chooseDdataList,
disabled: true, // 按鈕禁用狀態
desc: '', // 描述
descPlace: '', // 描述placehoder
descDisabled: true, // 描述禁用狀態
selectedList: [], // 已選維修部件 已選項
// selectedList模板
// selectedList: [
// {
// title: 'CPU',
// dataList: [
// {
// name: '',
// desc: ''
// }
// ]
// }
// ]
}
},
watch: {
// 監聽已選項
'chooseList': {
handler(val) {
let arr = [];
val.forEach(item => {
// 判斷是否只包含禁用
if (item.checkAllGroup.length) {
item.dataList.forEach(i => {
item.checkAllGroup.forEach(j => {
// 只有當按鈕狀態爲可選時纔可輸入描述
if (j === i.name && !i.disabled) {
arr.push(true);
}
})
})
} else {
arr.push(false);
item.indeterminate = false;
item.checkAll = false;
}
// 修改全選禁用狀態
let disabledList = [];
item.dataList.forEach(item => {
disabledList.push(item.disabled);
})
if (disabledList.includes(true) && new Set(disabledList).size === 1) {
item.checkAllDisabled = true;
} else {
item.checkAllDisabled = false;
}
})
// 修改描述input禁用狀態
if (arr.includes(true)) {
this.descDisabled = false;
} else {
this.descDisabled = true;
}
},
deep: true
},
// 監聽描述信息 - 修改穿梭框按鈕禁用狀態
'desc': {
handler(val) {
if (val !== '') {
this.disabled = false;
} else {
this.disabled = true;
}
}
}
},
computed: {
// 已選項
checked() {
return (this.handleNum(this.chooseList, 'checkAllGroup') || []).length;
},
// 待選項總數
total() {
return (this.handleNum(this.chooseList, 'dataList') || []).length;
},
// 右側已選
alreadyChoose() {
return (this.handleNum(this.selectedList, 'dataList') || []).length;
}
},
create() {
},
methods: {
// 穿梭按鈕事件,渲染右側列表
handleTransfer() {
// 處理已選項 先遍歷已選擇數據
this.chooseList.forEach(obj => {
// 如果存在已勾選的item
(obj.checkAllGroup || []).forEach(item => {
// 判斷右側是否已存在當前類型
if (this.selectedList.length) {
let titleList = [];
this.selectedList.map(num => {
// 若存在
if (num.title === obj.title) {
let list = [];
num.dataList.forEach(n => {
list.push(n.name);
})
if (!list.includes(item)) {
let objTpl = {
name: item,
desc: this.desc
}
num.dataList.push(objTpl);
// 複選框禁用
obj.dataList.forEach(m => {
if (m.name === item) {
m.disabled = true;
}
})
}
}
titleList.push(num.title);
})
// 若右側沒有當前項
if (!titleList.includes(obj.title)) {
let objTpl = {
title: obj.title,
dataList: []
}
let snapObj = {
name: item,
desc: this.desc
}
objTpl.dataList.push(snapObj)
this.selectedList.push(objTpl);
obj.dataList.forEach(m => {
if (m.name === item) {
m.disabled = true;
}
})
}
} else {
//若不存在
let objTpl = {
title: obj.title,
dataList: [
{
name: item,
desc: this.desc
}
]
}
this.selectedList.push(objTpl);
obj.dataList.forEach(m => {
if (m.name === item) {
m.disabled = true;
}
})
}
})
});
this.desc = '';
this.descDisabled = true;
},
// 全選check
handleCheckAll (key) {
if (this.chooseList[key].indeterminate) {
this.chooseList[key].checkAll = false;
} else {
this.chooseList[key].checkAll = !this.chooseList[key].checkAll;
}
this.chooseList[key].indeterminate = false;
if (this.chooseList[key].checkAll) {
let list = [];
this.chooseList[key].dataList.forEach(item => {
list.push(item.name);
})
this.chooseList[key].checkAllGroup = list;
} else {
this.chooseList[key].checkAllGroup = [];
}
},
// check某一項
checkAllGroupChange (key) {
let data = this.chooseList[key].checkAllGroup;
if (data.length === this.chooseList[key].dataList.length) {
this.chooseList[key].indeterminate = false;
this.chooseList[key].checkAll = true;
} else if (data.length > 0) {
this.chooseList[key].indeterminate = true;
this.chooseList[key].checkAll = false;
} else {
this.chooseList[key].indeterminate = false;
this.chooseList[key].checkAll = false;
}
},
// 刪除全部
delAll() {
this.selectedList = [];
this.chooseList.forEach(item => {
item.dataList.forEach(obj => {
obj.disabled = false;
})
item.checkAllGroup = [];
item.indeterminate = false;
})
},
// 刪除某一項
delItem(item) {
this.selectedList.forEach((obj, index) => {
if (item.title === obj.title) {
this.selectedList.splice(index, 1);
}
this.chooseList.forEach(n => {
if (n.title === item.title) {
n.dataList.forEach(m => {
m.disabled = false;
})
n.checkAllGroup = [];
n.indeterminate = false;
}
})
})
},
// 刪除某一個
delnum(val) {
// 刪除已選項
this.selectedList.forEach((item, k) => {
item.dataList.forEach((obj, index) => {
if (obj.name === val.name) {
item.dataList.splice(index, 1);
if (item.dataList.length === 0) {
this.selectedList.splice(k, 1);
}
}
})
})
// 重置待選項
this.chooseList.forEach(item => {
item.dataList.forEach((obj, index) => {
if (obj.name === val.name) {
obj.disabled = false;
}
})
item.checkAllGroup.forEach((obj, index) => {
if (obj === val.name) {
item.checkAllGroup.splice(index, 1); // 取消選中
item.checkAll = false; // 取消全選狀態
item.indeterminate = true;
}
})
})
},
// 處理 已選,待選,總數
handleNum(data, key) {
let list = [];
data.forEach(item => {
item[key].forEach(obj => {
list.push(obj);
})
})
return list;
}
}
}
</script>
<style lang="less">
.transferTpl {
width: 100%;
display: flex;
.left,.right {
width: 418px;
border: 1px solid #eaeaea;
.title {
width: 100%;
height: 30px;
line-height: 30px;
padding-left: 5px;
background: #f5f5f5;
span:nth-child(1) {
font-weight: 700;
}
}
.content {
padding: 5px;
height: 300px;
/* 選擇維修部件 */
.repair-cpu {
height: 234px;
overflow-y: auto;
padding: 5px 5px 0 5px;
.ivu-checkbox-wrapper {
width: 45%;
span:nth-child(2) {
display: inline-block;
width: 85%;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
vertical-align: middle;
}
}
}
/* 已選維修部件 */
.header {
height: 30px;
line-height: 30px;
border-bottom: 1px solid #eee;
span {
font-size: 12px;
display: inline-block;
}
span:first-child {
width: 100px;
margin-left: 36px;
display: inline-block;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
span:last-child {
width: 200px;
margin-left: 20px;
display: inline-block;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
.collapse {
height: 260px;
overflow-y: auto;
overflow-x: hidden;
.over-blur {
line-height: 24px;
span {
font-size: 12px;
display: inline-block;
}
span:first-child {
width: 100px;
margin-left: 10px;
display: inline-block;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
span:nth-child(2) {
width: 180px;
margin-left: 20px;
display: inline-block;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.over-blur-icon {
float: right;
display: inline-block;
cursor: pointer;
font-size: 14px;
}
}
}
.item-total-icon {
color: #999;
font-size: 18px;
cursor: pointer;
float: right;
line-height: 38px;
}
.ivu-collapse-simple {
border: none;
}
.ivu-collapse > .ivu-collapse-item {
border: none;
}
.ivu-collapse > .ivu-collapse-item > .ivu-collapse-header {
padding-left: 10px;
i {
margin-right: 0;
line-height: 21px;
}
}
}
.footer {
width: 100%;
height: 30px;
line-height: 30px;
padding-left: 5px;
background: #f5f5f5;
.desc {
width: 280px;
.ivu-input-inner-container input {
height: 24px;
}
}
}
}
.middle {
width: 100px;
display: flex;
align-items:center;
justify-content: center;
.move {
border: 1px solid #2b85e4;
background: #fff;
width: 60px;
height: 30px;
}
.move:hover {
background: #e7edf3;
}
.move:active {
background: #c6d8ea;
}
.move[disabled] {
background-color: #cccccc;
border: 1px solid #cccccc;
}
}
.right {
width: 418px;
border: 1px solid #eaeaea;
}
}
</style>