Modify the directory structure
Modify the directory structure
This commit is contained in:
parent
31fbef10e4
commit
4b05d84ea2
175 changed files with 48 additions and 46 deletions
126
src/components/affix/affix.vue
Normal file
126
src/components/affix/affix.vue
Normal file
|
@ -0,0 +1,126 @@
|
|||
<template>
|
||||
<div>
|
||||
<div :class="classes" :style="styles">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const prefixCls = 'ivu-affix';
|
||||
|
||||
function getScroll(target, top) {
|
||||
const prop = top ? 'pageYOffset' : 'pageXOffset';
|
||||
const method = top ? 'scrollTop' : 'scrollLeft';
|
||||
|
||||
let ret = target[prop];
|
||||
|
||||
if (typeof ret !== 'number') {
|
||||
ret = window.document.documentElement[method];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function getOffset(element) {
|
||||
const rect = element.getBoundingClientRect();
|
||||
|
||||
const scrollTop = getScroll(window, true);
|
||||
const scrollLeft = getScroll(window);
|
||||
|
||||
const docEl = window.document.body;
|
||||
const clientTop = docEl.clientTop || 0;
|
||||
const clientLeft = docEl.clientLeft || 0;
|
||||
|
||||
return {
|
||||
top: rect.top + scrollTop - clientTop,
|
||||
left: rect.left + scrollLeft - clientLeft
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
props: {
|
||||
offsetTop: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
offsetBottom: {
|
||||
type: Number
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
affix: false,
|
||||
styles: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
offsetType () {
|
||||
let type = 'top';
|
||||
if (this.offsetBottom >= 0) {
|
||||
type = 'bottom';
|
||||
}
|
||||
|
||||
return type;
|
||||
},
|
||||
classes () {
|
||||
return [
|
||||
{
|
||||
[`${prefixCls}`]: this.affix
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
ready () {
|
||||
window.addEventListener('scroll', this.handleScroll, false);
|
||||
window.addEventListener('resize', this.handleScroll, false);
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('scroll', this.handleScroll, false);
|
||||
window.removeEventListener('resize', this.handleScroll, false);
|
||||
},
|
||||
methods: {
|
||||
handleScroll () {
|
||||
const affix = this.affix;
|
||||
const scrollTop = getScroll(window, true);
|
||||
const elOffset = getOffset(this.$el);
|
||||
const windowHeight = window.innerHeight;
|
||||
const elHeight = this.$el.getElementsByTagName('div')[0].offsetHeight;
|
||||
|
||||
// Fixed Top
|
||||
if ((elOffset.top - this.offsetTop) < scrollTop && this.offsetType == 'top' && !affix) {
|
||||
this.affix = true;
|
||||
this.styles = {
|
||||
top: `${this.offsetTop}px`,
|
||||
left: `${elOffset.left}px`,
|
||||
width: `${this.$el.offsetWidth}px`
|
||||
};
|
||||
|
||||
this.$emit('on-change', true);
|
||||
} else if ((elOffset.top - this.offsetTop) > scrollTop && this.offsetType == 'top' && affix) {
|
||||
this.affix = false;
|
||||
this.styles = null;
|
||||
|
||||
this.$emit('on-change', false);
|
||||
}
|
||||
|
||||
// Fixed Bottom
|
||||
if ((elOffset.top + this.offsetBottom + elHeight) > (scrollTop + windowHeight) && this.offsetType == 'bottom' && !affix) {
|
||||
this.affix = true;
|
||||
this.styles = {
|
||||
bottom: `${this.offsetBottom}px`,
|
||||
left: `${elOffset.left}px`,
|
||||
width: `${this.$el.offsetWidth}px`
|
||||
};
|
||||
|
||||
this.$emit('on-change', true);
|
||||
} else if ((elOffset.top + this.offsetBottom + elHeight) < (scrollTop + windowHeight) && this.offsetType == 'bottom' && affix) {
|
||||
this.affix = false;
|
||||
this.styles = null;
|
||||
|
||||
this.$emit('on-change', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
2
src/components/affix/index.js
Normal file
2
src/components/affix/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import Affix from './affix.vue';
|
||||
export default Affix;
|
101
src/components/alert/alert.vue
Normal file
101
src/components/alert/alert.vue
Normal file
|
@ -0,0 +1,101 @@
|
|||
<template>
|
||||
<div v-if="!closed" :class="wrapClasses" transition="fade">
|
||||
<span :class="iconClasses" v-if="showIcon">
|
||||
<slot name="icon">
|
||||
<Icon :type="iconType"></Icon>
|
||||
</slot>
|
||||
</span>
|
||||
<span :class="messageClasses"><slot></slot></span>
|
||||
<span :class="descClasses" v-el:desc><slot name="desc"></slot></span>
|
||||
<a :class="closeClasses" v-if="closable" @click="close">
|
||||
<slot name="close">
|
||||
<Icon type="ios-close-empty"></Icon>
|
||||
</slot>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Icon from '../icon';
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-alert';
|
||||
|
||||
export default {
|
||||
components: { Icon },
|
||||
props: {
|
||||
type: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['success', 'info', 'warning', 'error']);
|
||||
},
|
||||
default: 'info'
|
||||
},
|
||||
closable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showIcon: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
closed: false,
|
||||
desc: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
wrapClasses () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
`${prefixCls}-${this.type}`,
|
||||
{
|
||||
[`${prefixCls}-with-icon`]: this.showIcon,
|
||||
[`${prefixCls}-with-desc`]: this.desc
|
||||
}
|
||||
]
|
||||
},
|
||||
messageClasses () {
|
||||
return `${prefixCls}-message`;
|
||||
},
|
||||
descClasses () {
|
||||
return `${prefixCls}-desc`;
|
||||
},
|
||||
closeClasses () {
|
||||
return `${prefixCls}-close`;
|
||||
},
|
||||
iconClasses () {
|
||||
return `${prefixCls}-icon`;
|
||||
},
|
||||
iconType () {
|
||||
let type = '';
|
||||
|
||||
switch (this.type) {
|
||||
case 'success':
|
||||
type = 'checkmark-circled';
|
||||
break;
|
||||
case 'info':
|
||||
type = 'information-circled';
|
||||
break;
|
||||
case 'warning':
|
||||
type = 'android-alert';
|
||||
break;
|
||||
case 'error':
|
||||
type = 'close-circled';
|
||||
break;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close (e) {
|
||||
this.closed = true;
|
||||
this.$emit('on-close', e);
|
||||
}
|
||||
},
|
||||
compiled () {
|
||||
this.desc = this.$els.desc.innerHTML != '';
|
||||
}
|
||||
}
|
||||
</script>
|
2
src/components/alert/index.js
Normal file
2
src/components/alert/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import Alert from './alert.vue';
|
||||
export default Alert;
|
90
src/components/back-top/back-top.vue
Normal file
90
src/components/back-top/back-top.vue
Normal file
|
@ -0,0 +1,90 @@
|
|||
<template>
|
||||
<div :class="classes" :style="styles" @click="back">
|
||||
<slot>
|
||||
<div :class="innerClasses">
|
||||
<i class="ivu-icon ivu-icon-chevron-up"></i>
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
const prefixCls = 'ivu-back-top';
|
||||
|
||||
function getScroll(target, top) {
|
||||
const prop = top ? 'pageYOffset' : 'pageXOffset';
|
||||
const method = top ? 'scrollTop' : 'scrollLeft';
|
||||
|
||||
let ret = target[prop];
|
||||
|
||||
if (typeof ret !== 'number') {
|
||||
ret = window.document.documentElement[method];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export default {
|
||||
props: {
|
||||
height: {
|
||||
type: Number,
|
||||
default: 400
|
||||
},
|
||||
bottom: {
|
||||
type: Number,
|
||||
default: 30
|
||||
},
|
||||
right: {
|
||||
type: Number,
|
||||
default: 30
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
backTop: false
|
||||
}
|
||||
},
|
||||
ready () {
|
||||
window.addEventListener('scroll', this.handleScroll, false);
|
||||
window.addEventListener('resize', this.handleScroll, false);
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('scroll', this.handleScroll, false);
|
||||
window.removeEventListener('resize', this.handleScroll, false);
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-show`]: this.backTop
|
||||
}
|
||||
]
|
||||
},
|
||||
styles () {
|
||||
return {
|
||||
bottom: `${this.bottom}px`,
|
||||
right: `${this.right}px`
|
||||
}
|
||||
},
|
||||
innerClasses () {
|
||||
return `${prefixCls}-inner`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleScroll () {
|
||||
const backTop = this.backTop;
|
||||
const scrollTop = getScroll(window, true);
|
||||
|
||||
if (this.height <= scrollTop && !backTop) {
|
||||
this.backTop = true;
|
||||
} else if (this.height > scrollTop && backTop) {
|
||||
this.backTop = false;
|
||||
}
|
||||
},
|
||||
back () {
|
||||
window.scrollTo(0, 0);
|
||||
this.$emit('on-click');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
2
src/components/back-top/index.js
Normal file
2
src/components/back-top/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import BackTop from './back-top.vue';
|
||||
export default BackTop;
|
77
src/components/badge/badge.vue
Normal file
77
src/components/badge/badge.vue
Normal file
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<span v-if="dot" :class="classes" v-el:badge>
|
||||
<slot></slot>
|
||||
<sup :class="dotClasses" v-show="badge"></sup>
|
||||
</span>
|
||||
<span v-else :class="classes" v-el:badge>
|
||||
<slot></slot>
|
||||
<sup v-if="count" :class="countClasses" v-show="badge">{{ finalCount }}</sup>
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
const prefixCls = 'ivu-badge';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
count: [Number, String],
|
||||
dot: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
overflowCount: {
|
||||
type: [Number, String],
|
||||
default: 99
|
||||
},
|
||||
class: String
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return `${prefixCls}`;
|
||||
},
|
||||
dotClasses () {
|
||||
return `${prefixCls}-dot`;
|
||||
},
|
||||
countClasses () {
|
||||
return [
|
||||
`${prefixCls}-count`,
|
||||
{
|
||||
[`${this.class}`]: !!this.class,
|
||||
[`${prefixCls}-count-alone`]: this.alone
|
||||
}
|
||||
]
|
||||
},
|
||||
finalCount () {
|
||||
return parseInt(this.count) >= parseInt(this.overflowCount) ? `${this.overflowCount}+` : this.count;
|
||||
},
|
||||
badge () {
|
||||
let status = false;
|
||||
|
||||
if (this.count) {
|
||||
status = !(parseInt(this.count) === 0);
|
||||
}
|
||||
|
||||
if (this.dot) {
|
||||
status = true;
|
||||
if (this.count) {
|
||||
if (parseInt(this.count) === 0) {
|
||||
status = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
alone: false
|
||||
}
|
||||
},
|
||||
compiled () {
|
||||
const child_length = this.$els.badge.children.length;
|
||||
if (child_length === 1) {
|
||||
this.alone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
2
src/components/badge/index.js
Normal file
2
src/components/badge/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import Badge from './badge.vue';
|
||||
export default Badge;
|
37
src/components/base/notification/index.js
Normal file
37
src/components/base/notification/index.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import Notification from './notification.vue';
|
||||
import Vue from 'vue';
|
||||
import { camelcaseToHyphen } from '../../../utils/assist';
|
||||
|
||||
Notification.newInstance = properties => {
|
||||
const _props = properties || {};
|
||||
|
||||
let props = '';
|
||||
Object.keys(_props).forEach(prop => {
|
||||
props += ' :' + camelcaseToHyphen(prop) + '=' + prop;
|
||||
});
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = `<notification${props}></notification>`;
|
||||
document.body.appendChild(div);
|
||||
|
||||
const notification = new Vue({
|
||||
el: div,
|
||||
data: _props,
|
||||
components: { Notification }
|
||||
}).$children[0];
|
||||
|
||||
return {
|
||||
notice (noticeProps) {
|
||||
notification.add(noticeProps);
|
||||
},
|
||||
remove (key) {
|
||||
notification.close(key);
|
||||
},
|
||||
component: notification,
|
||||
destroy () {
|
||||
document.body.removeChild(div);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default Notification;
|
93
src/components/base/notification/notice.vue
Normal file
93
src/components/base/notification/notice.vue
Normal file
|
@ -0,0 +1,93 @@
|
|||
<template>
|
||||
<div :class="classes" :style="style" :transition="transitionName">
|
||||
<div :class="[`${baseClass}-content`]" v-el:content>{{{ content }}}</div>
|
||||
<a :class="[`${baseClass}-close`]" @click="close" v-if="closable">
|
||||
<i class="ivu-icon ivu-icon-ios-close-empty"></i>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
prefixCls: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 1.5
|
||||
},
|
||||
content: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
style: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
return {
|
||||
right: '50%'
|
||||
}
|
||||
}
|
||||
},
|
||||
closable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
className: {
|
||||
type: String
|
||||
},
|
||||
key: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
onClose: {
|
||||
type: Function
|
||||
},
|
||||
transitionName: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
baseClass () {
|
||||
return `${this.prefixCls}-notice`;
|
||||
},
|
||||
classes () {
|
||||
return [
|
||||
this.baseClass,
|
||||
{
|
||||
[`${this.className}`]: !!this.className,
|
||||
[`${this.baseClass}-closable`]: this.closable
|
||||
}
|
||||
]
|
||||
},
|
||||
contentClasses () {
|
||||
return `${this.baseClass}-content`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearCloseTimer () {
|
||||
if (this.closeTimer) {
|
||||
clearTimeout(this.closeTimer);
|
||||
this.closeTimer = null;
|
||||
}
|
||||
},
|
||||
close () {
|
||||
this.clearCloseTimer();
|
||||
this.onClose();
|
||||
this.$parent.close(this.key);
|
||||
}
|
||||
},
|
||||
compiled () {
|
||||
this.clearCloseTimer();
|
||||
|
||||
if (this.duration !== 0) {
|
||||
this.closeTimer = setTimeout(() => {
|
||||
this.close();
|
||||
}, this.duration * 1000)
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.clearCloseTimer();
|
||||
}
|
||||
}
|
||||
</script>
|
92
src/components/base/notification/notification.vue
Normal file
92
src/components/base/notification/notification.vue
Normal file
|
@ -0,0 +1,92 @@
|
|||
<template>
|
||||
<div :class="classes" :style="style">
|
||||
<Notice v-for="notice in notices"
|
||||
:prefix-cls="prefixCls"
|
||||
:style="notice.style"
|
||||
:content="notice.content"
|
||||
:duration="notice.duration"
|
||||
:closable="notice.closable"
|
||||
:key="notice.key"
|
||||
:transition-name="notice.transitionName"
|
||||
:on-close="notice.onClose">
|
||||
</Notice>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Notice from './notice.vue';
|
||||
|
||||
const prefixCls = 'ivu-notification';
|
||||
let seed = 0;
|
||||
const now = Date.now();
|
||||
|
||||
function getUuid () {
|
||||
return 'ivuNotification_' + now + '_' + (seed++);
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { Notice },
|
||||
props: {
|
||||
prefixCls: {
|
||||
type: String,
|
||||
default: prefixCls
|
||||
},
|
||||
style: {
|
||||
type: Object,
|
||||
default: function () {
|
||||
return {
|
||||
top: '65px',
|
||||
left: '50%'
|
||||
}
|
||||
}
|
||||
},
|
||||
content: {
|
||||
type: String
|
||||
},
|
||||
className: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
notices: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${this.prefixCls}`,
|
||||
{
|
||||
[`${this.className}`]: !!this.className
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
add (notice) {
|
||||
const key = notice.key || getUuid();
|
||||
|
||||
let _notice = Object.assign({
|
||||
style: {
|
||||
right: '50%'
|
||||
},
|
||||
content: '',
|
||||
duration: 1.5,
|
||||
closable: false,
|
||||
key: key
|
||||
}, notice);
|
||||
|
||||
this.notices.push(_notice);
|
||||
},
|
||||
close (key) {
|
||||
const notices = this.notices;
|
||||
|
||||
for (let i = 0; i < notices.length; i++) {
|
||||
if (notices[i].key === key) {
|
||||
this.notices.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
101
src/components/base/popper.js
Normal file
101
src/components/base/popper.js
Normal file
|
@ -0,0 +1,101 @@
|
|||
/**
|
||||
* https://github.com/freeze-component/vue-popper
|
||||
* */
|
||||
import Popper from 'popper.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
placement: {
|
||||
type: String,
|
||||
default: 'bottom'
|
||||
},
|
||||
boundariesPadding: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
reference: Object,
|
||||
popper: Object,
|
||||
offset: {
|
||||
default: 0
|
||||
},
|
||||
value: Boolean,
|
||||
transition: String,
|
||||
options: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
gpuAcceleration: false,
|
||||
boundariesElement: 'body'
|
||||
}
|
||||
}
|
||||
},
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.visible = val;
|
||||
this.$emit('input', val);
|
||||
}
|
||||
},
|
||||
visible(val) {
|
||||
val ? this.updatePopper() : this.destroyPopper();
|
||||
this.$emit('input', val);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
createPopper() {
|
||||
if (!/^(top|bottom|left|right)(-start|-end)?$/g.test(this.placement)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options = this.options;
|
||||
const popper = this.popper || this.$els.popper;
|
||||
const reference = this.reference || this.$els.reference;
|
||||
|
||||
if (!popper || !reference) return;
|
||||
|
||||
if (this.popperJS && this.popperJS.hasOwnProperty('destroy')) {
|
||||
this.popperJS.destroy();
|
||||
}
|
||||
|
||||
options.placement = this.placement;
|
||||
options.offset = this.offset;
|
||||
|
||||
this.popperJS = new Popper(reference, popper, options);
|
||||
this.popperJS.onCreate(popper => {
|
||||
this.resetTransformOrigin(popper);
|
||||
this.$nextTick(this.updatePopper);
|
||||
this.$emit('created', this);
|
||||
});
|
||||
},
|
||||
updatePopper() {
|
||||
this.popperJS ? this.popperJS.update() : this.createPopper();
|
||||
},
|
||||
doDestroy() {
|
||||
if (this.visible) return;
|
||||
this.popperJS.destroy();
|
||||
this.popperJS = null;
|
||||
},
|
||||
destroyPopper() {
|
||||
if (this.popperJS) {
|
||||
this.resetTransformOrigin(this.popperJS);
|
||||
}
|
||||
},
|
||||
resetTransformOrigin(popper) {
|
||||
let placementMap = {top: 'bottom', bottom: 'top', left: 'right', right: 'left'};
|
||||
let placement = popper._popper.getAttribute('x-placement').split('-')[0];
|
||||
let origin = placementMap[placement];
|
||||
popper._popper.style.transformOrigin = ['top', 'bottom'].indexOf(placement) > -1 ? `center ${ origin }` : `${ origin } center`;
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.popperJS) {
|
||||
this.popperJS.destroy();
|
||||
}
|
||||
}
|
||||
};
|
36
src/components/breadcrumb/breadcrumb-item.vue
Normal file
36
src/components/breadcrumb/breadcrumb-item.vue
Normal file
|
@ -0,0 +1,36 @@
|
|||
<template>
|
||||
<span>
|
||||
<a v-if="href" :href="href" :class="linkClasses">
|
||||
<slot></slot>
|
||||
</a>
|
||||
<span v-else :class="linkClasses">
|
||||
<slot></slot>
|
||||
</span>
|
||||
<span :class="separatorClasses">
|
||||
<slot name="separator">{{{ separator }}}</slot>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
const prefixCls = 'ivu-breadcrumb-item';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
href: {
|
||||
type: String
|
||||
},
|
||||
separator: {
|
||||
type: String,
|
||||
default: '/'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
linkClasses () {
|
||||
return `${prefixCls}-link`;
|
||||
},
|
||||
separatorClasses () {
|
||||
return `${prefixCls}-separator`;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
37
src/components/breadcrumb/breadcrumb.vue
Normal file
37
src/components/breadcrumb/breadcrumb.vue
Normal file
|
@ -0,0 +1,37 @@
|
|||
<template>
|
||||
<div :class="classes">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
const prefixCls = 'ivu-breadcrumb';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
separator: {
|
||||
type: String,
|
||||
default: '/'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return `${prefixCls}`;
|
||||
}
|
||||
},
|
||||
compiled () {
|
||||
this.updateChildren();
|
||||
},
|
||||
methods: {
|
||||
updateChildren () {
|
||||
this.$children.forEach((child) => {
|
||||
child.separator = this.separator;
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
separator () {
|
||||
this.updateChildren();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
5
src/components/breadcrumb/index.js
Normal file
5
src/components/breadcrumb/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Breadcrumb from './breadcrumb.vue';
|
||||
import BreadcrumbItem from './breadcrumb-item.vue';
|
||||
|
||||
Breadcrumb.Item = BreadcrumbItem;
|
||||
export default Breadcrumb;
|
30
src/components/button/button-group.vue
Normal file
30
src/components/button/button-group.vue
Normal file
|
@ -0,0 +1,30 @@
|
|||
<template>
|
||||
<div :class="classes">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-btn-group';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
size: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['small', 'large']);
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-${this.size}`]: !!this.size
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
68
src/components/button/button.vue
Normal file
68
src/components/button/button.vue
Normal file
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<button :type="htmlType" :class="classes" :disabled="disabled">
|
||||
<i :class="loadingIconClasses" v-if="loading"></i>
|
||||
<i :class="typeIconClasses" v-if="icon && !loading"></i>
|
||||
<slot></slot>
|
||||
</button>
|
||||
</template>
|
||||
<script>
|
||||
import Icon from '../icon';
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-btn';
|
||||
const iconPrefixCls = 'ivu-icon';
|
||||
|
||||
export default {
|
||||
components: { Icon },
|
||||
props: {
|
||||
type: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['primary', 'ghost']);
|
||||
}
|
||||
},
|
||||
shape: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['circle', 'circle-outline']);
|
||||
}
|
||||
},
|
||||
size: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['small', 'large']);
|
||||
}
|
||||
},
|
||||
loading: Boolean,
|
||||
disabled: Boolean,
|
||||
htmlType: {
|
||||
default: 'button',
|
||||
validator (value) {
|
||||
return oneOf(value, ['button', 'submit', 'reset']);
|
||||
}
|
||||
},
|
||||
icon: String
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-${this.type}`]: !!this.type,
|
||||
[`${prefixCls}-${this.shape}`]: !!this.shape,
|
||||
[`${prefixCls}-${this.size}`]: !!this.size,
|
||||
[`${prefixCls}-loading`]: this.loading != null && this.loading
|
||||
}
|
||||
]
|
||||
},
|
||||
loadingIconClasses () {
|
||||
return `${iconPrefixCls} ivu-load-loop ${iconPrefixCls}-load-c`;
|
||||
},
|
||||
typeIconClasses () {
|
||||
return [
|
||||
`${iconPrefixCls}`,
|
||||
{
|
||||
[`${iconPrefixCls}-${this.icon}`]: !!this.icon
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
5
src/components/button/index.js
Normal file
5
src/components/button/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Button from './button.vue';
|
||||
import ButtonGroup from './button-group.vue';
|
||||
|
||||
Button.Group = ButtonGroup;
|
||||
export default Button;
|
58
src/components/card/card.vue
Normal file
58
src/components/card/card.vue
Normal file
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<div :class="classes">
|
||||
<div :class="headClasses" v-if="showHead" v-el:head><slot name="title"></slot></div>
|
||||
<div :class="extraClasses" v-if="showExtra" v-el:extra><slot name="extra"></slot></div>
|
||||
<div :class="bodyClasses"><slot></slot></div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
const prefixCls = 'ivu-card';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
bordered: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
disHover: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
shadow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
showHead: true,
|
||||
showExtra: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-bordered`]: this.bordered && !this.shadow,
|
||||
[`${prefixCls}-dis-hover`]: this.disHover || this.shadow,
|
||||
[`${prefixCls}-shadow`]: this.shadow
|
||||
}
|
||||
]
|
||||
},
|
||||
headClasses () {
|
||||
return `${prefixCls}-head`;
|
||||
},
|
||||
extraClasses () {
|
||||
return `${prefixCls}-extra`;
|
||||
},
|
||||
bodyClasses () {
|
||||
return `${prefixCls}-body`;
|
||||
}
|
||||
},
|
||||
compiled () {
|
||||
this.showHead = this.$els.head.innerHTML != '';
|
||||
this.showExtra = this.$els.extra.innerHTML != '';
|
||||
}
|
||||
}
|
||||
</script>
|
2
src/components/card/index.js
Normal file
2
src/components/card/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import Card from './card.vue';
|
||||
export default Card;
|
52
src/components/checkbox/checkbox-group.vue
Normal file
52
src/components/checkbox/checkbox-group.vue
Normal file
|
@ -0,0 +1,52 @@
|
|||
<template>
|
||||
<div :class="classes">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
const prefixCls = 'ivu-checkbox-group';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
model: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return `${prefixCls}`;
|
||||
}
|
||||
},
|
||||
compiled () {
|
||||
this.updateModel(true);
|
||||
},
|
||||
methods: {
|
||||
updateModel (update) {
|
||||
const model = this.model;
|
||||
|
||||
this.$children.forEach((child) => {
|
||||
child.model = model;
|
||||
|
||||
if (update) {
|
||||
child.selected = model.indexOf(child.value) >= 0;
|
||||
child.group = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
change (data) {
|
||||
this.model = data;
|
||||
this.$emit('on-change', data);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
model (val, oldVal) {
|
||||
if (val == oldVal) {
|
||||
this.updateModel();
|
||||
} else {
|
||||
this.updateModel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
104
src/components/checkbox/checkbox.vue
Normal file
104
src/components/checkbox/checkbox.vue
Normal file
|
@ -0,0 +1,104 @@
|
|||
<template>
|
||||
<label :class="wrapClasses">
|
||||
<span :class="checkboxClasses">
|
||||
<span :class="innerClasses"></span>
|
||||
<input
|
||||
v-if="group"
|
||||
type="checkbox"
|
||||
:class="inputClasses"
|
||||
:disabled="disabled"
|
||||
:value="value"
|
||||
v-model="model"
|
||||
@change="change">
|
||||
<input
|
||||
v-if="!group"
|
||||
type="checkbox"
|
||||
:class="inputClasses"
|
||||
:disabled="disabled"
|
||||
v-model="checked"
|
||||
@change="change">
|
||||
</span>
|
||||
<slot><span>{{ value }}</span></slot>
|
||||
</label>
|
||||
</template>
|
||||
<script>
|
||||
const prefixCls = 'ivu-checkbox';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
type: [String, Number, Boolean]
|
||||
},
|
||||
checked: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
model: [],
|
||||
selected: false,
|
||||
group: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
wrapClasses () {
|
||||
return [
|
||||
`${prefixCls}-wrapper`,
|
||||
{
|
||||
[`${prefixCls}-group-item`]: this.group,
|
||||
[`${prefixCls}-wrapper-checked`]: this.selected,
|
||||
[`${prefixCls}-wrapper-disabled`]: this.disabled
|
||||
}
|
||||
]
|
||||
},
|
||||
checkboxClasses () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-checked`]: this.selected,
|
||||
[`${prefixCls}-disabled`]: this.disabled
|
||||
}
|
||||
]
|
||||
},
|
||||
innerClasses () {
|
||||
return `${prefixCls}-inner`;
|
||||
},
|
||||
inputClasses () {
|
||||
return `${prefixCls}-input`;
|
||||
}
|
||||
},
|
||||
ready () {
|
||||
if (!this.group) {
|
||||
this.updateModel();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
change (event) {
|
||||
if (this.disabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.selected = event.target.checked;
|
||||
|
||||
if (this.group) {
|
||||
this.$parent.change(this.model);
|
||||
} else {
|
||||
this.$emit('on-change', this.checked);
|
||||
}
|
||||
},
|
||||
updateModel () {
|
||||
this.selected = this.checked;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
checked () {
|
||||
this.updateModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
5
src/components/checkbox/index.js
Normal file
5
src/components/checkbox/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Checkbox from './checkbox.vue';
|
||||
import CheckboxGroup from './checkbox-group.vue';
|
||||
|
||||
Checkbox.Group = CheckboxGroup;
|
||||
export default Checkbox;
|
83
src/components/circle/circle.vue
Normal file
83
src/components/circle/circle.vue
Normal file
|
@ -0,0 +1,83 @@
|
|||
<template>
|
||||
<div :style="circleSize" :class="wrapClasses">
|
||||
<svg viewBox="0 0 100 100">
|
||||
<path :d="pathString" :stroke="trailColor" :stroke-width="trailWidth" :fill-opacity="0"/>
|
||||
<path :d="pathString" :stroke-linecap="strokeLinecap" :stroke="strokeColor" :stroke-width="strokeWidth" fill-opacity="0" :style="pathStyle"/>
|
||||
</svg>
|
||||
<div :class="innerClasses">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-chart-circle';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
percent: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 120
|
||||
},
|
||||
strokeWidth: {
|
||||
type: Number,
|
||||
default: 6
|
||||
},
|
||||
strokeColor: {
|
||||
type: String,
|
||||
default: '#2db7f5'
|
||||
},
|
||||
strokeLinecap: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['square', 'round']);
|
||||
},
|
||||
default: 'round'
|
||||
},
|
||||
trailWidth: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
trailColor: {
|
||||
type: String,
|
||||
default: '#eaeef2'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
circleSize () {
|
||||
return {
|
||||
width: `${this.size}px`,
|
||||
height: `${this.size}px`
|
||||
};
|
||||
},
|
||||
radius () {
|
||||
return 50 - this.strokeWidth / 2;
|
||||
},
|
||||
pathString () {
|
||||
return `M 50,50 m 0,-${this.radius}
|
||||
a ${this.radius},${this.radius} 0 1 1 0,${2 * this.radius}
|
||||
a ${this.radius},${this.radius} 0 1 1 0,-${2 * this.radius}`;
|
||||
},
|
||||
len () {
|
||||
return Math.PI * 2 * this.radius;
|
||||
},
|
||||
pathStyle () {
|
||||
return {
|
||||
'stroke-dasharray': `${this.len}px ${this.len}px`,
|
||||
'stroke-dashoffset': `${((100 - this.percent) / 100 * this.len)}px`,
|
||||
'transition': 'stroke-dashoffset 0.6s ease 0s, stroke 0.6s ease'
|
||||
}
|
||||
},
|
||||
wrapClasses () {
|
||||
return `${prefixCls}`;
|
||||
},
|
||||
innerClasses () {
|
||||
return `${prefixCls}-inner`;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
2
src/components/circle/index.js
Normal file
2
src/components/circle/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import Circle from './circle.vue';
|
||||
export default Circle;
|
98
src/components/collapse/collapse.vue
Normal file
98
src/components/collapse/collapse.vue
Normal file
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<div :class="classes">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
const prefixCls = 'ivu-collapse';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
accordion: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
activeKey: {
|
||||
type: [Array, String]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return `${prefixCls}`;
|
||||
}
|
||||
},
|
||||
compiled () {
|
||||
this.setActive();
|
||||
},
|
||||
methods: {
|
||||
setActive () {
|
||||
const activeKey = this.getActiveKey();
|
||||
|
||||
this.$children.forEach((child, index) => {
|
||||
const key = child.key || index.toString();
|
||||
let isActive = false;
|
||||
|
||||
if (self.accordion) {
|
||||
isActive = activeKey === key;
|
||||
} else {
|
||||
isActive = activeKey.indexOf(key) > -1;
|
||||
}
|
||||
|
||||
child.isActive = isActive;
|
||||
child.index = index;
|
||||
})
|
||||
},
|
||||
getActiveKey () {
|
||||
let activeKey = this.activeKey || [];
|
||||
const accordion = this.accordion;
|
||||
|
||||
if (!Array.isArray(activeKey)) {
|
||||
activeKey = [activeKey];
|
||||
}
|
||||
|
||||
if (accordion && activeKey.length > 1) {
|
||||
activeKey = [activeKey[0]];
|
||||
}
|
||||
|
||||
for (let i = 0; i < activeKey.length; i++) {
|
||||
activeKey[i] = activeKey[i].toString();
|
||||
}
|
||||
|
||||
return activeKey;
|
||||
},
|
||||
toggle (data) {
|
||||
const key = data.key.toString();
|
||||
let newActiveKey = [];
|
||||
|
||||
if (this.accordion) {
|
||||
if (!data.isActive) {
|
||||
newActiveKey.push(key);
|
||||
}
|
||||
} else {
|
||||
let activeKey = this.getActiveKey();
|
||||
const keyIndex = activeKey.indexOf(key);
|
||||
|
||||
if (data.isActive) {
|
||||
if (keyIndex > -1) {
|
||||
activeKey.splice(keyIndex, 1);
|
||||
}
|
||||
} else {
|
||||
if (keyIndex < 0) {
|
||||
activeKey.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
newActiveKey = activeKey;
|
||||
}
|
||||
|
||||
this.activeKey = newActiveKey;
|
||||
this.$emit('on-change', newActiveKey);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
activeKey () {
|
||||
this.setActive();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
5
src/components/collapse/index.js
Normal file
5
src/components/collapse/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Collapse from './collapse.vue';
|
||||
import Panel from './panel.vue';
|
||||
|
||||
Collapse.Panel = Panel;
|
||||
export default Collapse;
|
57
src/components/collapse/panel.vue
Normal file
57
src/components/collapse/panel.vue
Normal file
|
@ -0,0 +1,57 @@
|
|||
<template>
|
||||
<div :class="itemClasses">
|
||||
<div :class="headerClasses" @click="toggle">
|
||||
<Icon type="arrow-right-b"></Icon>
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div :class="concentClasses" v-show="isActive">
|
||||
<div :class="boxClasses"><slot name="content"></slot></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Icon from '../icon';
|
||||
const prefixCls = 'ivu-collapse';
|
||||
|
||||
export default {
|
||||
components: { Icon },
|
||||
props: {
|
||||
key: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
index: 0, // use index for default when key is null
|
||||
isActive: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
itemClasses () {
|
||||
return [
|
||||
`${prefixCls}-item`,
|
||||
{
|
||||
[`${prefixCls}-item-active`]: this.isActive
|
||||
}
|
||||
]
|
||||
},
|
||||
headerClasses () {
|
||||
return `${prefixCls}-header`;
|
||||
},
|
||||
concentClasses () {
|
||||
return `${prefixCls}-content`;
|
||||
},
|
||||
boxClasses () {
|
||||
return `${prefixCls}-content-box`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle () {
|
||||
this.$parent.toggle({
|
||||
key: this.key || this.index,
|
||||
isActive: this.isActive
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
27
src/components/icon/icon.vue
Normal file
27
src/components/icon/icon.vue
Normal file
|
@ -0,0 +1,27 @@
|
|||
<template>
|
||||
<i :class="classes" :style="styles"></i>
|
||||
</template>
|
||||
<script>
|
||||
const prefixCls = 'ivu-icon';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
type: String,
|
||||
size: [Number, String]
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return `${prefixCls} ${prefixCls}-${this.type}`
|
||||
},
|
||||
styles () {
|
||||
if (!!this.size) {
|
||||
return {
|
||||
'font-size': `${this.size}px`
|
||||
}
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
2
src/components/icon/index.js
Normal file
2
src/components/icon/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import Icon from './icon.vue';
|
||||
export default Icon;
|
2
src/components/input-number/index.js
Normal file
2
src/components/input-number/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import InputNumber from './input-number.vue';
|
||||
export default InputNumber;
|
242
src/components/input-number/input-number.vue
Normal file
242
src/components/input-number/input-number.vue
Normal file
|
@ -0,0 +1,242 @@
|
|||
<template>
|
||||
<div :class="wrapClasses">
|
||||
<div :class="handlerClasses">
|
||||
<a
|
||||
@click="up"
|
||||
@mouse.down="preventDefault"
|
||||
:class="upClasses">
|
||||
<span :class="innerUpClasses" @click="preventDefault"></span>
|
||||
</a>
|
||||
<a
|
||||
@click="down"
|
||||
@mouse.down="preventDefault"
|
||||
:class="downClasses">
|
||||
<span :class="innerDownClasses" @click="preventDefault"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div :class="inputWrapClasses">
|
||||
<input
|
||||
:class="inputClasses"
|
||||
:disabled="disabled"
|
||||
autoComplete="off"
|
||||
@focus="focus"
|
||||
@blur="blur"
|
||||
@keydown.stop="keyDown"
|
||||
@change="change"
|
||||
:value="value">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-input-number';
|
||||
const iconPrefixCls = 'ivu-icon';
|
||||
|
||||
function isValueNumber (value) {
|
||||
return (/(^-?[0-9]+\.{1}\d+$)|(^-?[1-9][0-9]*$)/).test(value + '');
|
||||
}
|
||||
function addNum (num1, num2) {
|
||||
var sq1, sq2, m;
|
||||
try {
|
||||
sq1 = num1.toString().split(".")[1].length;
|
||||
}
|
||||
catch (e) {
|
||||
sq1 = 0;
|
||||
}
|
||||
try {
|
||||
sq2 = num2.toString().split(".")[1].length;
|
||||
}
|
||||
catch (e) {
|
||||
sq2 = 0;
|
||||
}
|
||||
// if (sq1 === 0 || sq2 === 0) {
|
||||
// return num1 + num2;
|
||||
// } else {
|
||||
// m = Math.pow(10, Math.max(sq1, sq2));
|
||||
// return (num1 * m + num2 * m) / m;
|
||||
// }
|
||||
m = Math.pow(10, Math.max(sq1, sq2));
|
||||
return (num1 * m + num2 * m) / m;
|
||||
}
|
||||
|
||||
export default {
|
||||
props: {
|
||||
max: {
|
||||
type: Number,
|
||||
default: Infinity
|
||||
},
|
||||
min: {
|
||||
type: Number,
|
||||
default: -Infinity
|
||||
},
|
||||
step: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
size: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['small', 'large']);
|
||||
}
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
focused: false,
|
||||
upDisabled: false,
|
||||
downDisabled: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
wrapClasses () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-${this.size}`]: !!this.size,
|
||||
[`${prefixCls}-disabled`]: this.disabled,
|
||||
[`${prefixCls}-focused`]: this.focused
|
||||
}
|
||||
]
|
||||
},
|
||||
handlerClasses () {
|
||||
return `${prefixCls}-handler-wrap`;
|
||||
},
|
||||
upClasses () {
|
||||
return [
|
||||
`${prefixCls}-handler`,
|
||||
`${prefixCls}-handler-up`,
|
||||
{
|
||||
[`${prefixCls}-handler-up-disabled`]: this.upDisabled
|
||||
}
|
||||
]
|
||||
},
|
||||
innerUpClasses () {
|
||||
return `${prefixCls}-handler-up-inner ${iconPrefixCls} ${iconPrefixCls}-ios-arrow-up`;
|
||||
},
|
||||
downClasses () {
|
||||
return [
|
||||
`${prefixCls}-handler`,
|
||||
`${prefixCls}-handler-down`,
|
||||
{
|
||||
[`${prefixCls}-handler-down-disabled`]: this.downDisabled
|
||||
}
|
||||
]
|
||||
},
|
||||
innerDownClasses () {
|
||||
return `${prefixCls}-handler-down-inner ${iconPrefixCls} ${iconPrefixCls}-ios-arrow-down`;
|
||||
},
|
||||
inputWrapClasses () {
|
||||
return `${prefixCls}-input-wrap`;
|
||||
},
|
||||
inputClasses () {
|
||||
return `${prefixCls}-input`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
preventDefault (e) {
|
||||
e.preventDefault();
|
||||
},
|
||||
up () {
|
||||
if (this.upDisabled) {
|
||||
return false;
|
||||
}
|
||||
this.changeStep('up');
|
||||
},
|
||||
down () {
|
||||
if (this.downDisabled) {
|
||||
return false;
|
||||
}
|
||||
this.changeStep('down');
|
||||
},
|
||||
changeStep (type) {
|
||||
if (this.disabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let val = Number(this.value);
|
||||
const step = Number(this.step);
|
||||
if (isNaN(val)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type == 'up') {
|
||||
val = addNum(val, step);
|
||||
} else if (type == 'down') {
|
||||
val = addNum(val, -step);
|
||||
}
|
||||
this.setValue(val);
|
||||
},
|
||||
setValue (val) {
|
||||
this.$nextTick(() => {
|
||||
this.value = val;
|
||||
});
|
||||
|
||||
this.$emit('on-change', val);
|
||||
},
|
||||
focus () {
|
||||
this.focused = true;
|
||||
},
|
||||
blur () {
|
||||
this.focused = false;
|
||||
},
|
||||
keyDown (e) {
|
||||
if (e.keyCode === 38) {
|
||||
e.preventDefault();
|
||||
this.up()
|
||||
} else if (e.keyCode === 40) {
|
||||
e.preventDefault();
|
||||
this.down()
|
||||
}
|
||||
},
|
||||
change (event) {
|
||||
let val = event.target.value.trim();
|
||||
|
||||
const max = this.max;
|
||||
const min = this.min;
|
||||
|
||||
if (isValueNumber(val)) {
|
||||
val = Number(val);
|
||||
this.value = val;
|
||||
|
||||
if (val > max) {
|
||||
this.setValue(max);
|
||||
} else if (val < min) {
|
||||
this.setValue(min);
|
||||
} else {
|
||||
this.setValue(val);
|
||||
}
|
||||
} else {
|
||||
event.target.value = this.value;
|
||||
}
|
||||
},
|
||||
changeVal (val) {
|
||||
if (isValueNumber(val) || val === 0) {
|
||||
val = Number(val);
|
||||
const step = this.step;
|
||||
|
||||
this.upDisabled = val + step > this.max;
|
||||
this.downDisabled = val - step < this.min;
|
||||
} else {
|
||||
this.upDisabled = true;
|
||||
this.downDisabled = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
ready () {
|
||||
this.changeVal(this.value);
|
||||
},
|
||||
watch: {
|
||||
value (val) {
|
||||
this.changeVal(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
2
src/components/input/index.js
Normal file
2
src/components/input/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import Input from './input.vue';
|
||||
export default Input;
|
49
src/components/input/input.vue
Normal file
49
src/components/input/input.vue
Normal file
|
@ -0,0 +1,49 @@
|
|||
<template>
|
||||
<input
|
||||
:class="classes"
|
||||
:type="type"
|
||||
:placeholder="placeholder"
|
||||
:name="name"
|
||||
v-model="value">
|
||||
</template>
|
||||
<script>
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-input';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: 'text'
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
twoWay: true
|
||||
},
|
||||
placeholder: String,
|
||||
name: String,
|
||||
size: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['small', 'large']);
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-${this.size}`]: !!this.size
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
36
src/components/layout/col.vue
Normal file
36
src/components/layout/col.vue
Normal file
|
@ -0,0 +1,36 @@
|
|||
<template>
|
||||
<div :class="classes">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-col';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
span: [Number, String],
|
||||
order: [Number, String],
|
||||
offset: [Number, String],
|
||||
push: [Number, String],
|
||||
pull: [Number, String],
|
||||
className: String
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-span-${this.span}`]: this.span,
|
||||
[`${prefixCls}-order-${this.order}`]: this.order,
|
||||
[`${prefixCls}-offset-${this.offset}`]: this.offset,
|
||||
[`${prefixCls}-push-${this.push}`]: this.push,
|
||||
[`${prefixCls}-pull-${this.pull}`]: this.pull,
|
||||
[`${this.className}`]: !!this.className
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
4
src/components/layout/index.js
Normal file
4
src/components/layout/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import Row from './row.vue';
|
||||
import Col from './col.vue';
|
||||
|
||||
export { Row, Col };
|
44
src/components/layout/row.vue
Normal file
44
src/components/layout/row.vue
Normal file
|
@ -0,0 +1,44 @@
|
|||
<template>
|
||||
<div :class="classes">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-row';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
type: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['flex']);
|
||||
}
|
||||
},
|
||||
align: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['top', 'middle', 'bottom']);
|
||||
}
|
||||
},
|
||||
justify: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['start', 'end', 'center', 'space-around', 'space-between']);
|
||||
}
|
||||
},
|
||||
className: String
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-${this.type}`]: !!this.type,
|
||||
[`${prefixCls}-${this.type}-${this.align}`]: !!this.align,
|
||||
[`${prefixCls}-${this.type}-${this.justify}`]: !!this.justify,
|
||||
[`${this.className}`]: !!this.className
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
110
src/components/loading-bar/index.js
Normal file
110
src/components/loading-bar/index.js
Normal file
|
@ -0,0 +1,110 @@
|
|||
import LoadingBar from './loading-bar';
|
||||
|
||||
let loadingBarInstance;
|
||||
let color = 'primary';
|
||||
let failedColor = 'error';
|
||||
let height = 2;
|
||||
let timer;
|
||||
|
||||
function getLoadingBarInstance () {
|
||||
loadingBarInstance = loadingBarInstance || LoadingBar.newInstance({
|
||||
color: color,
|
||||
failedColor: failedColor,
|
||||
height: height
|
||||
});
|
||||
|
||||
return loadingBarInstance;
|
||||
}
|
||||
|
||||
function update(options) {
|
||||
let instance = getLoadingBarInstance();
|
||||
|
||||
instance.update(options);
|
||||
}
|
||||
|
||||
function hide() {
|
||||
setTimeout(() => {
|
||||
update({
|
||||
show: false
|
||||
});
|
||||
setTimeout(() => {
|
||||
update({
|
||||
percent: 0
|
||||
});
|
||||
}, 200)
|
||||
}, 800);
|
||||
}
|
||||
|
||||
function clearTimer() {
|
||||
if (timer) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
start () {
|
||||
let percent = 0;
|
||||
|
||||
update({
|
||||
percent: percent,
|
||||
status: 'success',
|
||||
show: true
|
||||
});
|
||||
|
||||
timer = setInterval(() => {
|
||||
percent += Math.floor(Math.random () * 3 + 5);
|
||||
if (percent > 95) {
|
||||
clearTimer();
|
||||
}
|
||||
update({
|
||||
percent: percent,
|
||||
status: 'success',
|
||||
show: true
|
||||
});
|
||||
}, 200);
|
||||
},
|
||||
update (percent) {
|
||||
clearTimer();
|
||||
update({
|
||||
percent: percent,
|
||||
status: 'success',
|
||||
show: true
|
||||
});
|
||||
},
|
||||
finish () {
|
||||
clearTimer();
|
||||
update({
|
||||
percent: 100,
|
||||
status: 'success',
|
||||
show: true
|
||||
});
|
||||
hide();
|
||||
},
|
||||
error () {
|
||||
clearTimer();
|
||||
update({
|
||||
percent: 100,
|
||||
status: 'error',
|
||||
show: true
|
||||
});
|
||||
hide();
|
||||
},
|
||||
config (options) {
|
||||
if (options.color) {
|
||||
color = options.color;
|
||||
}
|
||||
if (options.failedColor) {
|
||||
failedColor = options.failedColor;
|
||||
}
|
||||
if (options.height) {
|
||||
height = options.height;
|
||||
}
|
||||
},
|
||||
destroy () {
|
||||
clearTimer();
|
||||
let instance = getLoadingBarInstance();
|
||||
loadingBarInstance = null;
|
||||
instance.destroy();
|
||||
}
|
||||
}
|
42
src/components/loading-bar/loading-bar.js
Normal file
42
src/components/loading-bar/loading-bar.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
import LoadingBar from './loading-bar.vue';
|
||||
import Vue from 'vue';
|
||||
import { camelcaseToHyphen } from '../../utils/assist';
|
||||
|
||||
LoadingBar.newInstance = properties => {
|
||||
const _props = properties || {};
|
||||
|
||||
let props = '';
|
||||
Object.keys(_props).forEach(prop => {
|
||||
props += ' :' + camelcaseToHyphen(prop) + '=' + prop;
|
||||
});
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = `<loading-bar${props}></loading-bar>`;
|
||||
document.body.appendChild(div);
|
||||
|
||||
const loading_bar = new Vue({
|
||||
el: div,
|
||||
data: _props,
|
||||
components: { LoadingBar }
|
||||
}).$children[0];
|
||||
|
||||
return {
|
||||
update (options) {
|
||||
if ('percent' in options) {
|
||||
loading_bar.percent = options.percent;
|
||||
}
|
||||
if (options.status) {
|
||||
loading_bar.status = options.status;
|
||||
}
|
||||
if ('show' in options) {
|
||||
loading_bar.show = options.show;
|
||||
}
|
||||
},
|
||||
component: loading_bar,
|
||||
destroy () {
|
||||
document.body.removeChild(div);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default LoadingBar;
|
77
src/components/loading-bar/loading-bar.vue
Normal file
77
src/components/loading-bar/loading-bar.vue
Normal file
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<div :class="classes" :style="outerStyles" v-show="show" transition="fade">
|
||||
<div :class="innerClasses" :style="styles"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-loading-bar';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
percent: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: 'primary'
|
||||
},
|
||||
failedColor: {
|
||||
type: String,
|
||||
default: 'error'
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 2
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
validator (value) {
|
||||
return oneOf(value, ['success', 'error']);
|
||||
},
|
||||
default: 'success'
|
||||
},
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return `${prefixCls}`;
|
||||
},
|
||||
innerClasses () {
|
||||
return [
|
||||
`${prefixCls}-inner`,
|
||||
{
|
||||
[`${prefixCls}-inner-color-primary`]: this.color === 'primary' && this.status === 'success',
|
||||
[`${prefixCls}-inner-failed-color-error`]: this.failedColor === 'error' && this.status === 'error'
|
||||
}
|
||||
]
|
||||
},
|
||||
outerStyles () {
|
||||
return {
|
||||
height: `${this.height}px`
|
||||
}
|
||||
},
|
||||
styles () {
|
||||
let style = {
|
||||
width: `${this.percent}%`,
|
||||
height: `${this.height}px`
|
||||
};
|
||||
|
||||
if (this.color !== 'primary' && this.status === 'success') {
|
||||
style.backgroundColor = this.color;
|
||||
}
|
||||
|
||||
if (this.failedColor !== 'error' && this.status === 'error') {
|
||||
style.backgroundColor = this.failedColor;
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
97
src/components/message/index.js
Normal file
97
src/components/message/index.js
Normal file
|
@ -0,0 +1,97 @@
|
|||
import Notification from '../base/notification';
|
||||
|
||||
const prefixCls = 'ivu-message';
|
||||
const iconPrefixCls = 'ivu-icon';
|
||||
const prefixKey = 'ivu_message_key_';
|
||||
|
||||
let defaultDuration = 1.5;
|
||||
let top;
|
||||
let messageInstance;
|
||||
let key = 1;
|
||||
|
||||
const iconTypes = {
|
||||
'info': 'information-circled',
|
||||
'success': 'checkmark-circled',
|
||||
'warning': 'android-alert',
|
||||
'error': 'close-circled',
|
||||
'loading': 'load-c'
|
||||
};
|
||||
|
||||
function getMessageInstance () {
|
||||
messageInstance = messageInstance || Notification.newInstance({
|
||||
prefixCls: prefixCls,
|
||||
style: {
|
||||
top: `${top}px`
|
||||
}
|
||||
});
|
||||
|
||||
return messageInstance;
|
||||
}
|
||||
|
||||
function notice (content, duration = defaultDuration, type, onClose) {
|
||||
if (!onClose) {
|
||||
onClose = function () {
|
||||
|
||||
}
|
||||
}
|
||||
const iconType = iconTypes[type];
|
||||
|
||||
// if loading
|
||||
const loadCls = type === 'loading' ? ' ivu-load-loop' : '';
|
||||
|
||||
let instance = getMessageInstance();
|
||||
|
||||
instance.notice({
|
||||
key: `${prefixKey}${key}`,
|
||||
duration: duration,
|
||||
style: {},
|
||||
transitionName: 'move-up',
|
||||
content: `
|
||||
<div class="${prefixCls}-custom-content ${prefixCls}-${type}">
|
||||
<i class="${iconPrefixCls} ${iconPrefixCls}-${iconType}${loadCls}"></i>
|
||||
<span>${content}</span>
|
||||
</div>
|
||||
`,
|
||||
onClose: onClose
|
||||
});
|
||||
|
||||
// 用于手动消除
|
||||
return (function () {
|
||||
let target = key++;
|
||||
|
||||
return function () {
|
||||
instance.remove(`${prefixKey}${target}`);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
export default {
|
||||
info (content, duration, onClose) {
|
||||
return notice(content, duration, 'info', onClose);
|
||||
},
|
||||
success (content, duration, onClose) {
|
||||
return notice(content, duration, 'success', onClose);
|
||||
},
|
||||
warning (content, duration, onClose) {
|
||||
return notice(content, duration, 'warning', onClose);
|
||||
},
|
||||
error (content, duration, onClose) {
|
||||
return notice(content, duration, 'error', onClose);
|
||||
},
|
||||
loading (content, duration, onClose) {
|
||||
return notice(content, duration, 'loading', onClose);
|
||||
},
|
||||
config (options) {
|
||||
if (options.top) {
|
||||
top = options.top;
|
||||
}
|
||||
if (options.duration) {
|
||||
defaultDuration = options.duration;
|
||||
}
|
||||
},
|
||||
destroy () {
|
||||
let instance = getMessageInstance();
|
||||
messageInstance = null;
|
||||
instance.destroy();
|
||||
}
|
||||
}
|
169
src/components/modal/confirm.js
Normal file
169
src/components/modal/confirm.js
Normal file
|
@ -0,0 +1,169 @@
|
|||
import Vue from 'vue';
|
||||
import Modal from './modal.vue';
|
||||
import Icon from '../icon/icon.vue';
|
||||
import Button from '../button/button.vue';
|
||||
import { camelcaseToHyphen } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-modal-confirm';
|
||||
|
||||
Modal.newInstance = properties => {
|
||||
const _props = properties || {};
|
||||
|
||||
let props = '';
|
||||
Object.keys(_props).forEach(prop => {
|
||||
props += ' :' + camelcaseToHyphen(prop) + '=' + prop;
|
||||
});
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = `
|
||||
<Modal${props} :visible.sync="visible" :width="width">
|
||||
<div class="${prefixCls}">
|
||||
<div class="${prefixCls}-head">
|
||||
<div :class="iconTypeCls"><i :class="iconNameCls"></i></div>
|
||||
<div class="${prefixCls}-head-title">{{{ title }}}</div>
|
||||
</div>
|
||||
<div class="${prefixCls}-body">
|
||||
{{{ body }}}
|
||||
</div>
|
||||
<div class="${prefixCls}-footer">
|
||||
<Button type="ghost" size="large" v-if="showCancel" @click="cancel">{{ cancelText }}</Button>
|
||||
<Button type="primary" size="large" :loading="buttonLoading" @click="ok">{{ okText }}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
`;
|
||||
document.body.appendChild(div);
|
||||
|
||||
const modal = new Vue({
|
||||
el: div,
|
||||
components: { Modal, Button, Icon },
|
||||
data: Object.assign(_props, {
|
||||
visible: false,
|
||||
width: 416,
|
||||
title: '',
|
||||
body: '',
|
||||
iconType: '',
|
||||
iconName: '',
|
||||
okText: '确定',
|
||||
cancelText: '取消',
|
||||
showCancel: false,
|
||||
loading: false,
|
||||
buttonLoading: false
|
||||
}),
|
||||
computed: {
|
||||
iconTypeCls () {
|
||||
return [
|
||||
`${prefixCls}-head-icon`,
|
||||
`${prefixCls}-head-icon-${this.iconType}`
|
||||
]
|
||||
},
|
||||
iconNameCls () {
|
||||
return [
|
||||
'ivu-icon',
|
||||
`ivu-icon-${this.iconName}`
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cancel () {
|
||||
this.visible = false;
|
||||
this.buttonLoading = false;
|
||||
this.onCancel();
|
||||
this.remove();
|
||||
},
|
||||
ok () {
|
||||
if (this.loading) {
|
||||
this.buttonLoading = true;
|
||||
} else {
|
||||
this.visible = false;
|
||||
this.remove();
|
||||
}
|
||||
this.onOk();
|
||||
},
|
||||
remove () {
|
||||
setTimeout(() => {
|
||||
this.destroy();
|
||||
}, 300);
|
||||
},
|
||||
destroy () {
|
||||
this.$destroy();
|
||||
document.body.removeChild(div);
|
||||
this.onRemove();
|
||||
},
|
||||
onOk () {},
|
||||
onCancel () {},
|
||||
onRemove () {}
|
||||
}
|
||||
}).$children[0];
|
||||
|
||||
return {
|
||||
show (props) {
|
||||
modal.$parent.showCancel = props.showCancel;
|
||||
modal.$parent.iconType = props.icon;
|
||||
|
||||
switch (props.icon) {
|
||||
case 'info':
|
||||
modal.$parent.iconName = 'information-circled';
|
||||
break;
|
||||
case 'success':
|
||||
modal.$parent.iconName = 'checkmark-circled';
|
||||
break;
|
||||
case 'warning':
|
||||
modal.$parent.iconName = 'android-alert';
|
||||
break;
|
||||
case 'error':
|
||||
modal.$parent.iconName = 'close-circled';
|
||||
break;
|
||||
case 'confirm':
|
||||
modal.$parent.iconName = 'help-circled';
|
||||
break;
|
||||
}
|
||||
|
||||
if ('width' in props) {
|
||||
modal.$parent.width = props.width;
|
||||
}
|
||||
|
||||
if ('title' in props) {
|
||||
modal.$parent.title = props.title;
|
||||
}
|
||||
|
||||
if ('content' in props) {
|
||||
modal.$parent.body = props.content;
|
||||
}
|
||||
|
||||
if ('okText' in props) {
|
||||
modal.$parent.okText = props.okText;
|
||||
}
|
||||
|
||||
if ('cancelText' in props) {
|
||||
modal.$parent.cancelText = props.cancelText;
|
||||
}
|
||||
|
||||
if ('onCancel' in props) {
|
||||
modal.$parent.onCancel = props.onCancel;
|
||||
}
|
||||
|
||||
if ('onOk' in props) {
|
||||
modal.$parent.onOk = props.onOk;
|
||||
}
|
||||
|
||||
// async for ok
|
||||
if ('loading' in props) {
|
||||
modal.$parent.loading = props.loading;
|
||||
}
|
||||
|
||||
// notice when component destroy
|
||||
modal.$parent.onRemove = props.onRemove;
|
||||
|
||||
modal.visible = true;
|
||||
},
|
||||
remove () {
|
||||
modal.visible = false;
|
||||
modal.$parent.buttonLoading = false;
|
||||
modal.$parent.remove();
|
||||
},
|
||||
component: modal
|
||||
}
|
||||
};
|
||||
|
||||
export default Modal;
|
65
src/components/modal/index.js
Normal file
65
src/components/modal/index.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
import Modal from './confirm';
|
||||
|
||||
let modalInstance;
|
||||
|
||||
function getModalInstance () {
|
||||
modalInstance = modalInstance || Modal.newInstance({
|
||||
closable: false,
|
||||
maskClosable: false,
|
||||
footerHide: true
|
||||
});
|
||||
|
||||
return modalInstance;
|
||||
}
|
||||
|
||||
function confirm (options) {
|
||||
let instance = getModalInstance();
|
||||
|
||||
options.onRemove = function () {
|
||||
modalInstance = null;
|
||||
};
|
||||
|
||||
instance.show(options);
|
||||
}
|
||||
|
||||
Modal.info = function (props = {}) {
|
||||
props.icon = 'info';
|
||||
props.showCancel = false;
|
||||
return confirm(props);
|
||||
};
|
||||
|
||||
Modal.success = function (props = {}) {
|
||||
props.icon = 'success';
|
||||
props.showCancel = false;
|
||||
return confirm(props);
|
||||
};
|
||||
|
||||
Modal.warning = function (props = {}) {
|
||||
props.icon = 'warning';
|
||||
props.showCancel = false;
|
||||
return confirm(props);
|
||||
};
|
||||
|
||||
Modal.error = function (props = {}) {
|
||||
props.icon = 'error';
|
||||
props.showCancel = false;
|
||||
return confirm(props);
|
||||
};
|
||||
|
||||
Modal.confirm = function (props = {}) {
|
||||
props.icon = 'confirm';
|
||||
props.showCancel = true;
|
||||
return confirm(props);
|
||||
};
|
||||
|
||||
Modal.remove = function () {
|
||||
if (!modalInstance) { // at loading status, remove after Cancel
|
||||
return false;
|
||||
}
|
||||
|
||||
const instance = getModalInstance();
|
||||
|
||||
instance.remove();
|
||||
};
|
||||
|
||||
export default Modal;
|
205
src/components/modal/modal.vue
Normal file
205
src/components/modal/modal.vue
Normal file
|
@ -0,0 +1,205 @@
|
|||
<template>
|
||||
<div :class="wrapClasses">
|
||||
<div :class="maskClasses" v-show="visible" @click="mask" transition="fade"></div>
|
||||
<div :class="classes" :style="styles" v-show="visible" transition="ease">
|
||||
<div :class="[`${prefixCls}-content`]">
|
||||
<a :class="[`${prefixCls}-close`]" v-if="closable" @click="close">
|
||||
<slot name="close">
|
||||
<Icon type="ios-close-empty"></Icon>
|
||||
</slot>
|
||||
</a>
|
||||
<div :class="[`${prefixCls}-header`]" v-if="showHead" v-el:head><slot name="header"><p>{{ title }}</p></slot></div>
|
||||
<div :class="[`${prefixCls}-body`]"><slot></slot></div>
|
||||
<div :class="[`${prefixCls}-footer`]" v-if="!footerHide">
|
||||
<slot name="footer">
|
||||
<Button type="ghost" size="large" @click="cancel">{{ cancelText }}</Button>
|
||||
<Button type="primary" size="large" :loading="buttonLoading" @click="ok">{{ okText }}</Button>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Icon from '../icon';
|
||||
import Button from '../button';
|
||||
import { getScrollBarSize } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-modal';
|
||||
|
||||
export default {
|
||||
components: { Icon, Button },
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
closable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
maskClosable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
title: {
|
||||
type: String
|
||||
},
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: 520
|
||||
},
|
||||
okText: {
|
||||
type: String,
|
||||
default: '确定'
|
||||
},
|
||||
cancelText: {
|
||||
type: String,
|
||||
default: '取消'
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
style: {
|
||||
type: Object
|
||||
},
|
||||
className: {
|
||||
type: String
|
||||
},
|
||||
// for instance
|
||||
footerHide: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
prefixCls: prefixCls,
|
||||
wrapShow: false,
|
||||
showHead: true,
|
||||
buttonLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
wrapClasses () {
|
||||
return [
|
||||
`${prefixCls}-wrap`,
|
||||
{
|
||||
[`${prefixCls}-hidden`]: !this.wrapShow,
|
||||
[`${this.className}`]: !!this.className
|
||||
}
|
||||
]
|
||||
},
|
||||
maskClasses () {
|
||||
return `${prefixCls}-mask`;
|
||||
},
|
||||
classes () {
|
||||
return `${prefixCls}`;
|
||||
},
|
||||
styles () {
|
||||
let style = {};
|
||||
|
||||
const styleWidth = {
|
||||
width: `${this.width}px`
|
||||
};
|
||||
|
||||
const customStyle = !!this.style ? this.style : {};
|
||||
|
||||
Object.assign(style, styleWidth, customStyle);
|
||||
|
||||
return style;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.visible = false;
|
||||
this.$emit('on-cancel');
|
||||
},
|
||||
mask () {
|
||||
if (this.maskClosable) {
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
cancel () {
|
||||
this.close();
|
||||
},
|
||||
ok () {
|
||||
if (this.loading) {
|
||||
this.buttonLoading = true;
|
||||
} else {
|
||||
this.visible = false;
|
||||
}
|
||||
this.$emit('on-ok');
|
||||
},
|
||||
EscClose (e) {
|
||||
if (this.visible && this.closable) {
|
||||
if (e.keyCode === 27) {
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
},
|
||||
checkScrollBar () {
|
||||
let fullWindowWidth = window.innerWidth;
|
||||
if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8
|
||||
const documentElementRect = document.documentElement.getBoundingClientRect();
|
||||
fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left);
|
||||
}
|
||||
this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth;
|
||||
if (this.bodyIsOverflowing) {
|
||||
this.scrollBarWidth = getScrollBarSize();
|
||||
}
|
||||
},
|
||||
setScrollBar () {
|
||||
if (this.bodyIsOverflowing && this.scrollBarWidth !== undefined) {
|
||||
document.body.style.paddingRight = `${this.scrollBarWidth}px`;
|
||||
}
|
||||
},
|
||||
resetScrollBar () {
|
||||
document.body.style.paddingRight = '';
|
||||
},
|
||||
addScrollEffect () {
|
||||
this.checkScrollBar();
|
||||
this.setScrollBar();
|
||||
document.body.style.overflow = 'hidden';
|
||||
},
|
||||
removeScrollEffect() {
|
||||
document.body.style.overflow = '';
|
||||
this.resetScrollBar();
|
||||
}
|
||||
},
|
||||
ready () {
|
||||
if (this.visible) {
|
||||
this.wrapShow = true;
|
||||
}
|
||||
|
||||
let showHead = true;
|
||||
|
||||
if (this.$els.head.innerHTML == '<p></p>' && !this.title) {
|
||||
showHead = false;
|
||||
}
|
||||
|
||||
this.showHead = showHead;
|
||||
|
||||
// ESC close
|
||||
document.addEventListener('keydown', this.EscClose);
|
||||
},
|
||||
beforeDestroy () {
|
||||
document.removeEventListener('keydown', this.EscClose);
|
||||
},
|
||||
watch: {
|
||||
visible (val) {
|
||||
if (val === false) {
|
||||
this.buttonLoading = false;
|
||||
setTimeout(() => {
|
||||
this.wrapShow = false;
|
||||
}, 300);
|
||||
this.removeScrollEffect();
|
||||
} else {
|
||||
this.wrapShow = true;
|
||||
this.addScrollEffect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
115
src/components/notice/index.js
Normal file
115
src/components/notice/index.js
Normal file
|
@ -0,0 +1,115 @@
|
|||
import Notification from '../base/notification';
|
||||
|
||||
const prefixCls = 'ivu-notice';
|
||||
const iconPrefixCls = 'ivu-icon';
|
||||
const prefixKey = 'ivu_notice_key_';
|
||||
|
||||
let top = 24;
|
||||
let defaultDuration = 4.5;
|
||||
let noticeInstance;
|
||||
let key = 1;
|
||||
|
||||
const iconTypes = {
|
||||
'info': 'information-circled',
|
||||
'success': 'checkmark-circled',
|
||||
'warning': 'android-alert',
|
||||
'error': 'close-circled'
|
||||
};
|
||||
|
||||
function getNoticeInstance () {
|
||||
noticeInstance = noticeInstance || Notification.newInstance({
|
||||
prefixCls: prefixCls,
|
||||
style: {
|
||||
top: `${top}px`,
|
||||
right: 0
|
||||
}
|
||||
});
|
||||
|
||||
return noticeInstance;
|
||||
}
|
||||
|
||||
function notice (type, options) {
|
||||
const title = options.title || '';
|
||||
const desc = options.desc || '';
|
||||
const noticeKey = options.key || `${prefixKey}${key}`;
|
||||
const onClose = options.onClose || function () {};
|
||||
// todo const btn = options.btn || null;
|
||||
const duration = (options.duration === 0) ? 0 : options.duration || defaultDuration;
|
||||
|
||||
key++;
|
||||
|
||||
let instance = getNoticeInstance();
|
||||
|
||||
let content;
|
||||
|
||||
if (type == 'normal') {
|
||||
content = `
|
||||
<div class="${prefixCls}-custom-content">
|
||||
<div class="${prefixCls}-title">${title}</div>
|
||||
<div class="${prefixCls}-desc">${desc}</div>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
const iconType = iconTypes[type];
|
||||
content = `
|
||||
<div class="${prefixCls}-custom-content ${prefixCls}-with-icon">
|
||||
<span class="${prefixCls}-icon ${prefixCls}-icon-${type}">
|
||||
<i class="${iconPrefixCls} ${iconPrefixCls}-${iconType}"></i>
|
||||
</span>
|
||||
<div class="${prefixCls}-title">${title}</div>
|
||||
<div class="${prefixCls}-desc">${desc}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
instance.notice({
|
||||
key: noticeKey.toString(),
|
||||
duration: duration,
|
||||
style: {},
|
||||
transitionName: 'move-right',
|
||||
content: content,
|
||||
onClose: onClose,
|
||||
closable: true
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
open (options) {
|
||||
return notice('normal', options);
|
||||
},
|
||||
info (options) {
|
||||
return notice('info', options);
|
||||
},
|
||||
success (options) {
|
||||
return notice('success', options);
|
||||
},
|
||||
warning (options) {
|
||||
return notice('warning', options);
|
||||
},
|
||||
error (options) {
|
||||
return notice('error', options);
|
||||
},
|
||||
config (options) {
|
||||
if (options.top) {
|
||||
top = options.top;
|
||||
}
|
||||
if (options.duration || options.duration === 0) {
|
||||
defaultDuration = options.duration;
|
||||
}
|
||||
},
|
||||
close (key) {
|
||||
if (key) {
|
||||
key = key.toString();
|
||||
if (noticeInstance) {
|
||||
noticeInstance.remove(key);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
destroy () {
|
||||
let instance = getNoticeInstance();
|
||||
noticeInstance = null;
|
||||
instance.destroy();
|
||||
}
|
||||
}
|
2
src/components/page/index.js
Normal file
2
src/components/page/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import Page from './page.vue';
|
||||
export default Page;
|
87
src/components/page/options.vue
Normal file
87
src/components/page/options.vue
Normal file
|
@ -0,0 +1,87 @@
|
|||
<template>
|
||||
<div v-if="showSizer || showElevator" :class="optsClasses">
|
||||
<div v-if="showSizer" :class="sizerClasses">
|
||||
<i-select :model.sync="pageSize" :size="size" @on-change="changeSize">
|
||||
<i-option v-for="item in pageSizeOpts" :value="item" style="text-align:center;">{{ item }} 条/页</i-option>
|
||||
</i-select>
|
||||
</div>
|
||||
<div v-if="showElevator" :class="ElevatorClasses">
|
||||
跳至
|
||||
<input type="text" :value="_current" @keyup.enter="changePage">
|
||||
页
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import iSelect from '../../components/select/select.vue';
|
||||
import iOption from '../../components/select/option.vue';
|
||||
|
||||
const prefixCls = 'ivu-page';
|
||||
|
||||
function isValueNumber (value) {
|
||||
return (/^[1-9][0-9]*$/).test(value + '');
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { iSelect, iOption },
|
||||
props: {
|
||||
pageSizeOpts: Array,
|
||||
showSizer: Boolean,
|
||||
showElevator: Boolean,
|
||||
current: Number,
|
||||
_current: Number,
|
||||
pageSize: Number,
|
||||
allPages: Number,
|
||||
isSmall: Boolean
|
||||
},
|
||||
computed: {
|
||||
size () {
|
||||
return this.isSmall ? 'small' : 'default';
|
||||
},
|
||||
optsClasses () {
|
||||
return [
|
||||
`${prefixCls}-options`
|
||||
]
|
||||
},
|
||||
sizerClasses () {
|
||||
return [
|
||||
`${prefixCls}-options-sizer`
|
||||
]
|
||||
},
|
||||
ElevatorClasses () {
|
||||
return [
|
||||
`${prefixCls}-options-elevator`
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeSize () {
|
||||
this.$emit('on-size', this.pageSize);
|
||||
},
|
||||
changePage (event) {
|
||||
let val = event.target.value.trim();
|
||||
let page = 0;
|
||||
|
||||
if (isValueNumber(val)) {
|
||||
val = Number(val);
|
||||
if (val != this.current) {
|
||||
const allPages = this.allPages;
|
||||
|
||||
if (val > allPages) {
|
||||
page = allPages;
|
||||
} else {
|
||||
page = val;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
if (page) {
|
||||
this.$emit('on-page', page);
|
||||
event.target.value = page;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
238
src/components/page/page.vue
Normal file
238
src/components/page/page.vue
Normal file
|
@ -0,0 +1,238 @@
|
|||
<template>
|
||||
<ul :class="simpleWrapClasses" v-if="simple">
|
||||
<li
|
||||
title="上一页"
|
||||
:class="prevClasses"
|
||||
@click="prev">
|
||||
<a><i class="ivu-icon ivu-icon-ios-arrow-left"></i></a>
|
||||
</li>
|
||||
<div :class="simplePagerClasses" :title="current + '/' + allPages">
|
||||
<input
|
||||
type="text"
|
||||
:value="current"
|
||||
@keydown="keyDown"
|
||||
@keyup="keyUp"
|
||||
@change="keyUp">
|
||||
<span>/</span>
|
||||
{{ allPages }}
|
||||
</div>
|
||||
<li
|
||||
title="下一页"
|
||||
:class="nextClasses"
|
||||
@click="next">
|
||||
<a><i class="ivu-icon ivu-icon-ios-arrow-right"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul :class="wrapClasses" v-else>
|
||||
<span :class="[`${prefixCls}-total`]" v-if="showTotal">
|
||||
<slot>共 {{ total }} 条</slot>
|
||||
</span>
|
||||
<li
|
||||
title="上一页"
|
||||
:class="prevClasses"
|
||||
@click="prev">
|
||||
<a><i class="ivu-icon ivu-icon-ios-arrow-left"></i></a>
|
||||
</li>
|
||||
<li title="第一页" :class="[`${prefixCls}-item`,{[`${prefixCls}-item-active`]: current == 1}]" @click="changePage(1)"><a>1</a></li>
|
||||
<li title="向前 5 页" v-if="current - 3 > 1" :class="[`${prefixCls}-item-jump-prev`]" @click="fastPrev"><a><i class="ivu-icon ivu-icon-ios-arrow-left"></i></a></li>
|
||||
<li :title="current - 2" v-if="current - 2 > 1" :class="[`${prefixCls}-item`]" @click="changePage(current - 2)"><a>{{ current - 2 }}</a></li>
|
||||
<li :title="current - 1" v-if="current - 1 > 1" :class="[`${prefixCls}-item`]" @click="changePage(current - 1)"><a>{{ current - 1 }}</a></li>
|
||||
<li :title="current" v-if="current != 1 && current != allPages" :class="[`${prefixCls}-item`,`${prefixCls}-item-active`]"><a>{{ current }}</a></li>
|
||||
<li :title="current + 1" v-if="current + 1 < allPages" :class="[`${prefixCls}-item`]" @click="changePage(current + 1)"><a>{{ current + 1 }}</a></li>
|
||||
<li :title="current + 2" v-if="current + 2 < allPages" :class="[`${prefixCls}-item`]" @click="changePage(current + 2)"><a>{{ current + 2 }}</a></li>
|
||||
<li title="向后 5 页" v-if="current + 3 < allPages" :class="[`${prefixCls}-item-jump-next`]" @click="fastNext"><a><i class="ivu-icon ivu-icon-ios-arrow-right"></i></a></li>
|
||||
<li :title="'最后一页:' + allPages" v-if="allPages > 1" :class="[`${prefixCls}-item`, {[`${prefixCls}-item-active`]: current == allPages}]" @click="changePage(allPages)"><a>{{ allPages }}</a></li>
|
||||
<li
|
||||
title="下一页"
|
||||
:class="nextClasses"
|
||||
@click="next">
|
||||
<a><i class="ivu-icon ivu-icon-ios-arrow-right"></i></a>
|
||||
</li>
|
||||
<Options
|
||||
:show-sizer="showSizer"
|
||||
:page-size="pageSize"
|
||||
:page-size-opts="pageSizeOpts"
|
||||
:show-elevator="showElevator"
|
||||
:_current.once="current"
|
||||
:current.sync="current"
|
||||
:all-pages="allPages"
|
||||
:is-small="isSmall"
|
||||
@on-size="onSize"
|
||||
@on-page="onPage">
|
||||
</Options>
|
||||
</ul>
|
||||
</template>
|
||||
<script>
|
||||
import { oneOf } from '../../utils/assist';
|
||||
import Options from './options.vue';
|
||||
|
||||
const prefixCls = 'ivu-page';
|
||||
|
||||
export default {
|
||||
components: { Options },
|
||||
props: {
|
||||
current: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
pageSize: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
pageSizeOpts: {
|
||||
type: Array,
|
||||
default () {
|
||||
return [10, 20, 30, 40]
|
||||
}
|
||||
},
|
||||
size: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['small']);
|
||||
}
|
||||
},
|
||||
simple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showTotal: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showElevator: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showSizer: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
prefixCls: prefixCls
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isSmall () {
|
||||
return !!this.size;
|
||||
},
|
||||
allPages () {
|
||||
const allPage = Math.ceil(this.total / this.pageSize);
|
||||
return (allPage === 0) ? 1 : allPage;
|
||||
},
|
||||
simpleWrapClasses () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
`${prefixCls}-simple`
|
||||
]
|
||||
},
|
||||
simplePagerClasses () {
|
||||
return `${prefixCls}-simple-pager`;
|
||||
},
|
||||
wrapClasses () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
'mini': !!this.size
|
||||
}
|
||||
]
|
||||
},
|
||||
prevClasses () {
|
||||
return [
|
||||
`${prefixCls}-prev`,
|
||||
{
|
||||
[`${prefixCls}-disabled`]: this.current == 1
|
||||
}
|
||||
]
|
||||
},
|
||||
nextClasses () {
|
||||
return [
|
||||
`${prefixCls}-next`,
|
||||
{
|
||||
[`${prefixCls}-disabled`]: this.current == this.allPages
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changePage (page) {
|
||||
if (this.current != page) {
|
||||
this.current = page;
|
||||
this.$emit('on-change', page);
|
||||
}
|
||||
},
|
||||
prev () {
|
||||
const current = this.current;
|
||||
if (current <= 1) {
|
||||
return false;
|
||||
}
|
||||
this.changePage(current - 1);
|
||||
},
|
||||
next () {
|
||||
const current = this.current;
|
||||
if (current >= this.allPages) {
|
||||
return false;
|
||||
}
|
||||
this.changePage(current + 1);
|
||||
},
|
||||
fastPrev () {
|
||||
const page = this.current - 5;
|
||||
if (page > 0) {
|
||||
this.changePage(page);
|
||||
} else {
|
||||
this.changePage(1);
|
||||
}
|
||||
},
|
||||
fastNext () {
|
||||
const page = this.current + 5;
|
||||
if (page > this.allPages) {
|
||||
this.changePage(this.allPages);
|
||||
} else {
|
||||
this.changePage(page);
|
||||
}
|
||||
},
|
||||
onSize (pageSize) {
|
||||
this.pageSize = pageSize;
|
||||
this.changePage(1);
|
||||
},
|
||||
onPage (page) {
|
||||
this.changePage(page);
|
||||
},
|
||||
keyDown (e) {
|
||||
const key = e.keyCode;
|
||||
const condition = (key >= 48 && key <= 57) || key == 8 || key == 37 || key == 39;
|
||||
|
||||
if (!condition) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
keyUp (e) {
|
||||
const key = e.keyCode;
|
||||
const val = parseInt(e.target.value);
|
||||
|
||||
if (key === 38) {
|
||||
this.prev()
|
||||
} else if (key === 40) {
|
||||
this.next()
|
||||
} else if (key == 13) {
|
||||
let page = 1;
|
||||
|
||||
if (val > this.allPages) {
|
||||
page = this.allPages;
|
||||
} else if (val <= 0) {
|
||||
page = 1;
|
||||
} else {
|
||||
page = val;
|
||||
}
|
||||
|
||||
e.target.value = page;
|
||||
this.changePage(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
3
src/components/poptip/index.js
Normal file
3
src/components/poptip/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import Poptip from './poptip.vue';
|
||||
|
||||
export default Poptip;
|
170
src/components/poptip/poptip.vue
Normal file
170
src/components/poptip/poptip.vue
Normal file
|
@ -0,0 +1,170 @@
|
|||
<template>
|
||||
<div
|
||||
:class="classes"
|
||||
@mouseenter="handleMouseenter"
|
||||
@mouseleave="handleMouseleave"
|
||||
v-clickoutside="handleClose">
|
||||
<div
|
||||
:class="[`${prefixCls}-rel`]"
|
||||
v-el:reference
|
||||
@click="handleClick"
|
||||
@mousedown="handleFocus"
|
||||
@mouseup="handleBlur">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div :class="[`${prefixCls}-popper`]" :style="styles" transition="fade" v-el:popper v-show="visible">
|
||||
<div :class="[`${prefixCls}-content`]">
|
||||
<div :class="[`${prefixCls}-arrow`]"></div>
|
||||
<div :class="[`${prefixCls}-inner`]" v-if="confirm">
|
||||
<div :class="[`${prefixCls}-body`]">
|
||||
<i class="ivu-icon ivu-icon-help-circled"></i>
|
||||
<div :class="[`${prefixCls}-body-message`]"><slot name="title">{{ title }}</slot></div>
|
||||
</div>
|
||||
<div :class="[`${prefixCls}-footer`]">
|
||||
<Button type="ghost" size="small" @click="cancel">{{ cancelText }}</Button>
|
||||
<Button type="primary" size="small" @click="ok">{{ okText }}</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="[`${prefixCls}-inner`]" v-if="!confirm">
|
||||
<div :class="[`${prefixCls}-title`]" v-if="showTitle" v-el:title><slot name="title">{{ title }}</slot></div>
|
||||
<div :class="[`${prefixCls}-body`]">
|
||||
<div :class="[`${prefixCls}-body-content`]"><slot name="content">{{ content }}</slot></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Popper from '../base/popper';
|
||||
import Button from '../button/button.vue';
|
||||
import clickoutside from '../../directives/clickoutside';
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-poptip';
|
||||
|
||||
export default {
|
||||
mixins: [Popper],
|
||||
directives: { clickoutside },
|
||||
components: { Button },
|
||||
props: {
|
||||
trigger: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['click', 'focus', 'hover']);
|
||||
},
|
||||
default: 'click'
|
||||
},
|
||||
placement: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end']);
|
||||
},
|
||||
default: 'top'
|
||||
},
|
||||
title: {
|
||||
type: [String, Number]
|
||||
},
|
||||
content: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
width: {
|
||||
type: [String, Number]
|
||||
},
|
||||
confirm: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
okText: {
|
||||
type: String,
|
||||
default: '确定'
|
||||
},
|
||||
cancelText: {
|
||||
type: String,
|
||||
default: '取消'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
prefixCls: prefixCls,
|
||||
showTitle: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-confirm`]: this.confirm
|
||||
}
|
||||
]
|
||||
},
|
||||
styles () {
|
||||
let style = {};
|
||||
|
||||
if (!!this.width) {
|
||||
style.width = `${this.width}px`;
|
||||
}
|
||||
return style;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick () {
|
||||
if (this.confirm) {
|
||||
this.visible = !this.visible;
|
||||
return true;
|
||||
}
|
||||
if (this.trigger !== 'click') {
|
||||
return false;
|
||||
}
|
||||
this.visible = !this.visible;
|
||||
},
|
||||
handleClose () {
|
||||
if (this.confirm) {
|
||||
this.visible = false;
|
||||
return true;
|
||||
}
|
||||
if (this.trigger !== 'click') {
|
||||
return false;
|
||||
}
|
||||
this.visible = false;
|
||||
},
|
||||
handleFocus () {
|
||||
if (this.trigger !== 'focus' || this.confirm) {
|
||||
return false;
|
||||
}
|
||||
this.visible = true;
|
||||
},
|
||||
handleBlur () {
|
||||
if (this.trigger !== 'focus' || this.confirm) {
|
||||
return false;
|
||||
}
|
||||
this.visible = false;
|
||||
},
|
||||
handleMouseenter () {
|
||||
if (this.trigger !== 'hover' || this.confirm) {
|
||||
return false;
|
||||
}
|
||||
this.visible = true;
|
||||
},
|
||||
handleMouseleave () {
|
||||
if (this.trigger !== 'hover' || this.confirm) {
|
||||
return false;
|
||||
}
|
||||
this.visible = false;
|
||||
},
|
||||
cancel () {
|
||||
this.visible = false;
|
||||
this.$emit('on-cancel');
|
||||
},
|
||||
ok () {
|
||||
this.visible = false;
|
||||
this.$emit('on-ok');
|
||||
}
|
||||
},
|
||||
ready () {
|
||||
if (!this.confirm) {
|
||||
this.showTitle = this.$els.title.innerHTML != '';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
2
src/components/progress/index.js
Normal file
2
src/components/progress/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import Progress from './progress.vue';
|
||||
export default Progress;
|
121
src/components/progress/progress.vue
Normal file
121
src/components/progress/progress.vue
Normal file
|
@ -0,0 +1,121 @@
|
|||
<template>
|
||||
<div :class="wrapClasses">
|
||||
<div :class="outerClasses">
|
||||
<div :class="innerClasses">
|
||||
<div :class="bgClasses" :style="bgStyle"></div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-if="!hideInfo" :class="textClasses">
|
||||
<slot>
|
||||
<span v-if="isStatus" :class="textInnerClasses">
|
||||
<Icon :type="statusIcon"></Icon>
|
||||
</span>
|
||||
<span v-else :class="textInnerClasses">
|
||||
{{ percent }}%
|
||||
</span>
|
||||
</slot>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Icon from '../icon';
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-progress';
|
||||
|
||||
export default {
|
||||
components: { Icon },
|
||||
props: {
|
||||
percent: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
status: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['normal', 'active', 'wrong', 'success']);
|
||||
},
|
||||
default: 'normal'
|
||||
},
|
||||
hideInfo: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
strokeWidth: {
|
||||
type: Number,
|
||||
default: 10
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isStatus () {
|
||||
return this.status == 'wrong' || this.status == 'success';
|
||||
},
|
||||
statusIcon () {
|
||||
let type = '';
|
||||
switch (this.status) {
|
||||
case 'wrong':
|
||||
type = 'ios-close';
|
||||
break;
|
||||
case 'success':
|
||||
type = 'ios-checkmark';
|
||||
break;
|
||||
}
|
||||
|
||||
return type;
|
||||
},
|
||||
bgStyle () {
|
||||
return {
|
||||
width: `${this.percent}%`,
|
||||
height: `${this.strokeWidth}px`
|
||||
}
|
||||
},
|
||||
wrapClasses () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
`${prefixCls}-${this.status}`,
|
||||
{
|
||||
[`${prefixCls}-show-info`]: !this.hideInfo,
|
||||
|
||||
}
|
||||
]
|
||||
},
|
||||
textClasses () {
|
||||
return `${prefixCls}-text`;
|
||||
},
|
||||
textInnerClasses () {
|
||||
return `${prefixCls}-text-inner`;
|
||||
},
|
||||
outerClasses () {
|
||||
return `${prefixCls}-outer`;
|
||||
},
|
||||
innerClasses () {
|
||||
return `${prefixCls}-inner`;
|
||||
},
|
||||
bgClasses () {
|
||||
return `${prefixCls}-bg`;
|
||||
}
|
||||
},
|
||||
compiled () {
|
||||
this.handleStatus();
|
||||
},
|
||||
methods: {
|
||||
handleStatus (isDown) {
|
||||
if (isDown) {
|
||||
this.status = 'normal';
|
||||
} else {
|
||||
if (parseInt(this.percent, 10) == 100) {
|
||||
this.status = 'success';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
percent (val, oldVal) {
|
||||
if (val < oldVal) {
|
||||
this.handleStatus(true);
|
||||
} else {
|
||||
this.handleStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
5
src/components/radio/index.js
Normal file
5
src/components/radio/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Radio from './radio.vue';
|
||||
import RadioGroup from './radio-group.vue';
|
||||
|
||||
Radio.Group = RadioGroup;
|
||||
export default Radio;
|
62
src/components/radio/radio-group.vue
Normal file
62
src/components/radio/radio-group.vue
Normal file
|
@ -0,0 +1,62 @@
|
|||
<template>
|
||||
<div :class="classes">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-radio-group';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
model: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
size: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['small', 'large']);
|
||||
}
|
||||
},
|
||||
type: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['button']);
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-${this.size}`]: !!this.size,
|
||||
[`${prefixCls}-${this.type}`]: !!this.type
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
compiled () {
|
||||
this.updateModel();
|
||||
},
|
||||
methods: {
|
||||
updateModel () {
|
||||
const model = this.model;
|
||||
this.$children.forEach((child) => {
|
||||
child.selected = model == child.value;
|
||||
child.group = true;
|
||||
});
|
||||
},
|
||||
change (data) {
|
||||
this.model = data.value;
|
||||
this.updateModel();
|
||||
this.$emit('on-change', data.value);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
model () {
|
||||
this.updateModel()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
96
src/components/radio/radio.vue
Normal file
96
src/components/radio/radio.vue
Normal file
|
@ -0,0 +1,96 @@
|
|||
<template>
|
||||
<label :class="wrapClasses">
|
||||
<span :class="radioClasses">
|
||||
<span :class="innerClasses"></span>
|
||||
<input
|
||||
type="radio"
|
||||
:class="inputClasses"
|
||||
:disabled="disabled"
|
||||
:checked="selected"
|
||||
@change="change">
|
||||
</span>
|
||||
<slot>{{ value }}</slot>
|
||||
</label>
|
||||
</template>
|
||||
<script>
|
||||
const prefixCls = 'ivu-radio';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
checked: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
type: [String, Number]
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
selected: false,
|
||||
group: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
wrapClasses () {
|
||||
return [
|
||||
`${prefixCls}-wrapper`,
|
||||
{
|
||||
[`${prefixCls}-group-item`]: this.group,
|
||||
[`${prefixCls}-wrapper-checked`]: this.selected,
|
||||
[`${prefixCls}-wrapper-disabled`]: this.disabled
|
||||
}
|
||||
]
|
||||
},
|
||||
radioClasses () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-checked`]: this.selected,
|
||||
[`${prefixCls}-disabled`]: this.disabled
|
||||
}
|
||||
]
|
||||
},
|
||||
innerClasses () {
|
||||
return `${prefixCls}-inner`;
|
||||
},
|
||||
inputClasses () {
|
||||
return `${prefixCls}-input`;
|
||||
}
|
||||
},
|
||||
ready () {
|
||||
if (!this.group) {
|
||||
this.updateModel();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
change (event) {
|
||||
if (this.disabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.selected = event.target.checked;
|
||||
this.checked = this.selected;
|
||||
|
||||
if (this.group && this.checked) {
|
||||
this.$parent.change({
|
||||
value: this.value,
|
||||
checked: this.checked
|
||||
});
|
||||
}
|
||||
},
|
||||
updateModel () {
|
||||
this.selected = this.checked;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
checked () {
|
||||
this.updateModel();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
59
src/components/select/dropdown.vue
Normal file
59
src/components/select/dropdown.vue
Normal file
|
@ -0,0 +1,59 @@
|
|||
<template>
|
||||
<div class="ivu-select-dropdown"><slot></slot></div>
|
||||
</template>
|
||||
<script>
|
||||
import Popper from 'popper.js';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
popper: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
update () {
|
||||
if (this.popper) {
|
||||
this.$nextTick(() => {
|
||||
this.popper.update();
|
||||
});
|
||||
} else {
|
||||
this.$nextTick(() => {
|
||||
this.popper = new Popper(this.$parent.$els.reference, this.$el, {
|
||||
gpuAcceleration: false,
|
||||
placement: 'bottom-start',
|
||||
boundariesPadding: 0,
|
||||
forceAbsolute: true
|
||||
});
|
||||
this.popper.onCreate(popper => {
|
||||
this.resetTransformOrigin(popper);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
destroy () {
|
||||
if (this.popper) {
|
||||
this.resetTransformOrigin(this.popper);
|
||||
setTimeout(() => {
|
||||
this.popper.destroy();
|
||||
this.popper = null;
|
||||
}, 300);
|
||||
}
|
||||
},
|
||||
resetTransformOrigin(popper) {
|
||||
let placementMap = {top: 'bottom', bottom: 'top'};
|
||||
let placement = popper._popper.getAttribute('x-placement').split('-')[0];
|
||||
let origin = placementMap[placement];
|
||||
popper._popper.style.transformOrigin = `center ${ origin }`;
|
||||
}
|
||||
},
|
||||
ready () {
|
||||
this.$on('on-update-popper', this.update);
|
||||
this.$on('on-destroy-popper', this.destroy);
|
||||
},
|
||||
beforeDestroy () {
|
||||
if (this.popper) {
|
||||
this.popper.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
5
src/components/select/index.js
Normal file
5
src/components/select/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Select from './select.vue';
|
||||
import Option from './option.vue';
|
||||
import OptionGroup from './option-group.vue';
|
||||
|
||||
export { Select, Option, OptionGroup };
|
25
src/components/select/option-group.vue
Normal file
25
src/components/select/option-group.vue
Normal file
|
@ -0,0 +1,25 @@
|
|||
<template>
|
||||
<li :class="[`${prefixCls}-wrap`]">
|
||||
<div :class="[`${prefixCls}-title`]">{{ label }}</div>
|
||||
<ul>
|
||||
<li :class="[`${prefixCls}`]"><slot></slot></li>
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
<script>
|
||||
const prefixCls = 'ivu-select-group';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
prefixCls: prefixCls
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
73
src/components/select/option.vue
Normal file
73
src/components/select/option.vue
Normal file
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<li :class="classes" @click.stop="select" @mouseout.stop="blur" v-show="!hidden"><slot>{{ showLabel }}</slot></li>
|
||||
</template>
|
||||
<script>
|
||||
const prefixCls = 'ivu-select-item';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Number],
|
||||
required: true
|
||||
},
|
||||
label: {
|
||||
type: [String, Number]
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
componentName: 'select-item',
|
||||
data () {
|
||||
return {
|
||||
selected: false,
|
||||
index: 0, // for up and down to focus
|
||||
isFocus: false,
|
||||
hidden: false, // for search
|
||||
searchLabel: '' // the value is slot,only for search
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-disabled`]: this.disabled,
|
||||
[`${prefixCls}-selected`]: this.selected,
|
||||
[`${prefixCls}-focus`]: this.isFocus
|
||||
}
|
||||
]
|
||||
},
|
||||
showLabel () {
|
||||
return (!!this.label) ? this.label : this.value;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
select () {
|
||||
if (this.disabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.$dispatch('on-select-selected', this.value);
|
||||
},
|
||||
blur () {
|
||||
this.isFocus = false;
|
||||
},
|
||||
queryChange (val) {
|
||||
this.hidden = !new RegExp(val, 'i').test(this.searchLabel);
|
||||
}
|
||||
},
|
||||
ready () {
|
||||
this.searchLabel = this.$el.innerHTML;
|
||||
},
|
||||
events: {
|
||||
'on-select-close' () {
|
||||
this.isFocus = false;
|
||||
},
|
||||
'on-query-change' (val) {
|
||||
this.queryChange(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
511
src/components/select/select.vue
Normal file
511
src/components/select/select.vue
Normal file
|
@ -0,0 +1,511 @@
|
|||
<template>
|
||||
<div :class="classes" v-clickoutside="handleClose">
|
||||
<div
|
||||
:class="[`${prefixCls}-selection`]"
|
||||
v-el:reference
|
||||
@click="toggleMenu">
|
||||
<div class="ivu-tag" v-for="item in selectedMultiple">
|
||||
<span class="ivu-tag-text">{{ item.label }}</span>
|
||||
<Icon type="ios-close-empty" @click.stop="removeTag($index)"></Icon>
|
||||
</div>
|
||||
<span :class="[`${prefixCls}-placeholder`]" v-show="showPlaceholder && !filterable">{{ placeholder }}</span>
|
||||
<span :class="[`${prefixCls}-selected-value`]" v-show="!showPlaceholder && !multiple && !filterable">{{ selectedSingle }}</span>
|
||||
<input
|
||||
type="text"
|
||||
v-if="filterable"
|
||||
v-model="query"
|
||||
:class="[`${prefixCls}-input`]"
|
||||
:placeholder="showPlaceholder ? placeholder : ''"
|
||||
:style="inputStyle"
|
||||
@blur="handleBlur"
|
||||
@keydown="resetInputState"
|
||||
@keydown.delete="handleInputDelete"
|
||||
v-el:input>
|
||||
<Icon type="ios-close" :class="[`${prefixCls}-arrow`]" v-show="showCloseIcon" @click.stop="clearSingleSelect"></Icon>
|
||||
<Icon type="arrow-down-b" :class="[`${prefixCls}-arrow`]"></Icon>
|
||||
</div>
|
||||
<Dropdown v-show="visible" transition="slide-up" v-ref:dropdown>
|
||||
<ul v-show="notFound" :class="[`${prefixCls}-not-found`]"><li>{{ notFoundText }}</li></ul>
|
||||
<ul v-else :class="[`${prefixCls}-dropdown-list`]"><slot></slot></ul>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Icon from '../icon';
|
||||
import Dropdown from './dropdown.vue';
|
||||
import clickoutside from '../../directives/clickoutside';
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-select';
|
||||
|
||||
export default {
|
||||
components: { Icon, Dropdown },
|
||||
directives: { clickoutside },
|
||||
props: {
|
||||
model: {
|
||||
type: [String, Number, Array],
|
||||
default: ''
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择'
|
||||
},
|
||||
filterable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
filterMethod: {
|
||||
type: Function
|
||||
},
|
||||
size: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['small', 'large', 'default']);
|
||||
}
|
||||
},
|
||||
labelInValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
notFoundText: {
|
||||
type: String,
|
||||
default: '无匹配数据'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
prefixCls: prefixCls,
|
||||
visible: false,
|
||||
options: [],
|
||||
optionInstances: [],
|
||||
selectedSingle: '', // label
|
||||
selectedMultiple: [],
|
||||
focusIndex: 0,
|
||||
query: '',
|
||||
inputLength: 20,
|
||||
notFound: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-visible`]: this.visible,
|
||||
[`${prefixCls}-disabled`]: this.disabled,
|
||||
[`${prefixCls}-multiple`]: this.multiple,
|
||||
[`${prefixCls}-single`]: !this.multiple,
|
||||
[`${prefixCls}-show-clear`]: this.showCloseIcon,
|
||||
[`${prefixCls}-${this.size}`]: !!this.size
|
||||
}
|
||||
]
|
||||
},
|
||||
showPlaceholder () {
|
||||
let status = false;
|
||||
|
||||
if ((typeof this.model) === 'string') {
|
||||
if (this.model === '') {
|
||||
status = true;
|
||||
}
|
||||
} else if (Array.isArray(this.model)) {
|
||||
if (!this.model.length) {
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
},
|
||||
showCloseIcon () {
|
||||
return !this.multiple && this.clearable && !this.showPlaceholder;
|
||||
},
|
||||
inputStyle () {
|
||||
let style = {};
|
||||
|
||||
if (this.multiple) {
|
||||
if (this.showPlaceholder) {
|
||||
style.width = '100%';
|
||||
} else {
|
||||
style.width = `${this.inputLength}px`;
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleMenu () {
|
||||
if (this.disabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.visible = !this.visible;
|
||||
},
|
||||
hideMenu () {
|
||||
this.visible = false;
|
||||
this.focusIndex = 0;
|
||||
this.$broadcast('on-select-close');
|
||||
},
|
||||
// find option component
|
||||
findChild (cb) {
|
||||
const find = function (child) {
|
||||
const name = child.$options.componentName;
|
||||
|
||||
if (name) {
|
||||
cb(child);
|
||||
} else if (child.$children.length) {
|
||||
child.$children.forEach((innerChild) => {
|
||||
find(innerChild, cb);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (this.optionInstances.length) {
|
||||
this.optionInstances.forEach((child) => {
|
||||
find(child);
|
||||
})
|
||||
} else {
|
||||
this.$children.forEach((child) => {
|
||||
find(child);
|
||||
});
|
||||
}
|
||||
},
|
||||
updateOptions (init) {
|
||||
let options = [];
|
||||
let index = 1;
|
||||
|
||||
this.findChild((child) => {
|
||||
options.push({
|
||||
value: child.value,
|
||||
label: (child.label === undefined) ? child.$el.innerHTML : child.label
|
||||
});
|
||||
child.index = index++;
|
||||
|
||||
if (init) {
|
||||
this.optionInstances.push(child);
|
||||
}
|
||||
});
|
||||
|
||||
this.options = options;
|
||||
|
||||
if (init) {
|
||||
this.updateSingleSelected(true);
|
||||
this.updateMultipleSelected(true);
|
||||
}
|
||||
},
|
||||
updateSingleSelected (init = false) {
|
||||
const type = typeof this.model;
|
||||
|
||||
if (type === 'string' || type === 'number') {
|
||||
for (let i = 0; i < this.options.length; i++) {
|
||||
if (this.model === this.options[i].value) {
|
||||
this.selectedSingle = this.options[i].label;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.toggleSingleSelected(this.model, init);
|
||||
},
|
||||
clearSingleSelect () {
|
||||
if (this.showCloseIcon) {
|
||||
this.findChild((child) => {
|
||||
child.selected = false;
|
||||
});
|
||||
this.model = '';
|
||||
|
||||
if (this.filterable) {
|
||||
this.query = '';
|
||||
}
|
||||
}
|
||||
},
|
||||
updateMultipleSelected (init = false) {
|
||||
if (this.multiple && Array.isArray(this.model)) {
|
||||
let selected = [];
|
||||
|
||||
for (let i = 0; i < this.model.length; i++) {
|
||||
const model = this.model[i];
|
||||
|
||||
for (let j = 0; j < this.options.length; j++) {
|
||||
const option = this.options[j];
|
||||
|
||||
if (model === option.value) {
|
||||
selected.push({
|
||||
value: option.value,
|
||||
label: option.label
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.selectedMultiple = selected;
|
||||
}
|
||||
|
||||
this.toggleMultipleSelected(this.model, init);
|
||||
},
|
||||
removeTag (index) {
|
||||
if (this.disabled) {
|
||||
return false;
|
||||
}
|
||||
this.model.splice(index, 1);
|
||||
|
||||
if (this.filterable && this.visible) {
|
||||
this.$els.input.focus();
|
||||
}
|
||||
|
||||
this.$broadcast('on-update-popper');
|
||||
},
|
||||
// to select option for single
|
||||
toggleSingleSelected (value, init = false) {
|
||||
if (!this.multiple) {
|
||||
let label = '';
|
||||
|
||||
this.findChild((child) => {
|
||||
if (child.value === value) {
|
||||
child.selected = true;
|
||||
label = (child.label === undefined) ? child.$el.innerHTML : child.label;
|
||||
} else {
|
||||
child.selected = false;
|
||||
}
|
||||
});
|
||||
|
||||
this.hideMenu();
|
||||
|
||||
if (!init) {
|
||||
if (this.labelInValue) {
|
||||
this.$emit('on-change', {
|
||||
value: value,
|
||||
label: label
|
||||
});
|
||||
} else {
|
||||
this.$emit('on-change', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// to select option for multiple
|
||||
toggleMultipleSelected (value, init = false) {
|
||||
if (this.multiple) {
|
||||
let hybridValue = [];
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
hybridValue.push({
|
||||
value: value[i]
|
||||
});
|
||||
}
|
||||
|
||||
this.findChild((child) => {
|
||||
const index = value.indexOf(child.value);
|
||||
|
||||
if (index >= 0) {
|
||||
child.selected = true;
|
||||
hybridValue[index].label = (child.label === undefined) ? child.$el.innerHTML : child.label;
|
||||
} else {
|
||||
child.selected = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (!init) {
|
||||
if (this.labelInValue) {
|
||||
this.$emit('on-change', hybridValue);
|
||||
} else {
|
||||
this.$emit('on-change', value);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
handleClose () {
|
||||
this.hideMenu();
|
||||
},
|
||||
handleKeydown (e) {
|
||||
if (this.visible) {
|
||||
const keyCode = e.keyCode;
|
||||
// Esc slide-up
|
||||
if (keyCode === 27) {
|
||||
e.preventDefault();
|
||||
this.hideMenu();
|
||||
}
|
||||
// next
|
||||
if (keyCode === 40) {
|
||||
e.preventDefault();
|
||||
this.navigateOptions('next');
|
||||
}
|
||||
// prev
|
||||
if (keyCode === 38) {
|
||||
e.preventDefault();
|
||||
this.navigateOptions('prev');
|
||||
}
|
||||
// enter
|
||||
if (keyCode === 13) {
|
||||
e.preventDefault();
|
||||
|
||||
this.findChild((child) => {
|
||||
if (child.isFocus) {
|
||||
child.select();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
navigateOptions (direction) {
|
||||
if (direction === 'next') {
|
||||
const next = this.focusIndex + 1;
|
||||
this.focusIndex = (this.focusIndex === this.options.length) ? 1 : next;
|
||||
} else if (direction === 'prev') {
|
||||
const prev = this.focusIndex - 1;
|
||||
this.focusIndex = (this.focusIndex <= 1) ? this.options.length : prev;
|
||||
}
|
||||
|
||||
let child_status = {
|
||||
disabled: false,
|
||||
hidden: false
|
||||
};
|
||||
|
||||
let find_deep = false; // can next find allowed
|
||||
|
||||
this.findChild((child) => {
|
||||
if (child.index === this.focusIndex) {
|
||||
child_status.disabled = child.disabled;
|
||||
child_status.hidden = child.hidden;
|
||||
|
||||
if (!child.disabled && !child.hidden) {
|
||||
child.isFocus = true;
|
||||
}
|
||||
} else {
|
||||
child.isFocus = false;
|
||||
}
|
||||
|
||||
if (!child.hidden && !child.disabled) {
|
||||
find_deep = true;
|
||||
}
|
||||
});
|
||||
|
||||
this.resetScrollTop();
|
||||
|
||||
if ((child_status.disabled || child_status.hidden) && find_deep) {
|
||||
this.navigateOptions(direction);
|
||||
}
|
||||
},
|
||||
resetScrollTop () {
|
||||
const index = this.focusIndex - 1;
|
||||
let bottomOverflowDistance = this.optionInstances[index].$el.getBoundingClientRect().bottom - this.$refs.dropdown.$el.getBoundingClientRect().bottom;
|
||||
let topOverflowDistance = this.optionInstances[index].$el.getBoundingClientRect().top - this.$refs.dropdown.$el.getBoundingClientRect().top;
|
||||
|
||||
if (bottomOverflowDistance > 0) {
|
||||
this.$refs.dropdown.$el.scrollTop += bottomOverflowDistance;
|
||||
}
|
||||
if (topOverflowDistance < 0) {
|
||||
this.$refs.dropdown.$el.scrollTop += topOverflowDistance;
|
||||
}
|
||||
},
|
||||
handleBlur () {
|
||||
setTimeout(() => {
|
||||
const model = this.model;
|
||||
|
||||
if (this.multiple) {
|
||||
|
||||
} else {
|
||||
if (model !== '') {
|
||||
this.findChild((child) => {
|
||||
if (child.value === model) {
|
||||
this.query = child.searchLabel;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
},
|
||||
resetInputState () {
|
||||
this.inputLength = this.$els.input.value.length * 12 + 20;
|
||||
},
|
||||
handleInputDelete () {
|
||||
if (this.multiple && this.model.length && this.query === '') {
|
||||
this.removeTag(this.model.length - 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
ready () {
|
||||
this.updateOptions(true);
|
||||
document.addEventListener('keydown', this.handleKeydown);
|
||||
},
|
||||
beforeDestroy () {
|
||||
document.removeEventListener('keydown', this.handleKeydown);
|
||||
},
|
||||
watch: {
|
||||
model () {
|
||||
if (this.multiple) {
|
||||
this.updateMultipleSelected();
|
||||
} else {
|
||||
this.updateSingleSelected();
|
||||
}
|
||||
},
|
||||
visible (val) {
|
||||
if (val) {
|
||||
if (this.multiple && this.filterable) {
|
||||
this.$els.input.focus();
|
||||
}
|
||||
this.$broadcast('on-update-popper');
|
||||
} else {
|
||||
if (this.filterable) {
|
||||
this.$els.input.blur();
|
||||
}
|
||||
this.$broadcast('on-destroy-popper');
|
||||
}
|
||||
},
|
||||
query (val) {
|
||||
this.$broadcast('on-query-change', val);
|
||||
let is_hidden = true;
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.findChild((child) => {
|
||||
if (!child.hidden) {
|
||||
is_hidden = false;
|
||||
}
|
||||
});
|
||||
this.notFound = is_hidden;
|
||||
});
|
||||
}
|
||||
},
|
||||
events: {
|
||||
'on-select-selected' (value) {
|
||||
if (this.model === value) {
|
||||
this.hideMenu();
|
||||
} else {
|
||||
if (this.multiple) {
|
||||
const index = this.model.indexOf(value);
|
||||
if (index >= 0) {
|
||||
this.removeTag(index);
|
||||
} else {
|
||||
this.model.push(value);
|
||||
this.$broadcast('on-update-popper');
|
||||
}
|
||||
|
||||
if (this.filterable) {
|
||||
this.query = '';
|
||||
this.$els.input.focus();
|
||||
}
|
||||
} else {
|
||||
this.model = value;
|
||||
|
||||
if (this.filterable) {
|
||||
this.findChild((child) => {
|
||||
if (child.value === value) {
|
||||
this.query = child.searchLabel;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
2
src/components/spin/index.js
Normal file
2
src/components/spin/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import Spin from './spin.vue';
|
||||
export default Spin;
|
60
src/components/spin/spin.vue
Normal file
60
src/components/spin/spin.vue
Normal file
|
@ -0,0 +1,60 @@
|
|||
<template>
|
||||
<div :class="classes" transition="fade">
|
||||
<div :class="mainClasses">
|
||||
<span :class="dotClasses"></span>
|
||||
<div :class="textClasses" v-el:text><slot></slot></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-spin';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
size: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['small', 'large']);
|
||||
}
|
||||
},
|
||||
fix: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
showText: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-${this.size}`]: !!this.size,
|
||||
[`${prefixCls}-fix`]: this.fix,
|
||||
[`${prefixCls}-show-text`]: this.showText,
|
||||
}
|
||||
]
|
||||
},
|
||||
mainClasses () {
|
||||
return `${prefixCls}-main`;
|
||||
},
|
||||
dotClasses () {
|
||||
return `${prefixCls}-dot`;
|
||||
},
|
||||
textClasses () {
|
||||
return `${prefixCls}-text`;
|
||||
}
|
||||
},
|
||||
compiled () {
|
||||
const text = this.$els.text.innerHTML;
|
||||
|
||||
if (text != '') {
|
||||
this.showText = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
5
src/components/steps/index.js
Normal file
5
src/components/steps/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Steps from './steps.vue';
|
||||
import Step from './step.vue';
|
||||
|
||||
Steps.Step = Step;
|
||||
export default Steps;
|
94
src/components/steps/step.vue
Normal file
94
src/components/steps/step.vue
Normal file
|
@ -0,0 +1,94 @@
|
|||
<template>
|
||||
<div :class="wrapClasses" :style="styles">
|
||||
<div :class="[`${prefixCls}-tail`]"><i></i></div>
|
||||
<div :class="[`${prefixCls}-head`]">
|
||||
<div :class="[`${prefixCls}-head-inner`]">
|
||||
<span v-if="!icon && status != 'finish' && status != 'error'">{{ stepNumber }}</span>
|
||||
<span v-else :class="iconClasses"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="[`${prefixCls}-main`]">
|
||||
<div :class="[`${prefixCls}-title`]">{{ title }}</div>
|
||||
<div v-if="content" :class="[`${prefixCls}-content`]">{{ content }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-steps';
|
||||
const iconPrefixCls = 'ivu-icon';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
status: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['wait', 'process', 'finish', 'error']);
|
||||
}
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
content: {
|
||||
type: String
|
||||
},
|
||||
icon: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
prefixCls: prefixCls,
|
||||
stepNumber: '',
|
||||
nextError: false,
|
||||
total: 1
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
wrapClasses () {
|
||||
return [
|
||||
`${prefixCls}-item`,
|
||||
`${prefixCls}-status-${this.status}`,
|
||||
{
|
||||
[`${prefixCls}-custom`]: !!this.icon,
|
||||
[`${prefixCls}-next-error`]: this.nextError
|
||||
}
|
||||
]
|
||||
},
|
||||
iconClasses () {
|
||||
let icon = '';
|
||||
|
||||
if (!!this.icon) {
|
||||
icon = this.icon;
|
||||
} else {
|
||||
if (this.status == 'finish') {
|
||||
icon = 'ios-checkmark-empty';
|
||||
} else if (this.status == 'error') {
|
||||
icon = 'ios-close-empty';
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
`${prefixCls}-icon`,
|
||||
`${iconPrefixCls}`,
|
||||
{
|
||||
[`${iconPrefixCls}-${icon}`]: icon != ''
|
||||
}
|
||||
]
|
||||
},
|
||||
styles () {
|
||||
return {
|
||||
width: `${1/this.total*100}%`
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
status () {
|
||||
if (this.status == 'error') {
|
||||
this.$parent.setNextError();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
107
src/components/steps/steps.vue
Normal file
107
src/components/steps/steps.vue
Normal file
|
@ -0,0 +1,107 @@
|
|||
<template>
|
||||
<div :class="classes">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-steps';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
current: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
status: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['wait', 'process', 'finish', 'error']);
|
||||
},
|
||||
default: 'process'
|
||||
},
|
||||
size: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['small']);
|
||||
}
|
||||
},
|
||||
direction: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['horizontal', 'vertical']);
|
||||
},
|
||||
default: 'horizontal'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
`${prefixCls}-${this.direction}`,
|
||||
{
|
||||
[`${prefixCls}-${this.size}`]: !!this.size
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
ready () {
|
||||
this.updateChildProps(true);
|
||||
this.setNextError();
|
||||
this.updateCurrent(true);
|
||||
},
|
||||
methods: {
|
||||
updateChildProps (isInit) {
|
||||
const total = this.$children.length;
|
||||
this.$children.forEach((child, index) => {
|
||||
child.stepNumber = index + 1;
|
||||
|
||||
if (this.direction === 'horizontal') {
|
||||
child.total = total;
|
||||
}
|
||||
|
||||
// 如果已存在status,且在初始化时,则略过
|
||||
// todo 如果当前是error,在current改变时需要处理
|
||||
if (!(isInit && child.status)) {
|
||||
if (index == this.current) {
|
||||
if (this.status != 'error') {
|
||||
child.status = 'process';
|
||||
}
|
||||
} else if (index < this.current) {
|
||||
child.status = 'finish';
|
||||
} else {
|
||||
child.status = 'wait';
|
||||
}
|
||||
}
|
||||
|
||||
if (child.status != 'error' && index != 0) {
|
||||
this.$children[index - 1].nextError = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
setNextError () {
|
||||
this.$children.forEach((child, index) => {
|
||||
if (child.status == 'error' && index != 0) {
|
||||
this.$children[index - 1].nextError = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
updateCurrent (isInit) {
|
||||
if (isInit) {
|
||||
const current_status = this.$children[this.current].status;
|
||||
if (!current_status) {
|
||||
this.$children[this.current].status = this.status;
|
||||
}
|
||||
} else {
|
||||
this.$children[this.current].status = this.status;
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
current () {
|
||||
this.updateChildProps();
|
||||
},
|
||||
status () {
|
||||
this.updateCurrent();
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
2
src/components/switch/index.js
Normal file
2
src/components/switch/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import Switch from './switch.vue';
|
||||
export default Switch;
|
56
src/components/switch/switch.vue
Normal file
56
src/components/switch/switch.vue
Normal file
|
@ -0,0 +1,56 @@
|
|||
<template>
|
||||
<span :class="wrapClasses" @click="toggle">
|
||||
<span :class="innerClasses">
|
||||
<slot name="open" v-if="checked"></slot>
|
||||
<slot name="close" v-if="!checked"></slot>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-switch';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
checked: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
size: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['small']);
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
wrapClasses () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-checked`]: this.checked,
|
||||
[`${prefixCls}-disabled`]: this.disabled,
|
||||
[`${prefixCls}-${this.size}`]: !!this.size
|
||||
}
|
||||
]
|
||||
},
|
||||
innerClasses () {
|
||||
return `${prefixCls}-inner`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle () {
|
||||
if (this.disabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.checked = !this.checked;
|
||||
this.$emit('on-change', this.checked);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
2
src/components/tag/index.js
Normal file
2
src/components/tag/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import Tag from './tag.vue';
|
||||
export default Tag;
|
51
src/components/tag/tag.vue
Normal file
51
src/components/tag/tag.vue
Normal file
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<div v-if="!closed" :class="classes" transition="fade">
|
||||
<span :class="textClasses"><slot></slot></span>
|
||||
<Icon v-if="closable" type="ios-close-empty" @click="close"></Icon>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Icon from '../icon';
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-tag';
|
||||
|
||||
export default {
|
||||
components: { Icon },
|
||||
props: {
|
||||
closable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
color: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['blue', 'green', 'red', 'yellow']);
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
closed: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-${this.color}`]: !!this.color
|
||||
}
|
||||
]
|
||||
},
|
||||
textClasses () {
|
||||
return `${prefixCls}-text`;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close (e) {
|
||||
this.closed = true;
|
||||
this.$emit('on-close', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
5
src/components/timeline/index.js
Normal file
5
src/components/timeline/index.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Timeline from './timeline.vue';
|
||||
import TimelineItem from './timeline-item.vue';
|
||||
|
||||
Timeline.Item = TimelineItem;
|
||||
export default Timeline;
|
65
src/components/timeline/timeline-item.vue
Normal file
65
src/components/timeline/timeline-item.vue
Normal file
|
@ -0,0 +1,65 @@
|
|||
<template>
|
||||
<li :class="itemClasses">
|
||||
<div :class="tailClasses"></div>
|
||||
<div :class="headClasses" :style="customColor" v-el:dot><slot name="dot"></slot></div>
|
||||
<div :class="contentClasses">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
<script>
|
||||
const prefixCls = 'ivu-timeline';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
default: 'blue'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
dot: false
|
||||
}
|
||||
},
|
||||
ready () {
|
||||
this.dot = this.$els.dot.innerHTML.length ? true : false;
|
||||
},
|
||||
computed: {
|
||||
itemClasses () {
|
||||
return `${prefixCls}-item`;
|
||||
},
|
||||
tailClasses () {
|
||||
return `${prefixCls}-item-tail`;
|
||||
},
|
||||
headClasses () {
|
||||
return [
|
||||
`${prefixCls}-item-head`,
|
||||
{
|
||||
[`${prefixCls}-item-head-custom`]: this.dot,
|
||||
[`${prefixCls}-item-head-${this.color}`]: this.headColorShow
|
||||
}
|
||||
]
|
||||
},
|
||||
headColorShow () {
|
||||
return this.color == 'blue' || this.color == 'red' || this.color == 'green';
|
||||
},
|
||||
customColor () {
|
||||
let style = {};
|
||||
if (this.color) {
|
||||
if (!this.headColorShow) {
|
||||
style = {
|
||||
'color': this.color,
|
||||
'border-color': this.color
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
},
|
||||
contentClasses () {
|
||||
return `${prefixCls}-item-content`;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
27
src/components/timeline/timeline.vue
Normal file
27
src/components/timeline/timeline.vue
Normal file
|
@ -0,0 +1,27 @@
|
|||
<template>
|
||||
<ul :class="classes">
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</template>
|
||||
<script>
|
||||
const prefixCls = 'ivu-timeline';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
pending: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
classes () {
|
||||
return [
|
||||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-pending`]: this.pending
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
3
src/components/tooltip/index.js
Normal file
3
src/components/tooltip/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import Tooltip from './tooltip.vue';
|
||||
|
||||
export default Tooltip;
|
59
src/components/tooltip/tooltip.vue
Normal file
59
src/components/tooltip/tooltip.vue
Normal file
|
@ -0,0 +1,59 @@
|
|||
<template>
|
||||
<div :class="[`${prefixCls}`]" @mouseenter="handleShowPopper" @mouseleave="handleClosePopper">
|
||||
<div :class="[`${prefixCls}-rel`]" v-el:reference>
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div :class="[`${prefixCls}-popper`]" transition="fade" v-el:popper v-show="!disabled && visible">
|
||||
<div :class="[`${prefixCls}-content`]">
|
||||
<div :class="[`${prefixCls}-arrow`]"></div>
|
||||
<div :class="[`${prefixCls}-inner`]"><slot name="content">{{ content }}</slot></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Popper from '../base/popper';
|
||||
import { oneOf } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-tooltip';
|
||||
|
||||
export default {
|
||||
mixins: [Popper],
|
||||
props: {
|
||||
placement: {
|
||||
validator (value) {
|
||||
return oneOf(value, ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end']);
|
||||
},
|
||||
default: 'bottom'
|
||||
},
|
||||
content: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
delay: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
prefixCls: prefixCls
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleShowPopper() {
|
||||
this.timeout = setTimeout(() => {
|
||||
this.visible = true;
|
||||
}, this.delay);
|
||||
},
|
||||
handleClosePopper() {
|
||||
clearTimeout(this.timeout);
|
||||
this.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue