|
@@ -0,0 +1,228 @@
|
|
|
+<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>
|