<template>
|
<div class="el-transfer-panel">
|
<p class="el-transfer-panel__header">
|
<el-checkbox
|
v-model="allChecked"
|
@change="handleAllCheckedChange"
|
:indeterminate="isIndeterminate">
|
{{ title }}
|
<span>{{ checkedSummary }}</span>
|
</el-checkbox>
|
</p>
|
|
<div :class="['el-transfer-panel__body', hasFooter ? 'is-with-footer' : '']">
|
<el-input
|
class="el-transfer-panel__filter"
|
v-model="query"
|
size="small"
|
:placeholder="placeholder"
|
@mouseenter.native="inputHover = true"
|
@mouseleave.native="inputHover = false"
|
v-if="filterable">
|
<i slot="prefix"
|
:class="['el-input__icon', 'el-icon-' + inputIcon]"
|
@click="clearQuery"
|
></i>
|
</el-input>
|
<el-checkbox-group
|
v-model="checked"
|
v-show="!hasNoMatch && data.length > 0"
|
:class="{ 'is-filterable': filterable }"
|
class="el-transfer-panel__list">
|
<el-checkbox
|
class="el-transfer-panel__item"
|
:label="item[keyProp]"
|
:disabled="item[disabledProp]"
|
:key="item[keyProp]"
|
v-for="item in filteredData">
|
<option-content :option="item"></option-content>
|
</el-checkbox>
|
</el-checkbox-group>
|
<p
|
class="el-transfer-panel__empty"
|
v-show="hasNoMatch">{{ t('el.transfer.noMatch') }}</p>
|
<p
|
class="el-transfer-panel__empty"
|
v-show="data.length === 0 && !hasNoMatch">{{ t('el.transfer.noData') }}</p>
|
</div>
|
<p class="el-transfer-panel__footer" v-if="hasFooter">
|
<slot></slot>
|
</p>
|
</div>
|
</template>
|
|
<script>
|
import ElCheckboxGroup from 'element-ui/packages/checkbox-group';
|
import ElCheckbox from 'element-ui/packages/checkbox';
|
import ElInput from 'element-ui/packages/input';
|
import Locale from 'element-ui/src/mixins/locale';
|
|
export default {
|
mixins: [Locale],
|
|
name: 'ElTransferPanel',
|
|
componentName: 'ElTransferPanel',
|
|
components: {
|
ElCheckboxGroup,
|
ElCheckbox,
|
ElInput,
|
OptionContent: {
|
props: {
|
option: Object
|
},
|
render(h) {
|
const getParent = vm => {
|
if (vm.$options.componentName === 'ElTransferPanel') {
|
return vm;
|
} else if (vm.$parent) {
|
return getParent(vm.$parent);
|
} else {
|
return vm;
|
}
|
};
|
const panel = getParent(this);
|
const transfer = panel.$parent || panel;
|
return panel.renderContent
|
? panel.renderContent(h, this.option)
|
: transfer.$scopedSlots.default
|
? transfer.$scopedSlots.default({ option: this.option })
|
: <span>{ this.option[panel.labelProp] || this.option[panel.keyProp] }</span>;
|
}
|
}
|
},
|
|
props: {
|
data: {
|
type: Array,
|
default() {
|
return [];
|
}
|
},
|
renderContent: Function,
|
placeholder: String,
|
title: String,
|
filterable: Boolean,
|
format: Object,
|
filterMethod: Function,
|
defaultChecked: Array,
|
props: Object
|
},
|
|
data() {
|
return {
|
checked: [],
|
allChecked: false,
|
query: '',
|
inputHover: false,
|
checkChangeByUser: true
|
};
|
},
|
|
watch: {
|
checked(val, oldVal) {
|
this.updateAllChecked();
|
if (this.checkChangeByUser) {
|
const movedKeys = val.concat(oldVal)
|
.filter(v => val.indexOf(v) === -1 || oldVal.indexOf(v) === -1);
|
this.$emit('checked-change', val, movedKeys);
|
} else {
|
this.$emit('checked-change', val);
|
this.checkChangeByUser = true;
|
}
|
},
|
|
data() {
|
const checked = [];
|
const filteredDataKeys = this.filteredData.map(item => item[this.keyProp]);
|
this.checked.forEach(item => {
|
if (filteredDataKeys.indexOf(item) > -1) {
|
checked.push(item);
|
}
|
});
|
this.checkChangeByUser = false;
|
this.checked = checked;
|
},
|
|
checkableData() {
|
this.updateAllChecked();
|
},
|
|
defaultChecked: {
|
immediate: true,
|
handler(val, oldVal) {
|
if (oldVal && val.length === oldVal.length &&
|
val.every(item => oldVal.indexOf(item) > -1)) return;
|
const checked = [];
|
const checkableDataKeys = this.checkableData.map(item => item[this.keyProp]);
|
val.forEach(item => {
|
if (checkableDataKeys.indexOf(item) > -1) {
|
checked.push(item);
|
}
|
});
|
this.checkChangeByUser = false;
|
this.checked = checked;
|
}
|
}
|
},
|
|
computed: {
|
filteredData() {
|
return this.data.filter(item => {
|
if (typeof this.filterMethod === 'function') {
|
return this.filterMethod(this.query, item);
|
} else {
|
const label = item[this.labelProp] || item[this.keyProp].toString();
|
return label.toLowerCase().indexOf(this.query.toLowerCase()) > -1;
|
}
|
});
|
},
|
|
checkableData() {
|
return this.filteredData.filter(item => !item[this.disabledProp]);
|
},
|
|
checkedSummary() {
|
const checkedLength = this.checked.length;
|
const dataLength = this.data.length;
|
const { noChecked, hasChecked } = this.format;
|
if (noChecked && hasChecked) {
|
return checkedLength > 0
|
? hasChecked.replace(/\${checked}/g, checkedLength).replace(/\${total}/g, dataLength)
|
: noChecked.replace(/\${total}/g, dataLength);
|
} else {
|
return `${ checkedLength }/${ dataLength }`;
|
}
|
},
|
|
isIndeterminate() {
|
const checkedLength = this.checked.length;
|
return checkedLength > 0 && checkedLength < this.checkableData.length;
|
},
|
|
hasNoMatch() {
|
return this.query.length > 0 && this.filteredData.length === 0;
|
},
|
|
inputIcon() {
|
return this.query.length > 0 && this.inputHover
|
? 'circle-close'
|
: 'search';
|
},
|
|
labelProp() {
|
return this.props.label || 'label';
|
},
|
|
keyProp() {
|
return this.props.key || 'key';
|
},
|
|
disabledProp() {
|
return this.props.disabled || 'disabled';
|
},
|
|
hasFooter() {
|
return !!this.$slots.default;
|
}
|
},
|
|
methods: {
|
updateAllChecked() {
|
const checkableDataKeys = this.checkableData.map(item => item[this.keyProp]);
|
this.allChecked = checkableDataKeys.length > 0 &&
|
checkableDataKeys.every(item => this.checked.indexOf(item) > -1);
|
},
|
|
handleAllCheckedChange(value) {
|
this.checked = value
|
? this.checkableData.map(item => item[this.keyProp])
|
: [];
|
},
|
|
clearQuery() {
|
if (this.inputIcon === 'circle-close') {
|
this.query = '';
|
}
|
}
|
}
|
};
|
</script>
|