update the master branch to the latest

This commit is contained in:
梁灏 2019-08-27 09:42:40 +08:00
parent 67d534df27
commit 23a0ba9831
611 changed files with 122648 additions and 0 deletions

View file

@ -0,0 +1,444 @@
<template>
<div :class="classes" v-click-outside="handleClose">
<div :class="[prefixCls + '-rel']" @click="toggleOpen" ref="reference">
<input type="hidden" :name="name" :value="currentValue">
<slot>
<i-input
:element-id="elementId"
ref="input"
:readonly="!filterable"
:disabled="disabled"
:value="displayInputRender"
@on-change="handleInput"
:size="size"
:placeholder="inputPlaceholder"></i-input>
<div
:class="[prefixCls + '-label']"
v-show="filterable && query === ''"
@click="handleFocus">{{ displayRender }}</div>
<Icon type="ios-close-circle" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.native.stop="clearSelect"></Icon>
<Icon :type="arrowType" :custom="customArrowType" :size="arrowSize" :class="[prefixCls + '-arrow']"></Icon>
</slot>
</div>
<transition name="transition-drop">
<Drop
v-show="visible"
:class="{ [prefixCls + '-transfer']: transfer }"
ref="drop"
:data-transfer="transfer"
:transfer="transfer"
v-transfer-dom>
<div>
<Caspanel
v-show="!filterable || (filterable && query === '')"
ref="caspanel"
:prefix-cls="prefixCls"
:data="data"
:disabled="disabled"
:change-on-select="changeOnSelect"
:trigger="trigger"></Caspanel>
<div :class="[prefixCls + '-dropdown']" v-show="filterable && query !== '' && querySelections.length">
<ul :class="[selectPrefixCls + '-dropdown-list']">
<li
:class="[selectPrefixCls + '-item', {
[selectPrefixCls + '-item-disabled']: item.disabled
}]"
v-for="(item, index) in querySelections"
@click="handleSelectItem(index)" v-html="item.display"></li>
</ul>
</div>
<ul v-show="(filterable && query !== '' && !querySelections.length) || !data.length" :class="[prefixCls + '-not-found-tip']"><li>{{ localeNotFoundText }}</li></ul>
</div>
</Drop>
</transition>
</div>
</template>
<script>
import iInput from '../input/input.vue';
import Drop from '../select/dropdown.vue';
import Icon from '../icon/icon.vue';
import Caspanel from './caspanel.vue';
import {directive as clickOutside} from 'v-click-outside-x';
import TransferDom from '../../directives/transfer-dom';
import { oneOf } from '../../utils/assist';
import Emitter from '../../mixins/emitter';
import Locale from '../../mixins/locale';
const prefixCls = 'ivu-cascader';
const selectPrefixCls = 'ivu-select';
export default {
name: 'Cascader',
mixins: [ Emitter, Locale ],
components: { iInput, Drop, Icon, Caspanel },
directives: { clickOutside, TransferDom },
props: {
data: {
type: Array,
default () {
return [];
}
},
value: {
type: Array,
default () {
return [];
}
},
disabled: {
type: Boolean,
default: false
},
clearable: {
type: Boolean,
default: true
},
placeholder: {
type: String
},
size: {
validator (value) {
return oneOf(value, ['small', 'large', 'default']);
},
default () {
return !this.$IVIEW || this.$IVIEW.size === '' ? 'default' : this.$IVIEW.size;
}
},
trigger: {
validator (value) {
return oneOf(value, ['click', 'hover']);
},
default: 'click'
},
changeOnSelect: {
type: Boolean,
default: false
},
renderFormat: {
type: Function,
default (label) {
return label.join(' / ');
}
},
loadData: {
type: Function
},
filterable: {
type: Boolean,
default: false
},
notFoundText: {
type: String
},
transfer: {
type: Boolean,
default () {
return !this.$IVIEW || this.$IVIEW.transfer === '' ? false : this.$IVIEW.transfer;
}
},
name: {
type: String
},
elementId: {
type: String
}
},
data () {
return {
prefixCls: prefixCls,
selectPrefixCls: selectPrefixCls,
visible: false,
selected: [],
tmpSelected: [],
updatingValue: false, // to fix set value in changeOnSelect type
currentValue: this.value,
query: '',
validDataStr: '',
isLoadedChildren: false // #950
};
},
computed: {
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-show-clear`]: this.showCloseIcon,
[`${prefixCls}-size-${this.size}`]: !!this.size,
[`${prefixCls}-visible`]: this.visible,
[`${prefixCls}-disabled`]: this.disabled,
[`${prefixCls}-not-found`]: this.filterable && this.query !== '' && !this.querySelections.length
}
];
},
showCloseIcon () {
return this.currentValue && this.currentValue.length && this.clearable && !this.disabled;
},
displayRender () {
let label = [];
for (let i = 0; i < this.selected.length; i++) {
label.push(this.selected[i].label);
}
return this.renderFormat(label, this.selected);
},
displayInputRender () {
return this.filterable ? '' : this.displayRender;
},
localePlaceholder () {
if (this.placeholder === undefined) {
return this.t('i.select.placeholder');
} else {
return this.placeholder;
}
},
inputPlaceholder () {
return this.filterable && this.currentValue.length ? null : this.localePlaceholder;
},
localeNotFoundText () {
if (this.notFoundText === undefined) {
return this.t('i.select.noMatch');
} else {
return this.notFoundText;
}
},
querySelections () {
let selections = [];
function getSelections (arr, label, value) {
for (let i = 0; i < arr.length; i++) {
let item = arr[i];
item.__label = label ? label + ' / ' + item.label : item.label;
item.__value = value ? value + ',' + item.value : item.value;
if (item.children && item.children.length) {
getSelections(item.children, item.__label, item.__value);
delete item.__label;
delete item.__value;
} else {
selections.push({
label: item.__label,
value: item.__value,
display: item.__label,
item: item,
disabled: !!item.disabled
});
}
}
}
getSelections(this.data);
selections = selections.filter(item => {
return item.label ? item.label.indexOf(this.query) > -1 : false;
}).map(item => {
item.display = item.display.replace(new RegExp(this.query, 'g'), `<span>${this.query}</span>`);
return item;
});
return selections;
},
// 3.4.0, global setting customArrow arrow
arrowType () {
let type = 'ios-arrow-down';
if (this.$IVIEW) {
if (this.$IVIEW.cascader.customArrow) {
type = '';
} else if (this.$IVIEW.cascader.arrow) {
type = this.$IVIEW.cascader.arrow;
}
}
return type;
},
// 3.4.0, global setting
customArrowType () {
let type = '';
if (this.$IVIEW) {
if (this.$IVIEW.cascader.customArrow) {
type = this.$IVIEW.cascader.customArrow;
}
}
return type;
},
// 3.4.0, global setting
arrowSize () {
let size = '';
if (this.$IVIEW) {
if (this.$IVIEW.cascader.arrowSize) {
size = this.$IVIEW.cascader.arrowSize;
}
}
return size;
}
},
methods: {
clearSelect () {
if (this.disabled) return false;
const oldVal = JSON.stringify(this.currentValue);
this.currentValue = this.selected = this.tmpSelected = [];
this.handleClose();
this.emitValue(this.currentValue, oldVal);
// this.$broadcast('on-clear');
this.broadcast('Caspanel', 'on-clear');
},
handleClose () {
this.visible = false;
},
toggleOpen () {
if (this.disabled) return false;
if (this.visible) {
if (!this.filterable) this.handleClose();
} else {
this.onFocus();
}
},
onFocus () {
this.visible = true;
if (!this.currentValue.length) {
this.broadcast('Caspanel', 'on-clear');
}
},
updateResult (result) {
this.tmpSelected = result;
},
updateSelected (init = false, changeOnSelectDataChange = false) {
// #2793 changeOnSelectDataChange used for changeOnSelect when data changed and set value
if (!this.changeOnSelect || init || changeOnSelectDataChange) {
this.broadcast('Caspanel', 'on-find-selected', {
value: this.currentValue
});
}
},
emitValue (val, oldVal) {
if (JSON.stringify(val) !== oldVal) {
this.$emit('on-change', this.currentValue, JSON.parse(JSON.stringify(this.selected)));
this.$nextTick(() => {
this.dispatch('FormItem', 'on-form-change', {
value: this.currentValue,
selected: JSON.parse(JSON.stringify(this.selected))
});
});
}
},
handleInput (event) {
this.query = event.target.value;
},
handleSelectItem (index) {
const item = this.querySelections[index];
if (item.item.disabled) return false;
this.query = '';
this.$refs.input.currentValue = '';
const oldVal = JSON.stringify(this.currentValue);
this.currentValue = item.value.split(',');
// use setTimeout for #4786, can not use nextTick, because @on-find-selected use nextTick
setTimeout(() => {
this.emitValue(this.currentValue, oldVal);
this.handleClose();
}, 0);
},
handleFocus () {
this.$refs.input.focus();
},
// loading data updateSelect
getValidData (data) {
function deleteData (item) {
const new_item = Object.assign({}, item);
if ('loading' in new_item) {
delete new_item.loading;
}
if ('__value' in new_item) {
delete new_item.__value;
}
if ('__label' in new_item) {
delete new_item.__label;
}
if ('children' in new_item && new_item.children.length) {
new_item.children = new_item.children.map(i => deleteData(i));
}
return new_item;
}
return data.map(item => deleteData(item));
}
},
created () {
this.validDataStr = JSON.stringify(this.getValidData(this.data));
this.$on('on-result-change', (params) => {
// lastValue: is click the final val
// fromInit: is this emit from update value
const lastValue = params.lastValue;
const changeOnSelect = params.changeOnSelect;
const fromInit = params.fromInit;
if (lastValue || changeOnSelect) {
const oldVal = JSON.stringify(this.currentValue);
this.selected = this.tmpSelected;
let newVal = [];
this.selected.forEach((item) => {
newVal.push(item.value);
});
if (!fromInit) {
this.updatingValue = true;
this.currentValue = newVal;
this.emitValue(this.currentValue, oldVal);
}
}
if (lastValue && !fromInit) {
this.handleClose();
}
});
},
mounted () {
this.updateSelected(true);
},
watch: {
visible (val) {
if (val) {
if (this.currentValue.length) {
this.updateSelected();
}
if (this.transfer) {
this.$refs.drop.update();
}
this.broadcast('Drop', 'on-update-popper');
} else {
if (this.filterable) {
this.query = '';
this.$refs.input.currentValue = '';
}
if (this.transfer) {
this.$refs.drop.destroy();
}
this.broadcast('Drop', 'on-destroy-popper');
}
this.$emit('on-visible-change', val);
},
value (val) {
this.currentValue = val;
if (!val.length) this.selected = [];
},
currentValue () {
this.$emit('input', this.currentValue);
if (this.updatingValue) {
this.updatingValue = false;
return;
}
this.updateSelected(true);
},
data: {
deep: true,
handler () {
const validDataStr = JSON.stringify(this.getValidData(this.data));
if (validDataStr !== this.validDataStr) {
this.validDataStr = validDataStr;
if (!this.isLoadedChildren) {
this.$nextTick(() => this.updateSelected(false, this.changeOnSelect));
}
this.isLoadedChildren = false;
}
}
}
}
};
</script>

View file

@ -0,0 +1,72 @@
<template>
<li :class="classes">
{{ data.label }}
<Icon :type="arrowType" :custom="customArrowType" :size="arrowSize" v-if="showArrow" />
<i v-if="showLoading" class="ivu-icon ivu-icon-ios-loading ivu-load-loop ivu-cascader-menu-item-loading"></i>
</li>
</template>
<script>
import Icon from '../icon/icon.vue';
export default {
name: 'Casitem',
components: { Icon },
props: {
data: Object,
prefixCls: String,
tmpItem: Object
},
computed: {
classes () {
return [
`${this.prefixCls}-menu-item`,
{
[`${this.prefixCls}-menu-item-active`]: this.tmpItem.value === this.data.value,
[`${this.prefixCls}-menu-item-disabled`]: this.data.disabled
}
];
},
showArrow () {
return (this.data.children && this.data.children.length) || ('loading' in this.data && !this.data.loading);
},
showLoading () {
return 'loading' in this.data && this.data.loading;
},
// 3.4.0, global setting customArrow arrow
arrowType () {
let type = 'ios-arrow-forward';
if (this.$IVIEW) {
if (this.$IVIEW.cascader.customItemArrow) {
type = '';
} else if (this.$IVIEW.cascader.itemArrow) {
type = this.$IVIEW.cascader.itemArrow;
}
}
return type;
},
// 3.4.0, global setting
customArrowType () {
let type = '';
if (this.$IVIEW) {
if (this.$IVIEW.cascader.customItemArrow) {
type = this.$IVIEW.cascader.customItemArrow;
}
}
return type;
},
// 3.4.0, global setting
arrowSize () {
let size = '';
if (this.$IVIEW) {
if (this.$IVIEW.cascader.itemArrowSize) {
size = this.$IVIEW.cascader.itemArrowSize;
}
}
return size;
}
}
};
</script>

View file

@ -0,0 +1,173 @@
<template>
<span>
<ul v-if="data && data.length" :class="[prefixCls + '-menu']">
<Casitem
v-for="item in data"
:key="getKey()"
:prefix-cls="prefixCls"
:data="item"
:tmp-item="tmpItem"
@click.native.stop="handleClickItem(item)"
@mouseenter.native.stop="handleHoverItem(item)"></Casitem>
</ul><Caspanel v-if="sublist && sublist.length" :prefix-cls="prefixCls" :data="sublist" :disabled="disabled" :trigger="trigger" :change-on-select="changeOnSelect"></Caspanel>
</span>
</template>
<script>
import Casitem from './casitem.vue';
import Emitter from '../../mixins/emitter';
import { findComponentUpward, findComponentDownward } from '../../utils/assist';
let key = 1;
export default {
name: 'Caspanel',
mixins: [ Emitter ],
components: { Casitem },
props: {
data: {
type: Array,
default () {
return [];
}
},
disabled: Boolean,
changeOnSelect: Boolean,
trigger: String,
prefixCls: String
},
data () {
return {
tmpItem: {},
result: [],
sublist: []
};
},
watch: {
data () {
this.sublist = [];
}
},
methods: {
handleClickItem (item) {
if (this.trigger !== 'click' && item.children && item.children.length) return; // #1922
this.handleTriggerItem(item, false, true);
},
handleHoverItem (item) {
if (this.trigger !== 'hover' || !item.children || !item.children.length) return; // #1922
this.handleTriggerItem(item, false, true);
},
handleTriggerItem (item, fromInit = false, fromUser = false) {
if (item.disabled) return;
const cascader = findComponentUpward(this, 'Cascader');
if (item.loading !== undefined && !item.children.length) {
if (cascader && cascader.loadData) {
cascader.loadData(item, () => {
// todo
if (fromUser) {
cascader.isLoadedChildren = true;
}
if (item.children.length) {
this.handleTriggerItem(item);
}
});
return;
}
}
// return value back recursion //
const backItem = this.getBaseItem(item);
// #5021 for this.changeOnSelect if #4472
if (
this.changeOnSelect ||
(backItem.label !== this.tmpItem.label || backItem.value !== this.tmpItem.value) ||
(backItem.label === this.tmpItem.label && backItem.value === this.tmpItem.value)
) {
this.tmpItem = backItem;
this.emitUpdate([backItem]);
}
if (item.children && item.children.length){
this.sublist = item.children;
this.dispatch('Cascader', 'on-result-change', {
lastValue: false,
changeOnSelect: this.changeOnSelect,
fromInit: fromInit
});
// #1553
if (this.changeOnSelect) {
const Caspanel = findComponentDownward(this, 'Caspanel');
if (Caspanel) {
Caspanel.$emit('on-clear', true);
}
}
} else {
this.sublist = [];
this.dispatch('Cascader', 'on-result-change', {
lastValue: true,
changeOnSelect: this.changeOnSelect,
fromInit: fromInit
});
}
if (cascader) {
cascader.$refs.drop.update();
}
},
updateResult (item) {
this.result = [this.tmpItem].concat(item);
this.emitUpdate(this.result);
},
getBaseItem (item) {
let backItem = Object.assign({}, item);
if (backItem.children) {
delete backItem.children;
}
return backItem;
},
emitUpdate (result) {
if (this.$parent.$options.name === 'Caspanel') {
this.$parent.updateResult(result);
} else {
this.$parent.$parent.updateResult(result);
}
},
getKey () {
return key++;
}
},
mounted () {
this.$on('on-find-selected', (params) => {
const val = params.value;
let value = [...val];
for (let i = 0; i < value.length; i++) {
for (let j = 0; j < this.data.length; j++) {
if (value[i] === this.data[j].value) {
this.handleTriggerItem(this.data[j], true);
value.splice(0, 1);
this.$nextTick(() => {
this.broadcast('Caspanel', 'on-find-selected', {
value: value
});
});
return false;
}
}
}
});
// deep for #1553
this.$on('on-clear', (deep = false) => {
this.sublist = [];
this.tmpItem = {};
if (deep) {
const Caspanel = findComponentDownward(this, 'Caspanel');
if (Caspanel) {
Caspanel.$emit('on-clear', true);
}
}
});
}
};
</script>

View file

@ -0,0 +1,2 @@
import Cascader from './cascader.vue';
export default Cascader;