All files / src/components/m-multiplex.vue index.vue

80% Statements 16/20
50% Branches 3/6
66.66% Functions 2/3
78.94% Lines 15/19

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218      7x   7x     7x   7x                                                                                                                                                                                                                                                                                                                                                                                                                              
<template>
<div :class="$style.root" :readonly="readonly" :disabled="disabled">
    <slot></slot>
</div>
</template>
 
<script>
import MEmitter from '../m-emitter.vue';
import { MParent } from '../m-parent.vue';
import MConverter from '../m-converter.vue';
import { isIE } from '../../utils/dom';
 
export default {
    name: 'm-multiplex',
    groupName: 'm-multiplex-group',
    childName: 'm-multiplex-item',
    mixins: [MEmitter, MParent, MConverter],
    props: {
        value: Array,
        keepOrder: { type: Boolean, default: false },
        readonly: { type: Boolean, default: false },
        disabled: { type: Boolean, default: false },
    },
    data() {
        return {
            // @inherit: groupVMs: [],
            // @inherit: itemVMs: [],
            selectedVMs: [],
        };
    },
    watch: {
        value(value, oldValue) {
            let currentValue = value;
            if (this.converter) {
                currentValue = this.currentConverter.set(value);
            }
 
            // 防止空数据也 throw error;
            if (currentValue === undefined || currentValue === null) {
                return;
            }
 
            if (!Array.isArray(currentValue)) {
                throw new Error('`value` should be an Array!'); // @TODO: 因为是同一个数组。。没有好的剪枝方法
            }
            // if (value !== oldValue && value.length === oldValue.length
            //     && value.every((val, index) => val === oldValue[index]))
            //     return;
            this.watchValue(currentValue);
        },
        selectedVMs(selectedVMs, oldVMs) {
            // const oldValue = oldVMs.map((itemVM) => itemVM.value);
            const values = selectedVMs.map((itemVM) => itemVM.value); // @TODO: 因为是同一个数组。。没有好的剪枝方法
            // if (value.length === oldValue.length && value.every((val, index) => val === oldValue[index]))
            //     return;
            let value = values;
            if (this.converter) {
                value = this.currentConverter.get(values);
            }
            const selectedItems = selectedVMs.map((itemVM) => itemVM.item);
            this.$emit('change', {
                value, // @TODO: oldValue,
                values,
                items: selectedItems,
                itemVMs: selectedVMs,
            });
        }, // This method just run once after pushing many itemVMs
        itemVMs() {
            // 更新列表之后,原来的选择可能已不存在,这里需要重新查找一遍
            this.watchValue(this.value);
        },
    },
    mounted() {
        // Don't need trigger `value` watcher at mounted hook.
        // Because there's a watcher for itemVMs.
        // this.watchValue(this.value);
        this.$emit('update', this.value, this);
 
        // fix: IE11下在子组件添加到itemVMs里时,itemVMs的watcher没有执行,需要再添加下watch才执行
        if (isIE()) {
            this.$watch('itemVMs', (value) => {
                this.watchValue(this.value);
            });
        }
    },
    methods: {
        watchValue(value) {
            let selectedVMs = [];
            const selectedMap = {}; // 对于过滤、分页等功能,需要保留原来的 selectedVMs
            this.selectedVMs.forEach((selectedVM) => {
                // 使用了converter="join:|"后,会使数组里的值是string,但value是number,导致没有选中
                // 添加判断string值
                if (value.includes(selectedVM.value) || value.includes('' + selectedVM.value))
                    selectedMap[selectedVM.value] = selectedVM;
            });
            if (value) {
                if (this.converter)
                    value = this.currentConverter.set(value);
                if (!this.keepOrder) {
                    value.forEach((val) => {
                        const itemVM = this.itemVMs.find(
                            (itemVM) => '' + itemVM.value === '' + val,
                        );
                        if (itemVM)
                            selectedMap[itemVM.value] = itemVM;
                        else if (this.selectedValuesData && Array.isArray(this.selectedValuesData)) { // 分页获取数据,下拉里可能还没有这个值,根据用户传入的数据进行展示
                            const itemData = this.selectedValuesData.find(
                                (itemData) => '' + itemData.value === '' + val,
                            );
                            if (itemData)
                                selectedMap[itemData.value] = Object.assign({ currentSelected: true, currentText: itemData.text }, itemData);
                        }
                    });
                } else {
                    this.itemVMs.forEach((itemVM) => {
                        if (value.includes(itemVM.value) || value.includes('' + itemVM.value))
                            selectedMap[itemVM.value] = itemVM;
                    });
                }
                selectedVMs = Object.values(selectedMap); // 必须单独指定一遍,因为有取消掉的
                this.itemVMs.forEach(
                    (itemVM) =>
                        (itemVM.currentSelected = (value.includes(itemVM.value) || value.includes('' + itemVM.value))),
                );
            } else {
                this.itemVMs.forEach(
                    (itemVM) =>
                        itemVM.currentSelected && selectedVMs.push(itemVM),
                );
            }
            this.selectedVMs = selectedVMs;
        },
        watchSelectedChange(selectedVM) {
            if (!this.keepOrder) {
                const index = this.selectedVMs.indexOf(selectedVM);
                if (selectedVM.currentSelected && (!~index || this.duplicated))
                    this.selectedVMs.push(selectedVM);
                else if (!selectedVM.currentSelected && ~index)
                    this.selectedVMs.splice(index, 1);
            } else
                this.selectedVMs = this.itemVMs.filter(
                    (itemVM) => itemVM.currentSelected,
                );
        },
        select(itemVM, selected) {
            // Check if enabled
            if (this.readonly || this.disabled || !itemVM || itemVM.disabled)
                return;
            if (!this.duplicated) {
                // Method overloading
                if (selected === undefined)
                    selected = !itemVM.currentSelected; // Prevent replication
                if (itemVM.currentSelected === selected)
                    return;
            } else {
                if (selected === undefined)
                    selected = true;
            }
            let oldValue = this.value;
            if (this.converter)
                oldValue = this.currentConverter.get(this.value);
            const oldVMs = this.selectedVMs;
            const oldItems = oldVMs.map((itemVM) => itemVM.item); // Emit a `before-` event with preventDefault()
            if (
                this.$emitPrevent(
                    'before-select',
                    {
                        selected,
                        item: itemVM && itemVM.item,
                        itemVM,
                        oldValue,
                        oldItems,
                        oldVMs,
                    },
                    this,
                )
            )
                return; // Assign and sync `selected`
            itemVM.currentSelected = selected;
            if (!itemVM._isVue) { // 由于有selectedValuesData的存在,itemVM可能不是vue实例
                const index = this.selectedVMs.indexOf(itemVM);
                this.selectedVMs.splice(index, 1);
            } else {
                itemVM.$emit('update:selected', selected);
                this.watchSelectedChange(itemVM); // Assign and sync `value`
            }
            const selectedVMs = this.selectedVMs;
            let value = selectedVMs.map((itemVM) => itemVM.value);
            if (this.converter)
                value = this.currentConverter.get(value);
            const selectedItems = selectedVMs.map((itemVM) => itemVM.item);
            this.$emit('input', value, this);
            this.$emit('update', value, this);
            this.$emit('update:value', value, this);
            this.$emit('select', {
                selected: itemVM.currentSelected,
                item: itemVM.item,
                itemVM,
                value,
                oldValue,
                items: selectedItems,
                oldItems,
                itemVMs: selectedVMs,
                oldVMs,
            }, this);
        },
    },
};
</script>
 
<style module>
.root {
    /* @Private */
    user-select: none;
    position: relative;
}
</style>