empty master

This commit is contained in:
梁灏 2019-08-27 09:37:17 +08:00
parent 92c1162255
commit 67d534df27
276 changed files with 0 additions and 28368 deletions

View file

@ -1,5 +0,0 @@
{
"presets": ["es2015"],
"plugins": ["transform-runtime"],
"comments": false
}

View file

@ -1,8 +0,0 @@
root = true
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

View file

@ -1,18 +0,0 @@
{
"root": true,
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"env": {
"browser": true
},
"extends": "eslint:recommended",
"plugins": [ "html" ],
"rules": {
"indent": ["error", 4, { "SwitchCase": 1 }],
"quotes": ["error", "single"],
"semi": ["error", "always"],
"no-console": ["error"]
}
}

1
.gitattributes vendored
View file

@ -1 +0,0 @@
src/styles/**/* linguist-vendored=false

View file

@ -1,25 +0,0 @@
<!--
issue 仅用于提交 bug 或 feature 及 文档错误,其余疑问恕不作答。提问前请先阅读 https://github.com/iview/iview/issues/79 和相关文档后考虑是否开启issue。
-->
<!--
我们十分感谢有价值的 issue 贡献者,所以请填写以下内容。如果提问不符要求、在文档中已有解答、已有相同 issue我们将直接 close感谢理解。
-->
### iView 版本号
<!-- 0.9.10 -->
### 操作系统/浏览器 版本号
<!-- macOS/Chrome 54 -->
### Vue 版本号
<!-- 1.0.26 -->
### 能够复现问题的在线示例
<!-- 使用下面的在线链接快速创建示例 -->
<!-- https://codepen.io/anon/pen/NbEbja -->
### 复现步骤
### 问题现象,以及你期望的结果是怎样的?
### 你估计可能的原因是什么(选填)?

20
.gitignore vendored
View file

@ -1,20 +0,0 @@
*.iml
.idea
.ipr
.iws
*.diff
*.patch
*.bak
.DS_Store
node_modules/
node_modules2/
.project
.settings
npm-debug.log
.*proj
.svn/
*.swp
*.swo
*.log
test/dist/
dist/

View file

@ -1,7 +0,0 @@
.*
*.md
*.yml
build/
node_modules/
test/
gulpfile.js

View file

@ -1,5 +0,0 @@
language: node_js
node_js:
- "4"
script:
- npm run test

21
LICENSE
View file

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2016 iview
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,83 +0,0 @@
<p align="center">
<a href="https://www.iviewui.com">
<img width="200" src="https://raw.githubusercontent.com/iview/iview/master/assets/logo.png">
</a>
</p>
# iView [![](https://img.shields.io/travis/iview/iview.svg?style=flat-square)](https://travis-ci.org/iview/iview) [![iView](https://img.shields.io/npm/v/iview.svg?style=flat-square)](https://www.npmjs.org/package/iview) [![NPM downloads](http://img.shields.io/npm/dm/iview.svg?style=flat-square)](https://npmjs.org/package/iview) [![Join the chat at https://gitter.im/iview/iview](https://badges.gitter.im/iview/iview.svg)](https://gitter.im/iview/iview?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
### A high quality UI Components Library with Vue.js
> iView works only with Vue 1.0 currently. We have been in developing. English document is coming soon. Please pay attention to the iView.
## Docs
### [中文文档](https://www.iviewui.com)
### English (Coming soon)
## Overview
### [组件概览Component Overview](https://www.iviewui.com/overview)
## Features
- High quality and rich functions
- Friendly APIs,free and flexible
- Great Documentation
- It is quite beautiful
- Using .vue file development mode
- Based on npm + webpack + babel, using ES2015
## Programming
![iView](https://raw.githubusercontent.com/iview/iview/master/assets/iview.png)
## Install
### Install vue-webpack project in the first place
Use [iview-project](https://github.com/iview/iview-project)(Recommended) Or [vue-cli](https://github.com/vuejs/vue-cli)
### Install iView
using npm
```
npm install iview --save
```
Or using script tag for global use
```
<script type="text/javascript" src="iview.min.js"></script>
```
## Usage
```html
<template>
<Slider :value.sync="value" range></Slider>
</template>
<script>
export default {
data () {
return {
value: [20, 50]
}
}
}
</script>
```
Use css
```js
import 'iview/dist/styles/iview.css';
```
## Browser Support
Normal browsers and Internet Explorer 9+.
## Links
- [TalkingData](https://github.com/TalkingData)
- [Vue](https://github.com/vuejs/vue)
- [Webpack](https://github.com/webpack/webpack)
- [ionicons](https://github.com/driftyco/ionicons)
- [Ant Design](https://github.com/ant-design/ant-design)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 KiB

View file

@ -1,25 +0,0 @@
var gulp = require('gulp');
var cleanCSS = require('gulp-clean-css');
var less = require('gulp-less');
var rename = require('gulp-rename');
var autoprefixer = require('gulp-autoprefixer');
// 编译less
gulp.task('css', function () {
gulp.src('../src/styles/index.less')
.pipe(less())
.pipe(autoprefixer({
browsers: ['last 2 versions', 'ie > 8']
}))
.pipe(cleanCSS())
.pipe(rename('iview.css'))
.pipe(gulp.dest('../dist/styles'));
});
// 拷贝字体文件
gulp.task('fonts', function () {
gulp.src('../src/styles/common/iconfont/fonts/*.*')
.pipe(gulp.dest('../dist/styles/fonts'));
});
gulp.task('default', ['css', 'fonts']);

View file

@ -1,61 +0,0 @@
/**
* 本地预览
*/
var path = require('path');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
// 入口
entry: {
main: './test/main',
vendors: ['vue', 'vue-router']
},
// 输出
output: {
path: path.join(__dirname, '../test/dist'),
publicPath: '/test/dist/',
filename: '[name].js',
chunkFilename: '[name].chunk.js'
},
// 加载器
module: {
loaders: [
{ test: /\.vue$/, loader: 'vue' },
{ test: /\.js$/, loader: 'babel', exclude: /node_modules/ },
{ test: /\.css$/, loader: 'style!css!autoprefixer'},
{ test: /\.less$/, loader: 'style!css!less' },
{ test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
{ test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/, loader: 'url-loader?limit=8192'},
{ test: /\.(html|tpl)$/, loader: 'html-loader' }
]
},
vue: {
loaders: {
css: ExtractTextPlugin.extract(
"style-loader",
"css-loader?sourceMap",
{
publicPath: "/test/dist/"
}
),
less: ExtractTextPlugin.extract(
'vue-style-loader',
'css-loader!less-loader'
),
js: 'babel'
}
},
resolve: {
// require时省略的扩展名require('module') 不需要module.js
extensions: ['', '.js', '.vue'],
alias: {
iview: '../../src/index'
}
},
plugins: [
new ExtractTextPlugin("[name].css",{ allChunks : true,resolve : ['modules'] }), // 提取CSS
new webpack.optimize.CommonsChunkPlugin('vendors', 'vendors.js'), // 提取第三方库
]
};

View file

@ -1,56 +0,0 @@
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: {
main: './src/index.js'
},
output: {
path: path.resolve(__dirname, '../dist'),
publicPath: '/dist/',
filename: 'iview.js',
library: 'iview',
libraryTarget: 'umd',
umdNamedDefine: true
},
externals: {
vue: {
root: 'Vue',
commonjs: 'vue',
commonjs2: 'vue',
amd: 'vue'
}
},
resolve: {
extensions: ['', '.js', '.vue']
},
module: {
loaders: [{
test: /\.vue$/,
loader: 'vue'
}, {
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/
}, {
test: /\.css$/,
loader: 'style!css!autoprefixer'
}, {
test: /\.less$/,
loader: 'style!css!less'
}, {
test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
loader: 'url?limit=8192'
}, {
test: /\.(html|tpl)$/,
loader: 'vue-html'
}]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"'
}
})
]
}

View file

@ -1,62 +0,0 @@
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: {
main: './src/index.js'
},
output: {
path: path.resolve(__dirname, '../dist'),
publicPath: '/dist/',
filename: 'iview.min.js',
library: 'iview',
libraryTarget: 'umd',
umdNamedDefine: true
},
externals: {
vue: {
root: 'Vue',
commonjs: 'vue',
commonjs2: 'vue',
amd: 'vue'
}
},
resolve: {
extensions: ['', '.js', '.vue']
},
module: {
loaders: [{
test: /\.vue$/,
loader: 'vue'
}, {
test: /\.js$/,
loader: 'babel',
exclude: /node_modules/
}, {
test: /\.css$/,
loader: 'style!css!autoprefixer'
}, {
test: /\.less$/,
loader: 'style!css!less'
}, {
test: /\.(gif|jpg|png|woff|svg|eot|ttf)\??.*$/,
loader: 'url?limit=8192'
}, {
test: /\.(html|tpl)$/,
loader: 'vue-html'
}]
},
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
new webpack.optimize.OccurenceOrderPlugin()
]
}

View file

@ -1,81 +0,0 @@
{
"name": "iview",
"version": "1.0.2",
"title": "iView",
"description": "A high quality UI components Library with Vue.js",
"homepage": "http://www.iviewui.com",
"keywords": [
"iview",
"vue",
"vue.js",
"component",
"components",
"ui",
"framework"
],
"main": "dist/iview.js",
"files": [
"dist",
"src"
],
"scripts": {
"dev": "webpack-dev-server --content-base test/ --open --inline --hot --compress --history-api-fallback --port 8081 --config build/webpack.dev.config.js",
"dist:style": "gulp --gulpfile build/build-style.js",
"dist:dev": "webpack --config build/webpack.dist.dev.config.js",
"dist:prod": "webpack --config build/webpack.dist.prod.config.js",
"dist": "npm run dist:style && npm run dist:dev && npm run dist:prod",
"lint": "eslint --fix --ext .js,.vue src",
"test": "npm run dist && npm run lint",
"prepublish": "npm run dist"
},
"repository": {
"type": "git",
"url": "https://github.com/iview/iview"
},
"author": "Aresn",
"license": "MIT",
"bugs": {
"url": "https://github.com/iview/iview/issues"
},
"dependencies": {
"async-validator": "^1.6.7",
"core-js": "^2.4.1",
"deepmerge": "^1.3.1",
"popper.js": "^0.6.4"
},
"peerDependencies": {
"vue": "^1.0.17"
},
"devDependencies": {
"autoprefixer-loader": "^2.0.0",
"babel": "^6.3.13",
"babel-core": "^6.11.4",
"babel-loader": "^6.2.4",
"babel-plugin-transform-runtime": "^6.12.0",
"babel-preset-es2015": "^6.9.0",
"babel-runtime": "^6.11.6",
"css-loader": "^0.23.1",
"eslint": "^3.12.2",
"eslint-plugin-html": "^1.7.0",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.8.5",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^3.1.1",
"gulp-clean-css": "^2.0.13",
"gulp-less": "^3.1.0",
"gulp-rename": "^1.2.2",
"html-loader": "^0.3.0",
"less": "^2.7.1",
"less-loader": "^2.2.3",
"style-loader": "^0.13.1",
"url-loader": "^0.5.7",
"vue": "^1.0.26",
"vue-hot-reload-api": "^1.3.3",
"vue-html-loader": "^1.2.3",
"vue-loader": "^8.5.3",
"vue-router": "^0.7.13",
"vue-style-loader": "^1.0.0",
"webpack": "^1.13.1",
"webpack-dev-server": "^1.16.1"
}
}

View file

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

View file

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

View file

@ -1,106 +0,0 @@
<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
},
banner: {
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,
[`${prefixCls}-with-banner`]: this.banner
}
];
},
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>

View file

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

View file

@ -1,75 +0,0 @@
<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>
import { scrollTop } from '../../utils/assist';
const prefixCls = 'ivu-back-top';
export default {
props: {
height: {
type: Number,
default: 400
},
bottom: {
type: Number,
default: 30
},
right: {
type: Number,
default: 30
},
duration: {
type: Number,
default: 1000
}
},
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 () {
this.backTop = window.pageYOffset >= this.height;
},
back () {
scrollTop(window, document.body.scrollTop, 0, this.duration);
this.$emit('on-click');
}
}
};
</script>

View file

@ -1,2 +0,0 @@
import BackTop from './back-top.vue';
export default BackTop;

View file

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

View file

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

View file

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

View file

@ -1,104 +0,0 @@
<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
}
},
data () {
return {
withDesc: false
};
},
computed: {
baseClass () {
return `${this.prefixCls}-notice`;
},
classes () {
return [
this.baseClass,
{
[`${this.className}`]: !!this.className,
[`${this.baseClass}-closable`]: this.closable,
[`${this.baseClass}-with-desc`]: this.withDesc
}
];
},
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);
}
// check if with desc in Notice component
if (this.prefixCls === 'ivu-notice') {
this.withDesc = this.$els.content.querySelectorAll(`.${this.prefixCls}-desc`)[0].innerHTML !== '';
}
},
beforeDestroy () {
this.clearCloseTimer();
}
};
</script>

View file

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

View file

@ -1,106 +0,0 @@
/**
* 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) {
if (val) {
this.updatePopper();
} else {
this.destroyPopper();
this.$emit('on-popper-hide');
}
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();
}
}
};

View file

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

View file

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

View file

@ -1,5 +0,0 @@
import Breadcrumb from './breadcrumb.vue';
import BreadcrumbItem from './breadcrumb-item.vue';
Breadcrumb.Item = BreadcrumbItem;
export default Breadcrumb;

View file

@ -1,41 +0,0 @@
<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']);
}
},
shape: {
validator (value) {
return oneOf(value, ['circle', 'circle-outline']);
}
},
vertical: {
type: Boolean,
default: false
}
},
computed: {
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-${this.size}`]: !!this.size,
[`${prefixCls}-${this.shape}`]: !!this.shape,
[`${prefixCls}-vertical`]: this.vertical
}
];
}
}
};
</script>

View file

@ -1,70 +0,0 @@
<template>
<button :type="htmlType" :class="classes" :disabled="disabled">
<Icon class="ivu-load-loop" type="load-c" v-if="loading"></Icon>
<Icon :type="icon" v-if="icon && !loading"></Icon>
<span v-if="showSlot" v-el:slot><slot></slot></span>
</button>
</template>
<script>
import Icon from '../icon';
import { oneOf } from '../../utils/assist';
const prefixCls = 'ivu-btn';
export default {
components: { Icon },
props: {
type: {
validator (value) {
return oneOf(value, ['primary', 'ghost', 'dashed', 'text', 'info', 'success', 'warning', 'error']);
}
},
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,
long: {
type: Boolean,
default: false
}
},
data () {
return {
showSlot: true
};
},
computed: {
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-${this.type}`]: !!this.type,
[`${prefixCls}-long`]: this.long,
[`${prefixCls}-${this.shape}`]: !!this.shape,
[`${prefixCls}-${this.size}`]: !!this.size,
[`${prefixCls}-loading`]: this.loading != null && this.loading,
[`${prefixCls}-icon-only`]: !this.showSlot && (!!this.icon || this.loading)
}
];
}
},
compiled () {
this.showSlot = this.$els.slot.innerHTML.replace(/\n/g, '').replace(/<!--[\w\W\r\n]*?-->/gmi, '') !== '';
}
};
</script>

View file

@ -1,5 +0,0 @@
import Button from './button.vue';
import ButtonGroup from './button-group.vue';
Button.Group = ButtonGroup;
export default Button;

View file

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

View file

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

View file

@ -1,34 +0,0 @@
<template>
<div :class="prefixCls" v-bind:style="styles"><slot></slot></div>
</template>
<script>
const prefixCls = 'ivu-carousel-item';
export default {
componentName: 'carousel-item',
data () {
return {
prefixCls: prefixCls,
width: 0,
height: 'auto',
left: 0
};
},
computed: {
styles () {
return {
width: `${this.width}px`,
height: `${this.height}`,
left: `${this.left}px`
};
}
},
compiled () {
this.$parent.slotChange();
},
beforeDestroy () {
this.$parent.slotChange();
}
};
</script>

View file

@ -1,245 +0,0 @@
<template>
<div :class="classes">
<button :class="arrowClasses" class="left" @click="arrowEvent(-1)">
<Icon type="chevron-left"></Icon>
</button>
<div :class="[prefixCls + '-list']">
<div :class="[prefixCls + '-track']" :style="trackStyles" v-el:slides>
<slot></slot>
</div>
</div>
<button :class="arrowClasses" class="right" @click="arrowEvent(1)">
<Icon type="chevron-right"></Icon>
</button>
<ul :class="dotsClasses">
<template v-for="n in slides.length">
<li :class="[n === currentIndex ? prefixCls + '-active' : '']"
@click="dotsEvent('click', n)"
@mouseover="dotsEvent('hover', n)">
<button></button>
</li>
</template>
</ul>
</div>
</template>
<script>
import Icon from '../icon/icon.vue';
import { getStyle, oneOf } from '../../utils/assist';
const prefixCls = 'ivu-carousel';
export default {
name: 'Carousel',
components: { Icon },
props: {
arrow: {
type: String,
default: 'hover',
validator (value) {
return oneOf(value, ['hover', 'always', 'never']);
}
},
autoplay: {
type: Boolean,
default: false
},
autoplaySpeed: {
type: Number,
default: 2000
},
easing: {
type: String,
default: 'ease'
},
dots: {
type: String,
default: 'inside',
validator (value) {
return oneOf(value, ['inside', 'outside', 'none']);
}
},
trigger: {
type: String,
default: 'click',
validator (value) {
return oneOf(value, ['click', 'hover']);
}
},
currentIndex: {
type: Number,
default: 0
},
height: {
type: [String, Number],
default: 'auto',
validator (value) {
return value === 'auto' || Object.prototype.toString.call(value) === '[object Number]';
}
}
},
data () {
return {
prefixCls: prefixCls,
listWidth: 0,
trackWidth: 0,
trackOffset: 0,
slides: [],
slideInstances: [],
timer: null,
ready: false
};
},
computed: {
classes () {
return [
`${prefixCls}`
];
},
trackStyles () {
return {
width: `${this.trackWidth}px`,
transform: `translate3d(-${this.trackOffset}px, 0px, 0px)`,
transition: `transform 500ms ${this.easing}`
};
},
arrowClasses () {
return [
`${prefixCls}-arrow`,
`${prefixCls}-arrow-${this.arrow}`
];
},
dotsClasses () {
return [
`${prefixCls}-dots`,
`${prefixCls}-dots-${this.dots}`
];
}
},
methods: {
// 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.slideInstances.length || !this.$children) {
this.slideInstances.forEach((child) => {
find(child);
});
} else {
this.$children.forEach((child) => {
find(child);
});
}
},
updateSlides (init ) {
let slides = [];
let index = 1;
this.findChild((child) => {
slides.push({
$el: child.$el
});
child.index = index++;
if (init) {
this.slideInstances.push(child);
}
});
this.slides = slides;
this.updatePos();
},
updatePos () {
this.findChild((child) => {
child.width = this.listWidth;
child.height = typeof this.height === 'number' ? `${this.height}px` : this.height;
});
this.trackWidth = (this.slides.length || 0) * this.listWidth;
},
// use when slot changed
slotChange () {
this.$nextTick(() => {
this.slides = [];
this.slideInstances = [];
this.updateSlides(true, true);
this.updatePos();
this.updateOffset();
});
},
handleResize () {
this.listWidth = parseInt(getStyle(this.$el, 'width'));
this.updatePos();
this.updateOffset();
},
add (offset) {
let index = this.currentIndex;
index += offset;
while (index < 0) index += this.slides.length;
index = index % this.slides.length;
this.currentIndex = index;
},
arrowEvent (offset) {
this.setAutoplay();
this.add(offset);
},
dotsEvent (event, n) {
if (event === this.trigger && this.currentIndex !== n) {
this.currentIndex = n;
// Reset autoplay timer when trigger be activated
this.setAutoplay();
}
},
setAutoplay () {
window.clearInterval(this.timer);
if (this.autoplay) {
this.timer = window.setInterval(() => {
this.add(1);
}, this.autoplaySpeed);
}
},
updateOffset () {
this.$nextTick(() => {
this.trackOffset = this.currentIndex * this.listWidth;
});
}
},
compiled () {
this.updateSlides(true);
},
watch: {
autoplay () {
this.setAutoplay();
},
autoplaySpeed () {
this.setAutoplay();
},
currentIndex (val, oldVal) {
this.$emit('on-change', oldVal, val);
this.updateOffset();
},
height () {
this.updatePos();
}
},
ready () {
this.handleResize();
this.setAutoplay();
window.addEventListener('resize', this.handleResize, false);
},
beforeDestroy () {
window.removeEventListener('resize', this.handleResize, false);
}
};
</script>

View file

@ -1,5 +0,0 @@
import Carousel from './carousel.vue';
import CarouselItem from './carousel-item.vue';
Carousel.Item = CarouselItem;
export default Carousel;

View file

@ -1,210 +0,0 @@
<template>
<div :class="classes" v-clickoutside="handleClose">
<div :class="[prefixCls + '-rel']" @click="toggleOpen">
<slot>
<i-input
readonly
:disabled="disabled"
:value.sync="displayRender"
:size="size"
:placeholder="placeholder"></i-input>
<Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.stop="clearSelect"></Icon>
<Icon type="arrow-down-b" :class="[prefixCls + '-arrow']"></Icon>
</slot>
</div>
<Dropdown v-show="visible" transition="slide-up">
<div>
<Caspanel
v-ref:caspanel
:prefix-cls="prefixCls"
:data.sync="data"
:disabled="disabled"
:change-on-select="changeOnSelect"
:trigger="trigger"></Caspanel>
</div>
</Dropdown>
</div>
</template>
<script>
import iInput from '../input/input.vue';
import Dropdown from '../select/dropdown.vue';
import Icon from '../icon/icon.vue';
import Caspanel from './caspanel.vue';
import clickoutside from '../../directives/clickoutside';
import { oneOf } from '../../utils/assist';
const prefixCls = 'ivu-cascader';
export default {
components: { iInput, Dropdown, Icon, Caspanel },
directives: { clickoutside },
props: {
data: {
type: Array,
default () {
return [];
}
},
value: {
type: Array,
default () {
return [];
}
},
disabled: {
type: Boolean,
default: false
},
clearable: {
type: Boolean,
default: true
},
placeholder: {
type: String,
default: '请选择'
},
size: {
validator (value) {
return oneOf(value, ['small', 'large']);
}
},
trigger: {
validator (value) {
return oneOf(value, ['click', 'hover']);
},
default: 'click'
},
changeOnSelect: {
type: Boolean,
default: false
},
renderFormat: {
type: Function,
default (label) {
return label.join(' / ');
}
}
},
data () {
return {
prefixCls: prefixCls,
visible: false,
selected: [],
tmpSelected: [],
updatingValue: false // to fix set value in changeOnSelect type
};
},
computed: {
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-show-clear`]: this.showCloseIcon,
[`${prefixCls}-visible`]: this.visible,
[`${prefixCls}-disabled`]: this.disabled
}
];
},
showCloseIcon () {
return this.value && this.value.length && this.clearable;
},
displayRender () {
let label = [];
for (let i = 0; i < this.selected.length; i++) {
label.push(this.selected[i].label);
}
return this.renderFormat(label, this.selected);
}
},
methods: {
clearSelect () {
const oldVal = JSON.stringify(this.value);
this.value = this.selected = this.tmpSelected = [];
this.handleClose();
this.emitValue(this.value, oldVal);
this.$broadcast('on-clear');
},
handleClose () {
this.visible = false;
},
toggleOpen () {
if (this.disabled) return false;
if (this.visible) {
this.handleClose();
} else {
this.onFocus();
}
},
onFocus () {
this.visible = true;
if (!this.value.length) {
this.$broadcast('on-clear');
}
},
updateResult (result) {
this.tmpSelected = result;
},
updateSelected (init = false) {
if (!this.changeOnSelect || init) {
this.$broadcast('on-find-selected', this.value);
}
},
emitValue (val, oldVal) {
if (JSON.stringify(val) !== oldVal) {
this.$emit('on-change', this.value, JSON.parse(JSON.stringify(this.selected)));
this.$dispatch('on-form-change', this.value, JSON.parse(JSON.stringify(this.selected)));
}
}
},
ready () {
this.updateSelected(true);
},
events: {
// lastValue: is click the final val
// fromInit: is this emit from update value
'on-result-change' (lastValue, changeOnSelect, fromInit) {
if (lastValue || changeOnSelect) {
const oldVal = JSON.stringify(this.value);
this.selected = this.tmpSelected;
let newVal = [];
this.selected.forEach((item) => {
newVal.push(item.value);
});
if (!fromInit) {
this.updatingValue = true;
this.value = newVal;
this.emitValue(this.value, oldVal);
}
}
if (lastValue && !fromInit) {
this.handleClose();
}
},
'on-form-blur' () {
return false;
},
'on-form-change' () {
return false;
}
},
watch: {
visible (val) {
if (val) {
if (this.value.length) {
this.updateSelected();
}
}
},
value () {
if (this.updatingValue) {
this.updatingValue = false;
return;
}
this.updateSelected(true);
}
}
};
</script>

View file

@ -1,23 +0,0 @@
<template>
<li :class="classes">{{ data.label }}<i v-if="data.children && data.children.length" class="ivu-icon ivu-icon-ios-arrow-right"></i></li>
</template>
<script>
export default {
props: {
data: Object,
prefixCls: String,
tmpItem: Object
},
computed: {
classes () {
return [
`${this.prefixCls}-menu-item`,
{
[`${this.prefixCls}-menu-item-active`]: this.tmpItem.value === this.data.value,
[`${this.prefixCls}-menu-item-disabled`]: this.data.disabled
}
];
}
}
};
</script>

View file

@ -1,114 +0,0 @@
<template>
<ul v-if="data && data.length" :class="[prefixCls + '-menu']">
<Casitem
v-for="item in data"
:prefix-cls="prefixCls"
:data.sync="item"
:tmp-item="tmpItem"
@click.stop="handleClickItem(item)"
@mouseenter.stop="handleHoverItem(item)"></Casitem>
</ul><Caspanel v-if="sublist && sublist.length" :prefix-cls="prefixCls" :data.sync="sublist" :disabled="disabled" :trigger="trigger" :change-on-select="changeOnSelect"></Caspanel>
</template>
<script>
import Casitem from './casitem.vue';
export default {
name: 'Caspanel',
components: { Casitem },
props: {
data: {
type: Array,
default () {
return [];
}
},
sublist: {
type: Array,
default () {
return [];
}
},
disabled: Boolean,
changeOnSelect: Boolean,
trigger: String,
prefixCls: String
},
data () {
return {
tmpItem: {},
result: []
};
},
methods: {
handleClickItem (item) {
if (this.trigger !== 'click' && item.children) return;
this.handleTriggerItem(item);
},
handleHoverItem (item) {
if (this.trigger !== 'hover' || !item.children) return;
this.handleTriggerItem(item);
},
handleTriggerItem (item, fromInit = false) {
if (item.disabled) return;
// return value back recursion
const backItem = this.getBaseItem(item);
this.tmpItem = backItem;
this.emitUpdate([backItem]);
if (item.children && item.children.length){
this.sublist = item.children;
this.$dispatch('on-result-change', false, this.changeOnSelect, fromInit);
} else {
this.sublist = [];
this.$dispatch('on-result-change', true, this.changeOnSelect, fromInit);
}
},
updateResult (item) {
this.result = [this.tmpItem].concat(item);
this.emitUpdate(this.result);
},
getBaseItem (item) {
let backItem = Object.assign({}, item);
if (backItem.children) {
delete backItem.children;
}
return backItem;
},
emitUpdate (result) {
if (this.$parent.$options.name === 'Caspanel') {
this.$parent.updateResult(result);
} else {
this.$parent.$parent.updateResult(result);
}
}
},
watch: {
data () {
this.sublist = [];
}
},
events: {
'on-find-selected' (val) {
let value = [...val];
for (let i = 0; i < value.length; i++) {
for (let j = 0; j < this.data.length; j++) {
if (value[i] === this.data[j].value) {
this.handleTriggerItem(this.data[j], true);
value.splice(0, 1);
this.$nextTick(() => {
this.$broadcast('on-find-selected', value);
});
return false;
}
}
}
},
'on-clear' () {
this.sublist = [];
this.tmpItem = {};
}
}
};
</script>

View file

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

View file

@ -1,52 +0,0 @@
<template>
<div :class="classes">
<slot></slot>
</div>
</template>
<script>
const prefixCls = 'ivu-checkbox-group';
export default {
name: 'checkboxGroup',
props: {
model: {
type: Array,
default () {
return [];
}
}
},
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);
this.$dispatch('on-form-change', data);
}
},
watch: {
model () {
this.updateModel(true);
}
}
};
</script>

View file

@ -1,115 +0,0 @@
<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 v-if="showSlot"><span v-el:slot>{{ 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
},
indeterminate: {
type: Boolean,
default: false
}
},
data () {
return {
model: [],
selected: false,
group: false,
showSlot: true
};
},
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,
[`${prefixCls}-indeterminate`]: this.indeterminate
}
];
},
innerClasses () {
return `${prefixCls}-inner`;
},
inputClasses () {
return `${prefixCls}-input`;
}
},
ready () {
if (this.$parent && this.$parent.$options.name === 'checkboxGroup') this.group = true;
if (!this.group) {
this.updateModel();
if (this.$els.slot && this.$els.slot.innerHTML === '') {
this.showSlot = false;
}
}
},
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);
this.$dispatch('on-form-change', this.checked);
}
},
updateModel () {
this.selected = this.checked;
}
},
watch: {
checked () {
this.updateModel();
}
}
};
</script>

View file

@ -1,5 +0,0 @@
import Checkbox from './checkbox.vue';
import CheckboxGroup from './checkbox-group.vue';
Checkbox.Group = CheckboxGroup;
export default Checkbox;

View file

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

View file

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

View file

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

View file

@ -1,5 +0,0 @@
import Collapse from './collapse.vue';
import Panel from './panel.vue';
Collapse.Panel = Panel;
export default Collapse;

View file

@ -1,57 +0,0 @@
<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/icon.vue';
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>

View file

@ -1,50 +0,0 @@
<template>
<div :class="[prefixCls + '-confirm']">
<span :class="timeClasses" v-if="showTime" @click="handleToggleTime">
<template v-if="isTime">{{ t('i.datepicker.selectDate') }}</template>
<template v-else>{{ t('i.datepicker.selectTime') }}</template>
</span>
<i-button size="small" type="text" @click="handleClear">{{ t('i.datepicker.clear') }}</i-button>
<i-button size="small" type="primary" @click="handleSuccess">{{ t('i.datepicker.ok') }}</i-button>
</div>
</template>
<script>
import iButton from '../../button/button.vue';
import Locale from '../../../mixins/locale';
const prefixCls = 'ivu-picker';
export default {
mixins: [ Locale ],
components: { iButton },
props: {
showTime: false,
isTime: false,
timeDisabled: false
},
data () {
return {
prefixCls: prefixCls
};
},
computed: {
timeClasses () {
return {
[`${prefixCls}-confirm-time-disabled`]: this.timeDisabled
};
}
},
methods: {
handleClear () {
this.$emit('on-pick-clear');
},
handleSuccess () {
this.$emit('on-pick-success');
},
handleToggleTime () {
if (this.timeDisabled) return;
this.$emit('on-pick-toggle-time');
}
}
};
</script>

View file

@ -1,284 +0,0 @@
<template>
<div
:class="classes"
@click="handleClick"
@mousemove="handleMouseMove">
<div :class="[prefixCls + '-header']">
<span>{{ t('i.datepicker.weeks.sun') }}</span><span>{{ t('i.datepicker.weeks.mon') }}</span><span>{{ t('i.datepicker.weeks.tue') }}</span><span>{{ t('i.datepicker.weeks.wed') }}</span><span>{{ t('i.datepicker.weeks.thu') }}</span><span>{{ t('i.datepicker.weeks.fri') }}</span><span>{{ t('i.datepicker.weeks.sat') }}</span>
</div>
<span :class="getCellCls(cell)" v-for="cell in readCells"><em :index="$index">{{ cell.text }}</em></span>
</div>
</template>
<script>
import { getFirstDayOfMonth, getDayCountOfMonth } from '../util';
import { deepCopy } from '../../../utils/assist';
import Locale from '../../../mixins/locale';
const prefixCls = 'ivu-date-picker-cells';
const clearHours = function (time) {
const cloneDate = new Date(time);
cloneDate.setHours(0, 0, 0, 0);
return cloneDate.getTime();
};
export default {
mixins: [ Locale ],
props: {
date: {},
year: {},
month: {},
selectionMode: {
default: 'day'
},
disabledDate: {},
minDate: {},
maxDate: {},
rangeState: {
default () {
return {
endDate: null,
selecting: false
};
}
},
value: ''
},
data () {
return {
prefixCls: prefixCls,
readCells: []
};
},
watch: {
'rangeState.endDate' (newVal) {
this.markRange(newVal);
},
minDate(newVal, oldVal) {
if (newVal && !oldVal) {
this.rangeState.selecting = true;
this.markRange(newVal);
} else if (!newVal) {
this.rangeState.selecting = false;
this.markRange(newVal);
} else {
this.markRange();
}
},
maxDate(newVal, oldVal) {
if (newVal && !oldVal) {
this.rangeState.selecting = false;
this.markRange(newVal);
// this.$emit('on-pick', {
// minDate: this.minDate,
// maxDate: this.maxDate
// });
}
},
cells: {
handler (cells) {
this.readCells = cells;
},
immediate: true
}
},
computed: {
classes () {
return [
`${prefixCls}`
];
},
cells () {
const date = new Date(this.year, this.month, 1);
let day = getFirstDayOfMonth(date); // day of first day
day = (day === 0 ? 7 : day);
const today = clearHours(new Date()); // timestamp of today
const selectDay = clearHours(new Date(this.value)); // timestamp of selected day
const minDay = clearHours(new Date(this.minDate));
const maxDay = clearHours(new Date(this.maxDate));
const dateCountOfMonth = getDayCountOfMonth(date.getFullYear(), date.getMonth());
const dateCountOfLastMonth = getDayCountOfMonth(date.getFullYear(), (date.getMonth() === 0 ? 11 : date.getMonth() - 1));
const disabledDate = this.disabledDate;
let cells = [];
const cell_tmpl = {
text: '',
type: '',
selected: false,
disabled: false,
range: false,
start: false,
end: false
};
if (day !== 7) {
for (let i = 0; i < day; i++) {
const cell = deepCopy(cell_tmpl);
cell.type = 'prev-month';
cell.text = dateCountOfLastMonth - (day - 1) + i;
let prevMonth = this.month - 1;
let prevYear = this.year;
if (this.month === 0) {
prevMonth = 11;
prevYear -= 1;
}
const time = clearHours(new Date(prevYear, prevMonth, cell.text));
cell.disabled = typeof disabledDate === 'function' && disabledDate(new Date(time));
cells.push(cell);
}
}
for (let i = 1; i <= dateCountOfMonth; i++) {
const cell = deepCopy(cell_tmpl);
const time = clearHours(new Date(this.year, this.month, i));
cell.type = time === today ? 'today' : 'normal';
cell.text = i;
cell.selected = time === selectDay;
cell.disabled = typeof disabledDate === 'function' && disabledDate(new Date(time));
cell.range = time >= minDay && time <= maxDay;
cell.start = this.minDate && time === minDay;
cell.end = this.maxDate && time === maxDay;
cells.push(cell);
}
const nextMonthCount = 42 - cells.length;
for (let i = 1; i <= nextMonthCount; i++) {
const cell = deepCopy(cell_tmpl);
cell.type = 'next-month';
cell.text = i;
let nextMonth = this.month + 1;
let nextYear = this.year;
if (this.month === 11) {
nextMonth = 0;
nextYear += 1;
}
const time = clearHours(new Date(nextYear, nextMonth, cell.text));
cell.disabled = typeof disabledDate === 'function' && disabledDate(new Date(time));
cells.push(cell);
}
return cells;
}
},
methods: {
getDateOfCell (cell) {
let year = this.year;
let month = this.month;
let day = cell.text;
const date = this.date;
const hours = date.getHours();
const minutes = date.getMinutes();
const seconds = date.getSeconds();
if (cell.type === 'prev-month') {
if (month === 0) {
month = 11;
year--;
} else {
month--;
}
} else if (cell.type === 'next-month') {
if (month === 11) {
month = 0;
year++;
} else {
month++;
}
}
return new Date(year, month, day, hours, minutes, seconds);
},
handleClick (event) {
const target = event.target;
if (target.tagName === 'EM') {
const cell = this.cells[parseInt(event.target.getAttribute('index'))];
if (cell.disabled) return;
const newDate = this.getDateOfCell(cell);
if (this.selectionMode === 'range') {
if (this.minDate && this.maxDate) {
const minDate = new Date(newDate.getTime());
const maxDate = null;
this.rangeState.selecting = true;
this.markRange(this.minDate);
this.$emit('on-pick', {minDate, maxDate}, false);
} else if (this.minDate && !this.maxDate) {
if (newDate >= this.minDate) {
const maxDate = new Date(newDate.getTime());
this.rangeState.selecting = false;
this.$emit('on-pick', {minDate: this.minDate, maxDate});
} else {
const minDate = new Date(newDate.getTime());
this.$emit('on-pick', {minDate, maxDate: this.maxDate}, false);
}
} else if (!this.minDate) {
const minDate = new Date(newDate.getTime());
this.rangeState.selecting = true;
this.markRange(this.minDate);
this.$emit('on-pick', {minDate, maxDate: this.maxDate}, false);
}
} else {
this.$emit('on-pick', newDate);
}
}
this.$emit('on-pick-click');
},
handleMouseMove (event) {
if (!this.rangeState.selecting) return;
this.$emit('on-changerange', {
minDate: this.minDate,
maxDate: this.maxDate,
rangeState: this.rangeState
});
const target = event.target;
if (target.tagName === 'EM') {
const cell = this.cells[parseInt(event.target.getAttribute('index'))];
// if (cell.disabled) return; // todo
this.rangeState.endDate = this.getDateOfCell(cell);
}
},
markRange (maxDate) {
const minDate = this.minDate;
if (!maxDate) maxDate = this.maxDate;
const minDay = clearHours(new Date(minDate));
const maxDay = clearHours(new Date(maxDate));
this.cells.forEach(cell => {
if (cell.type === 'today' || cell.type === 'normal') {
const time = clearHours(new Date(this.year, this.month, cell.text));
cell.range = time >= minDay && time <= maxDay;
cell.start = minDate && time === minDay;
cell.end = maxDate && time === maxDay;
}
});
},
getCellCls (cell) {
return [
`${prefixCls}-cell`,
{
[`${prefixCls}-cell-selected`]: cell.selected || cell.start || cell.end,
[`${prefixCls}-cell-disabled`]: cell.disabled,
[`${prefixCls}-cell-today`]: cell.type === 'today',
[`${prefixCls}-cell-prev-month`]: cell.type === 'prev-month',
[`${prefixCls}-cell-next-month`]: cell.type === 'next-month',
[`${prefixCls}-cell-range`]: cell.range && !cell.start && !cell.end
}
];
},
}
};
</script>

View file

@ -1,79 +0,0 @@
<template>
<div :class="classes" @click="handleClick">
<span :class="getCellCls(cell)" v-for="cell in cells"><em :index="$index">{{ tCell(cell.text) }}</em></span>
</div>
</template>
<script>
import { deepCopy } from '../../../utils/assist';
import Locale from '../../../mixins/locale';
const prefixCls = 'ivu-date-picker-cells';
export default {
mixins: [ Locale ],
props: {
date: {},
month: {
type: Number
},
disabledDate: {},
selectionMode: {
default: 'month'
}
},
computed: {
classes () {
return [
`${prefixCls}`,
`${prefixCls}-month`
];
},
cells () {
let cells = [];
const cell_tmpl = {
text: '',
selected: false,
disabled: false
};
for (let i = 0; i < 12; i++) {
const cell = deepCopy(cell_tmpl);
cell.text = i + 1;
const date = new Date(this.date);
date.setMonth(i);
cell.disabled = typeof this.disabledDate === 'function' && this.disabledDate(date) && this.selectionMode === 'month';
cell.selected = Number(this.month) === i;
cells.push(cell);
}
return cells;
}
},
methods: {
getCellCls (cell) {
return [
`${prefixCls}-cell`,
{
[`${prefixCls}-cell-selected`]: cell.selected,
[`${prefixCls}-cell-disabled`]: cell.disabled
}
];
},
handleClick (event) {
const target = event.target;
if (target.tagName === 'EM') {
const index = parseInt(event.target.getAttribute('index'));
const cell = this.cells[index];
if (cell.disabled) return;
this.$emit('on-pick', index);
}
this.$emit('on-pick-click');
},
tCell (cell) {
return this.t(`i.datepicker.months.m${cell}`);
}
}
};
</script>

View file

@ -1,207 +0,0 @@
<template>
<div :class="classes">
<div :class="[prefixCls+ '-list']" v-el:hours>
<ul :class="[prefixCls + '-ul']" @click="handleClickHours">
<li :class="getCellCls(item)" v-for="item in hoursList" v-show="!item.hide" :index="$index">{{ formatTime(item.text) }}</li>
</ul>
</div>
<div :class="[prefixCls+ '-list']" v-el:minutes>
<ul :class="[prefixCls + '-ul']" @click="handleClickMinutes">
<li :class="getCellCls(item)" v-for="item in minutesList" v-show="!item.hide" :index="$index">{{ formatTime(item.text) }}</li>
</ul>
</div>
<div :class="[prefixCls+ '-list']" v-show="showSeconds" v-el:seconds>
<ul :class="[prefixCls + '-ul']" @click="handleClickSeconds">
<li :class="getCellCls(item)" v-for="item in secondsList" v-show="!item.hide" :index="$index">{{ formatTime(item.text) }}</li>
</ul>
</div>
</div>
</template>
<script>
import Options from '../time-mixins';
import { deepCopy, scrollTop, firstUpperCase } from '../../../utils/assist';
const prefixCls = 'ivu-time-picker-cells';
export default {
mixins: [Options],
props: {
hours: {
type: [Number, String],
default: 0
},
minutes: {
type: [Number, String],
default: 0
},
seconds: {
type: [Number, String],
default: 0
},
showSeconds: {
type: Boolean,
default: true
}
},
data () {
return {
prefixCls: prefixCls,
compiled: false
};
},
computed: {
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-with-seconds`]: this.showSeconds
}
];
},
hoursList () {
let hours = [];
const hour_tmpl = {
text: 0,
selected: false,
disabled: false,
hide: false
};
for (let i = 0; i < 24; i++) {
const hour = deepCopy(hour_tmpl);
hour.text = i;
if (this.disabledHours.length && this.disabledHours.indexOf(i) > -1) {
hour.disabled = true;
if (this.hideDisabledOptions) hour.hide = true;
}
if (this.hours === i) hour.selected = true;
hours.push(hour);
}
return hours;
},
minutesList () {
let minutes = [];
const minute_tmpl = {
text: 0,
selected: false,
disabled: false,
hide: false
};
for (let i = 0; i < 60; i++) {
const minute = deepCopy(minute_tmpl);
minute.text = i;
if (this.disabledMinutes.length && this.disabledMinutes.indexOf(i) > -1) {
minute.disabled = true;
if (this.hideDisabledOptions) minute.hide = true;
}
if (this.minutes === i) minute.selected = true;
minutes.push(minute);
}
return minutes;
},
secondsList () {
let seconds = [];
const second_tmpl = {
text: 0,
selected: false,
disabled: false,
hide: false
};
for (let i = 0; i < 60; i++) {
const second = deepCopy(second_tmpl);
second.text = i;
if (this.disabledSeconds.length && this.disabledSeconds.indexOf(i) > -1) {
second.disabled = true;
if (this.hideDisabledOptions) second.hide = true;
}
if (this.seconds === i) second.selected = true;
seconds.push(second);
}
return seconds;
}
},
methods: {
getCellCls (cell) {
return [
`${prefixCls}-cell`,
{
[`${prefixCls}-cell-selected`]: cell.selected,
[`${prefixCls}-cell-disabled`]: cell.disabled
}
];
},
handleClickHours (event) {
this.handleClick('hours', event);
},
handleClickMinutes (event) {
this.handleClick('minutes', event);
},
handleClickSeconds (event) {
this.handleClick('seconds', event);
},
handleClick (type, event) {
const target = event.target;
if (target.tagName === 'LI') {
const cell = this[`${type}List`][parseInt(event.target.getAttribute('index'))];
if (cell.disabled) return;
const data = {};
data[type] = cell.text;
this.$emit('on-change', data);
}
this.$emit('on-pick-click');
},
scroll (type, index) {
const from = this.$els[type].scrollTop;
const to = 24 * this.getScrollIndex(type, index);
scrollTop(this.$els[type], from, to, 500);
},
getScrollIndex (type, index) {
const Type = firstUpperCase(type);
const disabled = this[`disabled${Type}`];
if (disabled.length && this.hideDisabledOptions) {
let _count = 0;
disabled.forEach(item => item <= index ? _count++ : '');
index -= _count;
}
return index;
},
updateScroll () {
const times = ['hours', 'minutes', 'seconds'];
this.$nextTick(() => {
times.forEach(type => {
this.$els[type].scrollTop = 24 * this.getScrollIndex(type, this[type]);
});
});
},
formatTime (text) {
return text < 10 ? '0' + text : text;
}
},
watch: {
hours (val) {
if (!this.compiled) return;
this.scroll('hours', val);
},
minutes (val) {
if (!this.compiled) return;
this.scroll('minutes', val);
},
seconds (val) {
if (!this.compiled) return;
this.scroll('seconds', val);
}
},
compiled () {
this.updateScroll();
this.$nextTick(() => this.compiled = true);
}
};
</script>

View file

@ -1,80 +0,0 @@
<template>
<div :class="classes" @click="handleClick">
<span :class="getCellCls(cell)" v-for="cell in cells"><em :index="$index">{{ cell.text }}</em></span>
</div>
</template>
<script>
import { deepCopy } from '../../../utils/assist';
const prefixCls = 'ivu-date-picker-cells';
export default {
props: {
date: {},
year: {},
disabledDate: {},
selectionMode: {
default: 'year'
}
},
computed: {
classes () {
return [
`${prefixCls}`,
`${prefixCls}-year`
];
},
startYear() {
return Math.floor(this.year / 10) * 10;
},
cells () {
let cells = [];
const cell_tmpl = {
text: '',
selected: false,
disabled: false
};
for (let i = 0; i < 10; i++) {
const cell = deepCopy(cell_tmpl);
cell.text = this.startYear + i;
const date = new Date(this.date);
date.setFullYear(cell.text);
cell.disabled = typeof this.disabledDate === 'function' && this.disabledDate(date) && this.selectionMode === 'year';
cell.selected = Number(this.year) === cell.text;
cells.push(cell);
}
return cells;
}
},
methods: {
getCellCls (cell) {
return [
`${prefixCls}-cell`,
{
[`${prefixCls}-cell-selected`]: cell.selected,
[`${prefixCls}-cell-disabled`]: cell.disabled
}
];
},
nextTenYear() {
this.$emit('on-pick', Number(this.year) + 10, false);
},
prevTenYear() {
this.$emit('on-pick', Number(this.year) - 10, false);
},
handleClick (event) {
const target = event.target;
if (target.tagName === 'EM') {
const cell = this.cells[parseInt(event.target.getAttribute('index'))];
if (cell.disabled) return;
this.$emit('on-pick', cell.text);
}
this.$emit('on-pick-click');
}
}
};
</script>

View file

@ -1,3 +0,0 @@
import DatePicker from './picker/date-picker';
export default DatePicker;

View file

@ -1,421 +0,0 @@
<template>
<div :class="classes">
<div :class="[prefixCls + '-sidebar']" v-if="shortcuts.length">
<div
:class="[prefixCls + '-shortcut']"
v-for="shortcut in shortcuts"
@click="handleShortcutClick(shortcut)">{{ shortcut.text }}</div>
</div>
<div :class="[prefixCls + '-body']">
<div :class="[prefixCls + '-content', prefixCls + '-content-left']" v-show="!isTime">
<div :class="[datePrefixCls + '-header']" v-show="leftCurrentView !== 'time'">
<span
:class="iconBtnCls('prev', '-double')"
@click="prevYear('left')"><Icon type="ios-arrow-left"></Icon></span>
<span
:class="iconBtnCls('prev')"
@click="prevMonth"
v-show="leftCurrentView === 'date'"><Icon type="ios-arrow-left"></Icon></span>
<span
:class="[datePrefixCls + '-header-label']"
@click="showYearPicker('left')">{{ leftYearLabel }}</span>
<span
:class="[datePrefixCls + '-header-label']"
@click="showMonthPicker('left')"
v-show="leftCurrentView === 'date'">{{ leftMonthLabel }}</span>
<span
:class="iconBtnCls('next', '-double')"
@click="nextYear('left')"
v-show="leftCurrentView === 'year' || leftCurrentView === 'month'"><Icon type="ios-arrow-right"></Icon></span>
</div>
<date-table
v-show="leftCurrentView === 'date'"
:year="leftYear"
:month="leftMonth"
:date="date"
:min-date="minDate"
:max-date="maxDate"
:range-state="rangeState"
selection-mode="range"
:disabled-date="disabledDate"
@on-changerange="handleChangeRange"
@on-pick="handleRangePick"
@on-pick-click="handlePickClick"></date-table>
<year-table
v-ref:left-year-table
v-show="leftCurrentView === 'year'"
:year="leftTableYear"
:date="leftTableDate"
selection-mode="range"
:disabled-date="disabledDate"
@on-pick="handleLeftYearPick"
@on-pick-click="handlePickClick"></year-table>
<month-table
v-ref:left-month-table
v-show="leftCurrentView === 'month'"
:month="leftMonth"
:date="leftTableDate"
selection-mode="range"
:disabled-date="disabledDate"
@on-pick="handleLeftMonthPick"
@on-pick-click="handlePickClick"></month-table>
</div>
<div :class="[prefixCls + '-content', prefixCls + '-content-right']" v-show="!isTime">
<div :class="[datePrefixCls + '-header']" v-show="rightCurrentView !== 'time'">
<span
:class="iconBtnCls('prev', '-double')"
@click="prevYear('right')"
v-show="rightCurrentView === 'year' || rightCurrentView === 'month'"><Icon type="ios-arrow-left"></Icon></span>
<span
:class="[datePrefixCls + '-header-label']"
@click="showYearPicker('right')">{{ rightYearLabel }}</span>
<span
:class="[datePrefixCls + '-header-label']"
@click="showMonthPicker('right')"
v-show="rightCurrentView === 'date'">{{ rightMonthLabel }}</span>
<span
:class="iconBtnCls('next', '-double')"
@click="nextYear('right')"><Icon type="ios-arrow-right"></Icon></span>
<span
:class="iconBtnCls('next')"
@click="nextMonth"
v-show="rightCurrentView === 'date'"><Icon type="ios-arrow-right"></Icon></span>
</div>
<date-table
v-show="rightCurrentView === 'date'"
:year="rightYear"
:month="rightMonth"
:date="rightDate"
:min-date="minDate"
:max-date="maxDate"
:range-state="rangeState"
selection-mode="range"
:disabled-date="disabledDate"
@on-changerange="handleChangeRange"
@on-pick="handleRangePick"
@on-pick-click="handlePickClick"></date-table>
<year-table
v-ref:right-year-table
v-show="rightCurrentView === 'year'"
:year="rightTableYear"
:date="rightTableDate"
selection-mode="range"
:disabled-date="disabledDate"
@on-pick="handleRightYearPick"
@on-pick-click="handlePickClick"></year-table>
<month-table
v-ref:right-month-table
v-show="rightCurrentView === 'month'"
:month="rightMonth"
:date="rightTableDate"
selection-mode="range"
:disabled-date="disabledDate"
@on-pick="handleRightMonthPick"
@on-pick-click="handlePickClick"></month-table>
</div>
<div :class="[prefixCls + '-content']" v-show="isTime">
<time-picker
v-ref:time-picker
v-show="isTime"
@on-pick="handleTimePick"
@on-pick-click="handlePickClick"></time-picker>
</div>
<Confirm
v-if="confirm"
:show-time="showTime"
:is-time="isTime"
:time-disabled="timeDisabled"
@on-pick-toggle-time="handleToggleTime"
@on-pick-clear="handlePickClear"
@on-pick-success="handlePickSuccess"></Confirm>
</div>
</div>
</template>
<script>
import Icon from '../../icon/icon.vue';
import DateTable from '../base/date-table.vue';
import YearTable from '../base/year-table.vue';
import MonthTable from '../base/month-table.vue';
import TimePicker from './time-range.vue';
import Confirm from '../base/confirm.vue';
import { toDate, prevMonth, nextMonth, initTimeDate } from '../util';
import Mixin from './mixin';
import Locale from '../../../mixins/locale';
const prefixCls = 'ivu-picker-panel';
const datePrefixCls = 'ivu-date-picker';
export default {
name: 'DatePicker',
mixins: [ Mixin, Locale ],
components: { Icon, DateTable, YearTable, MonthTable, TimePicker, Confirm },
data () {
return {
prefixCls: prefixCls,
datePrefixCls: datePrefixCls,
shortcuts: [],
date: initTimeDate(),
value: '',
minDate: '',
maxDate: '',
confirm: false,
rangeState: {
endDate: null,
selecting: false
},
showTime: false,
disabledDate: '',
leftCurrentView: 'date',
rightCurrentView: 'date',
selectionMode: 'range',
leftTableYear: null,
rightTableYear: null,
isTime: false,
format: 'yyyy-MM-dd'
};
},
computed: {
classes () {
return [
`${prefixCls}-body-wrapper`,
`${datePrefixCls}-with-range`,
{
[`${prefixCls}-with-sidebar`]: this.shortcuts.length
}
];
},
leftYear () {
return this.date.getFullYear();
},
leftTableDate () {
if (this.leftCurrentView === 'year' || this.leftCurrentView === 'month') {
return new Date(this.leftTableYear);
} else {
return this.date;
}
},
leftYearLabel () {
const tYear = this.t('i.datepicker.year');
if (this.leftCurrentView === 'year') {
const year = this.leftTableYear;
if (!year) return '';
const startYear = Math.floor(year / 10) * 10;
return `${startYear}${tYear} - ${startYear + 9}${tYear}`;
} else {
const year = this.leftCurrentView === 'month' ? this.leftTableYear : this.leftYear;
if (!year) return '';
return `${year}${tYear}`;
}
},
leftMonth () {
return this.date.getMonth();
},
leftMonthLabel () {
const month = this.leftMonth + 1;
return this.t(`i.datepicker.month${month}`);
},
rightYear () {
return this.rightDate.getFullYear();
},
rightTableDate () {
if (this.rightCurrentView === 'year' || this.rightCurrentView === 'month') {
return new Date(this.rightTableYear);
} else {
return this.date;
}
},
rightYearLabel () {
const tYear = this.t('i.datepicker.year');
if (this.rightCurrentView === 'year') {
const year = this.rightTableYear;
if (!year) return '';
const startYear = Math.floor(year / 10) * 10;
return `${startYear}${tYear} - ${startYear + 9}${tYear}`;
} else {
const year = this.rightCurrentView === 'month' ? this.rightTableYear : this.rightYear;
if (!year) return '';
return `${year}${tYear}`;
}
},
rightMonth () {
return this.rightDate.getMonth();
},
rightMonthLabel () {
const month = this.rightMonth + 1;
return this.t(`i.datepicker.month${month}`);
},
rightDate () {
const newDate = new Date(this.date);
const month = newDate.getMonth();
newDate.setDate(1);
if (month === 11) {
newDate.setFullYear(newDate.getFullYear() + 1);
newDate.setMonth(0);
} else {
newDate.setMonth(month + 1);
}
return newDate;
},
timeDisabled () {
return !(this.minDate && this.maxDate);
}
},
watch: {
value(newVal) {
if (!newVal) {
this.minDate = null;
this.maxDate = null;
} else if (Array.isArray(newVal)) {
this.minDate = newVal[0] ? toDate(newVal[0]) : null;
this.maxDate = newVal[1] ? toDate(newVal[1]) : null;
if (this.minDate) this.date = new Date(this.minDate);
}
if (this.showTime) this.$refs.timePicker.value = newVal;
},
minDate (val) {
if (this.showTime) this.$refs.timePicker.date = val;
},
maxDate (val) {
if (this.showTime) this.$refs.timePicker.dateEnd = val;
},
format (val) {
if (this.showTime) this.$refs.timePicker.format = val;
},
isTime (val) {
if (val) this.$refs.timePicker.updateScroll();
}
},
methods: {
resetDate () {
this.date = new Date(this.date);
this.leftTableYear = this.date.getFullYear();
this.rightTableYear = this.rightDate.getFullYear();
},
handleClear() {
this.minDate = null;
this.maxDate = null;
this.date = new Date();
this.handleConfirm();
if (this.showTime) this.$refs.timePicker.handleClear();
},
resetView(reset = false) {
this.leftCurrentView = 'date';
this.rightCurrentView = 'date';
this.leftTableYear = this.leftYear;
this.rightTableYear = this.rightYear;
if (reset) this.isTime = false;
},
prevYear (direction) {
if (this[`${direction}CurrentView`] === 'year') {
this.$refs[`${direction}YearTable`].prevTenYear();
} else if (this[`${direction}CurrentView`] === 'month') {
this[`${direction}TableYear`]--;
} else {
const date = this.date;
date.setFullYear(date.getFullYear() - 1);
this.resetDate();
}
},
nextYear (direction) {
if (this[`${direction}CurrentView`] === 'year') {
this.$refs[`${direction}YearTable`].nextTenYear();
} else if (this[`${direction}CurrentView`] === 'month') {
this[`${direction}TableYear`]--;
} else {
const date = this.date;
date.setFullYear(date.getFullYear() + 1);
this.resetDate();
}
},
prevMonth () {
this.date = prevMonth(this.date);
},
nextMonth () {
this.date = nextMonth(this.date);
},
handleLeftYearPick (year, close = true) {
this.handleYearPick(year, close, 'left');
},
handleRightYearPick (year, close = true) {
this.handleYearPick(year, close, 'right');
},
handleYearPick (year, close, direction) {
this[`${direction}TableYear`] = year;
if (!close) return;
this[`${direction}CurrentView`] = 'month';
},
handleLeftMonthPick (month) {
this.handleMonthPick(month, 'left');
},
handleRightMonthPick (month) {
this.handleMonthPick(month, 'right');
},
handleMonthPick (month, direction) {
let year = this[`${direction}TableYear`];
if (direction === 'right') {
if (month === 0) {
month = 11;
year--;
} else {
month--;
}
}
this.date.setYear(year);
this.date.setMonth(month);
this[`${direction}CurrentView`] = 'date';
this.resetDate();
},
showYearPicker (direction) {
this[`${direction}CurrentView`] = 'year';
this[`${direction}TableYear`] = this[`${direction}Year`];
},
showMonthPicker (direction) {
this[`${direction}CurrentView`] = 'month';
},
handleConfirm(visible) {
this.$emit('on-pick', [this.minDate, this.maxDate], visible);
},
handleRangePick (val, close = true) {
if (this.maxDate === val.maxDate && this.minDate === val.minDate) return;
this.minDate = val.minDate;
this.maxDate = val.maxDate;
if (!close) return;
// if (!this.showTime) {
// this.handleConfirm(false);
// }
this.handleConfirm(false);
},
handleChangeRange (val) {
this.minDate = val.minDate;
this.maxDate = val.maxDate;
this.rangeState = val.rangeState;
},
handleToggleTime () {
this.isTime = !this.isTime;
},
handleTimePick (date) {
this.minDate = date[0];
this.maxDate = date[1];
this.handleConfirm(false);
}
},
compiled () {
if (this.showTime) {
// todo
this.$refs.timePicker.date = this.minDate;
this.$refs.timePicker.dateEnd = this.maxDate;
this.$refs.timePicker.value = this.value;
this.$refs.timePicker.format = this.format;
this.$refs.timePicker.showDate = true;
}
}
};
</script>

View file

@ -1,293 +0,0 @@
<template>
<div :class="classes">
<div :class="[prefixCls + '-sidebar']" v-if="shortcuts.length">
<div
:class="[prefixCls + '-shortcut']"
v-for="shortcut in shortcuts"
@click="handleShortcutClick(shortcut)">{{ shortcut.text }}</div>
</div>
<div :class="[prefixCls + '-body']">
<div :class="[datePrefixCls + '-header']" v-show="currentView !== 'time'">
<span
:class="iconBtnCls('prev', '-double')"
@click="prevYear"><Icon type="ios-arrow-left"></Icon></span>
<span
:class="iconBtnCls('prev')"
@click="prevMonth"
v-show="currentView === 'date'"><Icon type="ios-arrow-left"></Icon></span>
<span
:class="[datePrefixCls + '-header-label']"
@click="showYearPicker">{{ yearLabel }}</span>
<span
:class="[datePrefixCls + '-header-label']"
@click="showMonthPicker"
v-show="currentView === 'date'">{{ monthLabel }}</span>
<span
:class="iconBtnCls('next', '-double')"
@click="nextYear"><Icon type="ios-arrow-right"></Icon></span>
<span
:class="iconBtnCls('next')"
@click="nextMonth"
v-show="currentView === 'date'"><Icon type="ios-arrow-right"></Icon></span>
</div>
<div :class="[prefixCls + '-content']">
<date-table
v-show="currentView === 'date'"
:year="year"
:month="month"
:date="date"
:value="value"
:selection-mode="selectionMode"
:disabled-date="disabledDate"
@on-pick="handleDatePick"
@on-pick-click="handlePickClick"></date-table>
<year-table
v-ref:year-table
v-show="currentView === 'year'"
:year="year"
:date="date"
:selection-mode="selectionMode"
:disabled-date="disabledDate"
@on-pick="handleYearPick"
@on-pick-click="handlePickClick"></year-table>
<month-table
v-ref:month-table
v-show="currentView === 'month'"
:month="month"
:date="date"
:selection-mode="selectionMode"
:disabled-date="disabledDate"
@on-pick="handleMonthPick"
@on-pick-click="handlePickClick"></month-table>
<time-picker
v-ref:time-picker
show-date
v-show="currentView === 'time'"
@on-pick="handleTimePick"
@on-pick-click="handlePickClick"></time-picker>
</div>
<Confirm
v-if="confirm"
:show-time="showTime"
:is-time="isTime"
@on-pick-toggle-time="handleToggleTime"
@on-pick-clear="handlePickClear"
@on-pick-success="handlePickSuccess"></Confirm>
</div>
</div>
</template>
<script>
import Icon from '../../icon/icon.vue';
import DateTable from '../base/date-table.vue';
import YearTable from '../base/year-table.vue';
import MonthTable from '../base/month-table.vue';
import TimePicker from './time.vue';
import Confirm from '../base/confirm.vue';
import Mixin from './mixin';
import Locale from '../../../mixins/locale';
import { initTimeDate } from '../util';
const prefixCls = 'ivu-picker-panel';
const datePrefixCls = 'ivu-date-picker';
export default {
name: 'DatePicker',
mixins: [ Mixin, Locale ],
components: { Icon, DateTable, YearTable, MonthTable, TimePicker, Confirm },
data () {
return {
prefixCls: prefixCls,
datePrefixCls: datePrefixCls,
shortcuts: [],
currentView: 'date',
date: initTimeDate(),
value: '',
showTime: false,
selectionMode: 'day',
disabledDate: '',
year: null,
month: null,
confirm: false,
isTime: false,
format: 'yyyy-MM-dd'
};
},
computed: {
classes () {
return [
`${prefixCls}-body-wrapper`,
{
[`${prefixCls}-with-sidebar`]: this.shortcuts.length
}
];
},
yearLabel () {
const tYear = this.t('i.datepicker.year');
const year = this.year;
if (!year) return '';
if (this.currentView === 'year') {
const startYear = Math.floor(year / 10) * 10;
return `${startYear}${tYear} - ${startYear + 9}${tYear}`;
}
return `${year}${tYear}`;
},
monthLabel () {
const month = this.month + 1;
return this.t(`i.datepicker.month${month}`);
}
},
watch: {
value (newVal) {
if (!newVal) return;
newVal = new Date(newVal);
if (!isNaN(newVal)) {
this.date = newVal;
this.year = newVal.getFullYear();
this.month = newVal.getMonth();
}
if (this.showTime) this.$refs.timePicker.value = newVal;
},
date (val) {
if (this.showTime) this.$refs.timePicker.date = val;
},
format (val) {
if (this.showTime) this.$refs.timePicker.format = val;
},
currentView (val) {
if (val === 'time') this.$refs.timePicker.updateScroll();
}
},
methods: {
resetDate () {
this.date = new Date(this.date);
},
handleClear () {
this.date = new Date();
this.$emit('on-pick', '');
if (this.showTime) this.$refs.timePicker.handleClear();
},
resetView (reset = false) {
if (this.currentView !== 'time' || reset) {
if (this.selectionMode === 'month') {
this.currentView = 'month';
} else if (this.selectionMode === 'year') {
this.currentView = 'year';
} else {
this.currentView = 'date';
}
}
this.year = this.date.getFullYear();
this.month = this.date.getMonth();
if (reset) this.isTime = false;
},
prevYear () {
if (this.currentView === 'year') {
this.$refs.yearTable.prevTenYear();
} else {
this.year--;
this.date.setFullYear(this.year);
this.resetDate();
}
},
nextYear () {
if (this.currentView === 'year') {
this.$refs.yearTable.nextTenYear();
} else {
this.year++;
this.date.setFullYear(this.year);
this.resetDate();
}
},
prevMonth () {
this.month--;
if (this.month < 0) {
this.month = 11;
this.year--;
}
},
nextMonth () {
this.month++;
if (this.month > 11) {
this.month = 0;
this.year++;
}
},
showYearPicker () {
this.currentView = 'year';
},
showMonthPicker () {
this.currentView = 'month';
},
handleToggleTime () {
if (this.currentView === 'date') {
this.currentView = 'time';
this.isTime = true;
} else if (this.currentView === 'time') {
this.currentView = 'date';
this.isTime = false;
}
},
handleYearPick(year, close = true) {
this.year = year;
if (!close) return;
this.date.setFullYear(year);
if (this.selectionMode === 'year') {
this.$emit('on-pick', new Date(year, 0, 1));
} else {
this.currentView = 'month';
}
this.resetDate();
},
handleMonthPick (month) {
this.month = month;
const selectionMode = this.selectionMode;
if (selectionMode !== 'month') {
this.date.setMonth(month);
this.currentView = 'date';
this.resetDate();
} else {
this.date.setMonth(month);
this.year && this.date.setFullYear(this.year);
this.resetDate();
const value = new Date(this.date.getFullYear(), month, 1);
this.$emit('on-pick', value);
}
},
handleDatePick (value) {
if (this.selectionMode === 'day') {
this.$emit('on-pick', new Date(value.getTime()));
this.date.setFullYear(value.getFullYear());
this.date.setMonth(value.getMonth());
this.date.setDate(value.getDate());
}
this.resetDate();
},
handleTimePick (date) {
this.handleDatePick(date);
}
},
compiled () {
if (this.selectionMode === 'month') {
this.currentView = 'month';
}
if (this.date && !this.year) {
this.year = this.date.getFullYear();
this.month = this.date.getMonth();
}
if (this.showTime) {
// todo 使
this.$refs.timePicker.date = this.date;
this.$refs.timePicker.value = this.value;
this.$refs.timePicker.format = this.format;
this.$refs.timePicker.showDate = true;
}
}
};
</script>

View file

@ -1,27 +0,0 @@
const prefixCls = 'ivu-picker-panel';
const datePrefixCls = 'ivu-date-picker';
export default {
methods: {
iconBtnCls (direction, type = '') {
return [
`${prefixCls}-icon-btn`,
`${datePrefixCls}-${direction}-btn`,
`${datePrefixCls}-${direction}-btn-arrow${type}`,
];
},
handleShortcutClick (shortcut) {
if (shortcut.value) this.$emit('on-pick', shortcut.value());
if (shortcut.onClick) shortcut.onClick(this);
},
handlePickClear () {
this.$emit('on-pick-clear');
},
handlePickSuccess () {
this.$emit('on-pick-success');
},
handlePickClick () {
this.$emit('on-pick-click');
}
}
};

View file

@ -1,207 +0,0 @@
<template>
<div :class="classes">
<div :class="[prefixCls + '-body']">
<div :class="[prefixCls + '-content', prefixCls + '-content-left']">
<div :class="[timePrefixCls + '-header']">
<template v-if="showDate">{{ visibleDate }}</template>
<template v-else>{{ t('i.datepicker.startTime') }}</template>
</div>
<time-spinner
v-ref:time-spinner
:show-seconds="showSeconds"
:hours="hours"
:minutes="minutes"
:seconds="seconds"
:disabled-hours="disabledHours"
:disabled-minutes="disabledMinutes"
:disabled-seconds="disabledSeconds"
:hide-disabled-options="hideDisabledOptions"
@on-change="handleStartChange"
@on-pick-click="handlePickClick"></time-spinner>
</div>
<div :class="[prefixCls + '-content', prefixCls + '-content-right']">
<div :class="[timePrefixCls + '-header']">
<template v-if="showDate">{{ visibleDateEnd }}</template>
<template v-else>{{ t('i.datepicker.endTime') }}</template>
</div>
<time-spinner
v-ref:time-spinner-end
:show-seconds="showSeconds"
:hours="hoursEnd"
:minutes="minutesEnd"
:seconds="secondsEnd"
:disabled-hours="disabledHours"
:disabled-minutes="disabledMinutes"
:disabled-seconds="disabledSeconds"
:hide-disabled-options="hideDisabledOptions"
@on-change="handleEndChange"
@on-pick-click="handlePickClick"></time-spinner>
</div>
<Confirm
v-if="confirm"
@on-pick-clear="handlePickClear"
@on-pick-success="handlePickSuccess"></Confirm>
</div>
</div>
</template>
<script>
import TimeSpinner from '../base/time-spinner.vue';
import Confirm from '../base/confirm.vue';
import Mixin from './mixin';
import Locale from '../../../mixins/locale';
import { initTimeDate, toDate, formatDate } from '../util';
const prefixCls = 'ivu-picker-panel';
const timePrefixCls = 'ivu-time-picker';
export default {
mixins: [ Mixin, Locale ],
components: { TimeSpinner, Confirm },
data () {
return {
prefixCls: prefixCls,
timePrefixCls: timePrefixCls,
format: 'HH:mm:ss',
showDate: false,
date: initTimeDate(),
dateEnd: initTimeDate(),
value: '',
hours: '',
minutes: '',
seconds: '',
hoursEnd: '',
minutesEnd: '',
secondsEnd: '',
disabledHours: [],
disabledMinutes: [],
disabledSeconds: [],
hideDisabledOptions: false,
confirm: false
};
},
computed: {
classes () {
return [
`${prefixCls}-body-wrapper`,
`${timePrefixCls}-with-range`,
{
[`${timePrefixCls}-with-seconds`]: this.showSeconds
}
];
},
showSeconds () {
return (this.format || '').indexOf('ss') !== -1;
},
visibleDate () {
const date = this.date || initTimeDate();
const tYear = this.t('i.datepicker.year');
const month = date.getMonth() + 1;
const tMonth = this.t(`i.datepicker.month${month}`);
return `${date.getFullYear()}${tYear} ${tMonth}`;
},
visibleDateEnd () {
const date = this.dateEnd || initTimeDate();
const tYear = this.t('i.datepicker.year');
const month = date.getMonth() + 1;
const tMonth = this.t(`i.datepicker.month${month}`);
return `${date.getFullYear()}${tYear} ${tMonth}`;
}
},
watch: {
value (newVal) {
if (!newVal) return;
if (Array.isArray(newVal)) {
const valStart = newVal[0] ? toDate(newVal[0]) : false;
const valEnd = newVal[1] ? toDate(newVal[1]) : false;
if (valStart && valEnd) {
this.handleChange(
{
hours: valStart.getHours(),
minutes: valStart.getMinutes(),
seconds: valStart.getSeconds()
},
{
hours: valEnd.getHours(),
minutes: valEnd.getMinutes(),
seconds: valEnd.getSeconds()
},
false
);
}
}
}
},
methods: {
handleClear() {
this.date = initTimeDate();
this.dateEnd = initTimeDate();
this.hours = '';
this.minutes = '';
this.seconds = '';
this.hoursEnd = '';
this.minutesEnd = '';
this.secondsEnd = '';
},
handleChange (date, dateEnd, emit = true) {
const oldDateEnd = new Date(this.dateEnd);
if (date.hours !== undefined) {
this.date.setHours(date.hours);
this.hours = this.date.getHours();
}
if (date.minutes !== undefined) {
this.date.setMinutes(date.minutes);
this.minutes = this.date.getMinutes();
}
if (date.seconds !== undefined) {
this.date.setSeconds(date.seconds);
this.seconds = this.date.getSeconds();
}
if (dateEnd.hours !== undefined) {
this.dateEnd.setHours(dateEnd.hours);
this.hoursEnd = this.dateEnd.getHours();
}
if (dateEnd.minutes !== undefined) {
this.dateEnd.setMinutes(dateEnd.minutes);
this.minutesEnd = this.dateEnd.getMinutes();
}
if (dateEnd.seconds !== undefined) {
this.dateEnd.setSeconds(dateEnd.seconds);
this.secondsEnd = this.dateEnd.getSeconds();
}
// judge endTime > startTime?
if (this.dateEnd < this.date) {
this.$nextTick(() => {
this.dateEnd = new Date(this.date);
this.hoursEnd = this.dateEnd.getHours();
this.minutesEnd = this.dateEnd.getMinutes();
this.secondsEnd = this.dateEnd.getSeconds();
const format = 'yyyy-MM-dd HH:mm:ss';
if (formatDate(oldDateEnd, format) !== formatDate(this.dateEnd, format)) {
if (emit) this.$emit('on-pick', [this.date, this.dateEnd], true);
}
});
} else {
if (emit) this.$emit('on-pick', [this.date, this.dateEnd], true);
}
},
handleStartChange (date) {
this.handleChange(date, {});
},
handleEndChange (date) {
this.handleChange({}, date);
},
updateScroll () {
this.$refs.timeSpinner.updateScroll();
this.$refs.timeSpinnerEnd.updateScroll();
}
},
compiled () {
if (this.$parent && this.$parent.$options.name === 'DatePicker') this.showDate = true;
}
};
</script>

View file

@ -1,115 +0,0 @@
<template>
<div :class="[prefixCls + '-body-wrapper']">
<div :class="[prefixCls + '-body']">
<div :class="[timePrefixCls + '-header']" v-if="showDate">{{ visibleDate }}</div>
<div :class="[prefixCls + '-content']">
<time-spinner
v-ref:time-spinner
:show-seconds="showSeconds"
:hours="hours"
:minutes="minutes"
:seconds="seconds"
:disabled-hours="disabledHours"
:disabled-minutes="disabledMinutes"
:disabled-seconds="disabledSeconds"
:hide-disabled-options="hideDisabledOptions"
@on-change="handleChange"
@on-pick-click="handlePickClick"></time-spinner>
</div>
<Confirm
v-if="confirm"
@on-pick-clear="handlePickClear"
@on-pick-success="handlePickSuccess"></Confirm>
</div>
</div>
</template>
<script>
import TimeSpinner from '../base/time-spinner.vue';
import Confirm from '../base/confirm.vue';
import Mixin from './mixin';
import Locale from '../../../mixins/locale';
import { initTimeDate } from '../util';
const prefixCls = 'ivu-picker-panel';
const timePrefixCls = 'ivu-time-picker';
export default {
mixins: [ Mixin, Locale ],
components: { TimeSpinner, Confirm },
data () {
return {
prefixCls: prefixCls,
timePrefixCls: timePrefixCls,
date: initTimeDate(),
value: '',
showDate: false,
format: 'HH:mm:ss',
hours: '',
minutes: '',
seconds: '',
disabledHours: [],
disabledMinutes: [],
disabledSeconds: [],
hideDisabledOptions: false,
confirm: false
};
},
computed: {
showSeconds () {
return (this.format || '').indexOf('ss') !== -1;
},
visibleDate () {
const date = this.date;
const month = date.getMonth() + 1;
const tYear = this.t('i.datepicker.year');
const tMonth = this.t(`i.datepicker.month${month}`);
return `${date.getFullYear()}${tYear} ${tMonth}`;
}
},
watch: {
value (newVal) {
if (!newVal) return;
newVal = new Date(newVal);
if (!isNaN(newVal)) {
this.date = newVal;
this.handleChange({
hours: newVal.getHours(),
minutes: newVal.getMinutes(),
seconds: newVal.getSeconds()
}, false);
}
}
},
methods: {
handleClear() {
this.date = initTimeDate();
this.hours = '';
this.minutes = '';
this.seconds = '';
},
handleChange (date, emit = true) {
if (date.hours !== undefined) {
this.date.setHours(date.hours);
this.hours = this.date.getHours();
}
if (date.minutes !== undefined) {
this.date.setMinutes(date.minutes);
this.minutes = this.date.getMinutes();
}
if (date.seconds !== undefined) {
this.date.setSeconds(date.seconds);
this.seconds = this.date.getSeconds();
}
if (emit) this.$emit('on-pick', this.date, true);
},
updateScroll () {
this.$refs.timeSpinner.updateScroll();
}
},
compiled () {
if (this.$parent && this.$parent.$options.name === 'DatePicker') this.showDate = true;
}
};
</script>

View file

@ -1,496 +0,0 @@
<template>
<div
:class="[prefixCls]"
v-clickoutside="handleClose">
<div v-el:reference :class="[prefixCls + '-rel']">
<slot>
<i-input
:class="[prefixCls + '-editor']"
:readonly="!editable || readonly"
:disabled="disabled"
:size="size"
:placeholder="placeholder"
:value="visualValue"
@on-change="handleInputChange"
@on-focus="handleFocus"
@on-click="handleIconClick"
@mouseenter="handleInputMouseenter"
@mouseleave="handleInputMouseleave"
:icon="iconType"></i-input>
</slot>
</div>
<Drop v-show="opened" :placement="placement" :transition="transition" v-ref:drop>
<div v-el:picker></div>
</Drop>
</div>
</template>
<script>
import Vue from 'vue';
import iInput from '../../components/input/input.vue';
import Drop from '../../components/select/dropdown.vue';
import clickoutside from '../../directives/clickoutside';
import { oneOf } from '../../utils/assist';
import { formatDate, parseDate } from './util';
const prefixCls = 'ivu-date-picker';
const DEFAULT_FORMATS = {
date: 'yyyy-MM-dd',
month: 'yyyy-MM',
year: 'yyyy',
datetime: 'yyyy-MM-dd HH:mm:ss',
time: 'HH:mm:ss',
timerange: 'HH:mm:ss',
daterange: 'yyyy-MM-dd',
datetimerange: 'yyyy-MM-dd HH:mm:ss'
};
const RANGE_SEPARATOR = ' - ';
const DATE_FORMATTER = function(value, format) {
return formatDate(value, format);
};
const DATE_PARSER = function(text, format) {
return parseDate(text, format);
};
const RANGE_FORMATTER = function(value, format) {
if (Array.isArray(value) && value.length === 2) {
const start = value[0];
const end = value[1];
if (start && end) {
return formatDate(start, format) + RANGE_SEPARATOR + formatDate(end, format);
}
}
return '';
};
const RANGE_PARSER = function(text, format) {
const array = text.split(RANGE_SEPARATOR);
if (array.length === 2) {
const range1 = array[0];
const range2 = array[1];
return [parseDate(range1, format), parseDate(range2, format)];
}
return [];
};
const TYPE_VALUE_RESOLVER_MAP = {
default: {
formatter(value) {
if (!value) return '';
return '' + value;
},
parser(text) {
if (text === undefined || text === '') return null;
return text;
}
},
date: {
formatter: DATE_FORMATTER,
parser: DATE_PARSER
},
datetime: {
formatter: DATE_FORMATTER,
parser: DATE_PARSER
},
daterange: {
formatter: RANGE_FORMATTER,
parser: RANGE_PARSER
},
datetimerange: {
formatter: RANGE_FORMATTER,
parser: RANGE_PARSER
},
timerange: {
formatter: RANGE_FORMATTER,
parser: RANGE_PARSER
},
time: {
formatter: DATE_FORMATTER,
parser: DATE_PARSER
},
month: {
formatter: DATE_FORMATTER,
parser: DATE_PARSER
},
year: {
formatter: DATE_FORMATTER,
parser: DATE_PARSER
},
number: {
formatter(value) {
if (!value) return '';
return '' + value;
},
parser(text) {
let result = Number(text);
if (!isNaN(text)) {
return result;
} else {
return null;
}
}
}
};
export default {
components: { iInput, Drop },
directives: { clickoutside },
props: {
format: {
type: String
},
readonly: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
editable: {
type: Boolean,
default: true
},
clearable: {
type: Boolean,
default: true
},
confirm: {
type: Boolean,
default: false
},
open: {
type: Boolean,
default: null
},
size: {
validator (value) {
return oneOf(value, ['small', 'large']);
}
},
placeholder: {
type: String,
default: ''
},
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-start'
},
options: {
type: Object
}
},
data () {
return {
prefixCls: prefixCls,
showClose: false,
visible: false,
picker: null,
internalValue: '',
disableClickOutSide: false // fixed when click a date,trigger clickoutside to close picker
};
},
computed: {
opened () {
return this.open === null ? this.visible : this.open;
},
iconType () {
let icon = 'ios-calendar-outline';
if (this.type === 'time' || this.type === 'timerange') icon = 'ios-clock-outline';
if (this.showClose) icon = 'ios-close';
return icon;
},
transition () {
if (this.placement === 'bottom-start' || this.placement === 'bottom' || this.placement === 'bottom-end') {
return 'slide-up';
} else {
return 'slide-down';
}
},
selectionMode() {
if (this.type === 'month') {
return 'month';
} else if (this.type === 'year') {
return 'year';
}
return 'day';
},
visualValue: {
get () {
const value = this.internalValue;
if (!value) return;
const formatter = (
TYPE_VALUE_RESOLVER_MAP[this.type] ||
TYPE_VALUE_RESOLVER_MAP['default']
).formatter;
const format = DEFAULT_FORMATS[this.type];
return formatter(value, this.format || format);
},
set (value) {
if (value) {
const type = this.type;
const parser = (
TYPE_VALUE_RESOLVER_MAP[type] ||
TYPE_VALUE_RESOLVER_MAP['default']
).parser;
const parsedValue = parser(value, this.format || DEFAULT_FORMATS[type]);
if (parsedValue) {
if (this.picker) this.picker.value = parsedValue;
}
return;
}
if (this.picker) this.picker.value = value;
}
}
},
methods: {
handleClose () {
if (this.open !== null) return;
if (!this.disableClickOutSide) this.visible = false;
this.disableClickOutSide = false;
},
handleFocus () {
if (this.readonly) return;
this.visible = true;
},
handleInputChange (event) {
const oldValue = this.visualValue;
const value = event.target.value;
let correctValue = '';
let correctDate = '';
const type = this.type;
const format = this.format || DEFAULT_FORMATS[type];
if (type === 'daterange' || type === 'timerange' || type === 'datetimerange') {
const parser = (
TYPE_VALUE_RESOLVER_MAP[type] ||
TYPE_VALUE_RESOLVER_MAP['default']
).parser;
const formatter = (
TYPE_VALUE_RESOLVER_MAP[type] ||
TYPE_VALUE_RESOLVER_MAP['default']
).formatter;
const parsedValue = parser(value, format);
if (parsedValue[0] instanceof Date && parsedValue[1] instanceof Date) {
if (parsedValue[0].getTime() > parsedValue[1].getTime()) {
correctValue = oldValue;
} else {
correctValue = formatter(parsedValue, format);
}
// todo disabledDate
} else {
correctValue = oldValue;
}
correctDate = parser(correctValue, format);
} else if (type === 'time') {
const parsedDate = parseDate(value, format);
if (parsedDate instanceof Date) {
if (this.disabledHours.length || this.disabledMinutes.length || this.disabledSeconds.length) {
const hours = parsedDate.getHours();
const minutes = parsedDate.getMinutes();
const seconds = parsedDate.getSeconds();
if ((this.disabledHours.length && this.disabledHours.indexOf(hours) > -1) ||
(this.disabledMinutes.length && this.disabledMinutes.indexOf(minutes) > -1) ||
(this.disabledSeconds.length && this.disabledSeconds.indexOf(seconds) > -1)) {
correctValue = oldValue;
} else {
correctValue = formatDate(parsedDate, format);
}
} else {
correctValue = formatDate(parsedDate, format);
}
} else {
correctValue = oldValue;
}
correctDate = parseDate(correctValue, format);
} else {
const parsedDate = parseDate(value, format);
if (parsedDate instanceof Date) {
const options = this.options || false;
if (options && options.disabledDate && typeof options.disabledDate === 'function' && options.disabledDate(new Date(parsedDate))) {
correctValue = oldValue;
} else {
correctValue = formatDate(parsedDate, format);
}
} else {
correctValue = oldValue;
}
correctDate = parseDate(correctValue, format);
}
this.visualValue = correctValue;
event.target.value = correctValue;
this.internalValue = correctDate;
if (correctValue !== oldValue) this.emitChange(correctDate);
},
handleInputMouseenter () {
if (this.readonly || this.disabled) return;
if (this.visualValue && this.clearable) {
this.showClose = true;
}
},
handleInputMouseleave () {
this.showClose = false;
},
handleIconClick () {
if (this.showClose) {
this.handleClear();
} else {
this.handleFocus();
}
},
handleClear () {
this.visible = false;
this.internalValue = '';
this.value = '';
this.$emit('on-clear');
this.$dispatch('on-form-change', '');
},
showPicker () {
if (!this.picker) {
const type = this.type;
this.picker = new Vue(this.panel).$mount(this.$els.picker);
if (type === 'datetime' || type === 'datetimerange') {
this.confirm = true;
this.picker.showTime = true;
}
this.picker.value = this.internalValue;
this.picker.confirm = this.confirm;
this.picker.selectionMode = this.selectionMode;
if (this.format) this.picker.format = this.format;
// TimePicker
if (this.disabledHours) this.picker.disabledHours = this.disabledHours;
if (this.disabledMinutes) this.picker.disabledMinutes = this.disabledMinutes;
if (this.disabledSeconds) this.picker.disabledSeconds = this.disabledSeconds;
if (this.hideDisabledOptions) this.picker.hideDisabledOptions = this.hideDisabledOptions;
const options = this.options;
for (const option in options) {
this.picker[option] = options[option];
}
this.picker.$on('on-pick', (date, visible = false) => {
if (!this.confirm) this.visible = visible;
this.value = date;
this.picker.value = date;
this.picker.resetView && this.picker.resetView();
this.emitChange(date);
});
this.picker.$on('on-pick-clear', () => {
this.handleClear();
});
this.picker.$on('on-pick-success', () => {
this.visible = false;
this.$emit('on-ok');
});
this.picker.$on('on-pick-click', () => this.disableClickOutSide = true);
}
if (this.internalValue instanceof Date) {
this.picker.date = new Date(this.internalValue.getTime());
} else {
this.picker.value = this.internalValue;
}
this.picker.resetView && this.picker.resetView();
},
emitChange (date) {
const type = this.type;
const format = this.format || DEFAULT_FORMATS[type];
const formatter = (
TYPE_VALUE_RESOLVER_MAP[type] ||
TYPE_VALUE_RESOLVER_MAP['default']
).formatter;
let newDate = formatter(date, format);
if (type === 'daterange' || type === 'timerange') {
newDate = [newDate.split(RANGE_SEPARATOR)[0], newDate.split(RANGE_SEPARATOR)[1]];
}
this.$emit('on-change', newDate);
this.$dispatch('on-form-change', newDate);
}
},
watch: {
visible (val) {
if (val) {
this.showPicker();
this.$refs.drop.update();
if (this.open === null) this.$emit('on-open-change', true);
} else {
if (this.picker) this.picker.resetView && this.picker.resetView(true);
this.$refs.drop.destroy();
if (this.open === null) this.$emit('on-open-change', false);
}
},
internalValue(val) {
if (!val && this.picker && typeof this.picker.handleClear === 'function') {
this.picker.handleClear();
}
},
value: {
immediate: true,
handler (val) {
const type = this.type;
const parser = (
TYPE_VALUE_RESOLVER_MAP[type] ||
TYPE_VALUE_RESOLVER_MAP['default']
).parser;
if (val && type === 'time' && !(val instanceof Date)) {
val = parser(val, this.format || DEFAULT_FORMATS[type]);
} else if (val && type === 'timerange' && Array.isArray(val) && val.length === 2 && !(val[0] instanceof Date) && !(val[1] instanceof Date)) {
val = val.join(RANGE_SEPARATOR);
val = parser(val, this.format || DEFAULT_FORMATS[type]);
}
this.internalValue = val;
}
},
open (val) {
if (val === true) {
this.visible = val;
this.$emit('on-open-change', true);
} else if (val === false) {
this.$emit('on-open-change', false);
}
}
},
beforeDestroy () {
if (this.picker) {
this.picker.$destroy();
}
},
ready () {
if (this.open !== null) this.visible = this.open;
},
events: {
'on-form-blur' () {
return false;
},
'on-form-change' () {
return false;
}
}
};
</script>

View file

@ -1,36 +0,0 @@
import Picker from '../picker.vue';
import DatePanel from '../panel/date.vue';
import DateRangePanel from '../panel/date-range.vue';
const getPanel = function (type) {
if (type === 'daterange' || type === 'datetimerange') {
return DateRangePanel;
}
return DatePanel;
};
import { oneOf } from '../../../utils/assist';
export default {
mixins: [Picker],
props: {
type: {
validator (value) {
return oneOf(value, ['year', 'month', 'date', 'daterange', 'datetime', 'datetimerange']);
},
default: 'date'
},
value: {}
},
created () {
if (!this.value) {
if (this.type === 'daterange' || this.type === 'datetimerange') {
this.value = ['',''];
} else {
this.value = '';
}
}
this.panel = getPanel(this.type);
}
};

View file

@ -1,36 +0,0 @@
import Picker from '../picker.vue';
import TimePanel from '../panel/time.vue';
import TimeRangePanel from '../panel/time-range.vue';
import Options from '../time-mixins';
const getPanel = function (type) {
if (type === 'timerange') {
return TimeRangePanel;
}
return TimePanel;
};
import { oneOf } from '../../../utils/assist';
export default {
mixins: [Picker, Options],
props: {
type: {
validator (value) {
return oneOf(value, ['time', 'timerange']);
},
default: 'time'
},
value: {}
},
created () {
if (!this.value) {
if (this.type === 'timerange') {
this.value = ['',''];
} else {
this.value = '';
}
}
this.panel = getPanel(this.type);
}
};

View file

@ -1,26 +0,0 @@
export default {
props: {
disabledHours: {
type: Array,
default () {
return [];
}
},
disabledMinutes: {
type: Array,
default () {
return [];
}
},
disabledSeconds: {
type: Array,
default () {
return [];
}
},
hideDisabledOptions: {
type: Boolean,
default: false
}
}
};

View file

@ -1,85 +0,0 @@
import dateUtil from '../../utils/date';
export const toDate = function(date) {
date = new Date(date);
if (isNaN(date.getTime())) return null;
return date;
};
export const formatDate = function(date, format) {
date = toDate(date);
if (!date) return '';
return dateUtil.format(date, format || 'yyyy-MM-dd');
};
export const parseDate = function(string, format) {
return dateUtil.parse(string, format || 'yyyy-MM-dd');
};
export const getDayCountOfMonth = function(year, month) {
if (month === 3 || month === 5 || month === 8 || month === 10) {
return 30;
}
if (month === 1) {
if (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) {
return 29;
} else {
return 28;
}
}
return 31;
};
export const getFirstDayOfMonth = function(date) {
const temp = new Date(date.getTime());
temp.setDate(1);
return temp.getDay();
};
export const prevMonth = function(src) {
const year = src.getFullYear();
const month = src.getMonth();
const date = src.getDate();
const newYear = month === 0 ? year - 1 : year;
const newMonth = month === 0 ? 11 : month - 1;
const newMonthDayCount = getDayCountOfMonth(newYear, newMonth);
if (newMonthDayCount < date) {
src.setDate(newMonthDayCount);
}
src.setMonth(newMonth);
src.setFullYear(newYear);
return new Date(src.getTime());
};
export const nextMonth = function(src) {
const year = src.getFullYear();
const month = src.getMonth();
const date = src.getDate();
const newYear = month === 11 ? year + 1 : year;
const newMonth = month === 11 ? 0 : month + 1;
const newMonthDayCount = getDayCountOfMonth(newYear, newMonth);
if (newMonthDayCount < date) {
src.setDate(newMonthDayCount);
}
src.setMonth(newMonth);
src.setFullYear(newYear);
return new Date(src.getTime());
};
export const initTimeDate = function () {
const date = new Date();
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
return date;
};

View file

@ -1,57 +0,0 @@
<template>
<li :class="classes" @click="handleClick"><slot></slot></li>
</template>
<script>
const prefixCls = 'ivu-dropdown-item';
export default {
props: {
key: {
type: [String, Number]
},
disabled: {
type: Boolean,
default: false
},
selected: {
type: Boolean,
default: false
},
divided: {
type: Boolean,
default: false
}
},
computed: {
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-disabled`]: this.disabled,
[`${prefixCls}-selected`]: this.selected,
[`${prefixCls}-divided`]: this.divided
}
];
}
},
methods: {
handleClick () {
const $parent = this.$parent.$parent;
const hasChildren = this.$parent && this.$parent.$options.name === 'Dropdown';
if (this.disabled) {
this.$nextTick(() => {
$parent.visible = true;
});
} else if (hasChildren) {
this.$parent.$emit('on-haschild-click');
} else {
if ($parent && $parent.$options.name === 'Dropdown') {
$parent.$emit('on-hover-click');
}
}
$parent.$emit('on-click', this.key);
}
}
};
</script>

View file

@ -1,6 +0,0 @@
<template>
<ul class="ivu-dropdown-menu"><slot></slot></ul>
</template>
<script>
export default {};
</script>

View file

@ -1,134 +0,0 @@
<template>
<div
:class="[prefixCls]"
v-clickoutside="handleClose"
@mouseenter="handleMouseenter"
@mouseleave="handleMouseleave">
<div :class="[prefixCls-rel]" v-el:reference @click="handleClick"><slot></slot></div>
<Drop v-show="visible" :placement="placement" :transition="transition" v-ref:drop><slot name="list"></slot></Drop>
</div>
</template>
<script>
import Drop from '../select/dropdown.vue';
import clickoutside from '../../directives/clickoutside';
import { oneOf } from '../../utils/assist';
const prefixCls = 'ivu-dropdown';
export default {
name: 'Dropdown',
directives: { clickoutside },
components: { Drop },
props: {
trigger: {
validator (value) {
return oneOf(value, ['click', 'hover', 'custom']);
},
default: 'hover'
},
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'
},
visible: {
type: Boolean,
default: false
}
},
computed: {
transition () {
return ['bottom-start', 'bottom', 'bottom-end'].indexOf(this.placement) > -1 ? 'slide-up' : 'fade';
}
},
data () {
return {
prefixCls: prefixCls
};
},
methods: {
handleClick () {
if (this.trigger === 'custom') return false;
if (this.trigger !== 'click') {
return false;
}
this.visible = !this.visible;
},
handleMouseenter () {
if (this.trigger === 'custom') return false;
if (this.trigger !== 'hover') {
return false;
}
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.visible = true;
}, 250);
},
handleMouseleave () {
if (this.trigger === 'custom') return false;
if (this.trigger !== 'hover') {
return false;
}
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.visible = false;
}, 150);
},
handleClose () {
if (this.trigger === 'custom') return false;
if (this.trigger !== 'click') {
return false;
}
this.visible = false;
},
hasParent () {
const $parent = this.$parent.$parent;
if ($parent && $parent.$options.name === 'Dropdown') {
return $parent;
} else {
return false;
}
}
},
watch: {
visible (val) {
if (val) {
this.$refs.drop.update();
} else {
this.$refs.drop.destroy();
}
this.$emit('on-visible-change', val);
}
},
events: {
'on-click' (key) {
const $parent = this.hasParent();
if ($parent ) $parent.$emit('on-click', key);
},
'on-hover-click' () {
const $parent = this.hasParent();
if ($parent) {
this.$nextTick(() => {
if (this.trigger === 'custom') return false;
this.visible = false;
});
$parent.$emit('on-hover-click');
} else {
this.$nextTick(() => {
if (this.trigger === 'custom') return false;
this.visible = false;
});
}
},
'on-haschild-click' () {
this.$nextTick(() => {
if (this.trigger === 'custom') return false;
this.visible = true;
});
const $parent = this.hasParent();
if ($parent) $parent.$emit('on-haschild-click');
}
}
};
</script>

View file

@ -1,7 +0,0 @@
import Dropdown from './dropdown.vue';
import DropdownMenu from './dropdown-menu.vue';
import DropdownItem from './dropdown-item.vue';
Dropdown.Menu = DropdownMenu;
Dropdown.Item = DropdownItem;
export default Dropdown;

View file

@ -1,235 +0,0 @@
<template>
<div :class="classes">
<label :class="[prefixCls + '-label']" :style="labelStyles" v-if="label"><slot name="label">{{ label }}</slot></label>
<div :class="[prefixCls + '-content']" :style="contentStyles">
<slot></slot>
<div transition="fade" :class="[prefixCls + '-error-tip']" v-if="validateState === 'error' && showMessage && form.showMessage">{{ validateMessage }}</div>
</div>
</div>
</template>
<script>
// https://github.com/ElemeFE/element/blob/dev/packages/form/src/form-item.vue
import AsyncValidator from 'async-validator';
const prefixCls = 'ivu-form-item';
function getPropByPath(obj, path) {
let tempObj = obj;
path = path.replace(/\[(\w+)\]/g, '.$1');
path = path.replace(/^\./, '');
let keyArr = path.split('.');
let i = 0;
for (let len = keyArr.length; i < len - 1; ++i) {
let key = keyArr[i];
if (key in tempObj) {
tempObj = tempObj[key];
} else {
throw new Error('[iView warn]: please transfer a valid prop path to form item!');
}
}
return {
o: tempObj,
k: keyArr[i],
v: tempObj[keyArr[i]]
};
}
export default {
props: {
label: {
type: String,
default: ''
},
labelWidth: {
type: Number
},
prop: {
type: String
},
required: {
type: Boolean,
default: false
},
rules: {
type: [Object, Array]
},
error: {
type: String
},
validateStatus: {
type: Boolean
},
showMessage: {
type: Boolean,
default: true
}
},
data () {
return {
prefixCls: prefixCls,
isRequired: false,
validateState: '',
validateMessage: '',
validateDisabled: false,
validator: {}
};
},
watch: {
error (val) {
this.validateMessage = val;
this.validateState = 'error';
},
validateStatus (val) {
this.validateState = val;
}
},
computed: {
classes () {
return [
`${prefixCls}`,
{
[`${prefixCls}-required`]: this.required || this.isRequired,
[`${prefixCls}-error`]: this.validateState === 'error',
[`${prefixCls}-validating`]: this.validateState === 'validating'
}
];
},
form() {
let parent = this.$parent;
while (parent.$options.name !== 'iForm') {
parent = parent.$parent;
}
return parent;
},
fieldValue: {
cache: false,
get() {
const model = this.form.model;
if (!model || !this.prop) { return; }
let path = this.prop;
if (path.indexOf(':') !== -1) {
path = path.replace(/:/, '.');
}
return getPropByPath(model, path).v;
}
},
labelStyles () {
let style = {};
const labelWidth = this.labelWidth || this.form.labelWidth;
if (labelWidth) {
style.width = `${labelWidth}px`;
}
return style;
},
contentStyles () {
let style = {};
const labelWidth = this.labelWidth || this.form.labelWidth;
if (labelWidth) {
style.marginLeft = `${labelWidth}px`;
}
return style;
}
},
methods: {
getRules () {
let formRules = this.form.rules;
const selfRules = this.rules;
formRules = formRules ? formRules[this.prop] : [];
return [].concat(selfRules || formRules || []);
},
getFilteredRule (trigger) {
const rules = this.getRules();
return rules.filter(rule => !rule.trigger || rule.trigger.indexOf(trigger) !== -1);
},
validate(trigger, callback = function () {}) {
const rules = this.getFilteredRule(trigger);
if (!rules || rules.length === 0) {
callback();
return true;
}
this.validateState = 'validating';
let descriptor = {};
descriptor[this.prop] = rules;
const validator = new AsyncValidator(descriptor);
let model = {};
model[this.prop] = this.fieldValue;
validator.validate(model, { firstFields: true }, errors => {
this.validateState = !errors ? 'success' : 'error';
this.validateMessage = errors ? errors[0].message : '';
callback(this.validateMessage);
});
},
resetField () {
this.validateState = '';
this.validateMessage = '';
let model = this.form.model;
let value = this.fieldValue;
let path = this.prop;
if (path.indexOf(':') !== -1) {
path = path.replace(/:/, '.');
}
let prop = getPropByPath(model, path);
if (Array.isArray(value) && value.length > 0) {
this.validateDisabled = true;
prop.o[prop.k] = [];
} else if (value !== this.initialValue) {
this.validateDisabled = true;
prop.o[prop.k] = this.initialValue;
}
},
onFieldBlur() {
this.validate('blur');
},
onFieldChange() {
if (this.validateDisabled) {
this.validateDisabled = false;
return;
}
this.validate('change');
}
},
ready () {
if (this.prop) {
this.$dispatch('on-form-item-add', this);
Object.defineProperty(this, 'initialValue', {
value: this.fieldValue
});
let rules = this.getRules();
if (rules.length) {
rules.every(rule => {
if (rule.required) {
this.isRequired = true;
return false;
}
});
this.$on('on-form-blur', this.onFieldBlur);
this.$on('on-form-change', this.onFieldChange);
}
}
},
beforeDestroy () {
this.$dispatch('on-form-item-remove', this);
}
};
</script>

View file

@ -1,96 +0,0 @@
<template>
<form :class="classes"><slot></slot></form>
</template>
<script>
// https://github.com/ElemeFE/element/blob/dev/packages/form/src/form.vue
import { oneOf } from '../../utils/assist';
const prefixCls = 'ivu-form';
export default {
name: 'iForm',
props: {
model: {
type: Object
},
rules: {
type: Object
},
labelWidth: {
type: Number
},
labelPosition: {
validator (value) {
return oneOf(value, ['left', 'right', 'top']);
},
default: 'right'
},
inline: {
type: Boolean,
default: false
},
showMessage: {
type: Boolean,
default: true
}
},
data () {
return {
fields: []
};
},
computed: {
classes () {
return [
`${prefixCls}`,
`${prefixCls}-label-${this.labelPosition}`,
{
[`${prefixCls}-inline`]: this.inline
}
];
}
},
methods: {
resetFields() {
this.fields.forEach(field => {
field.resetField();
});
},
validate(callback) {
let valid = true;
let count = 0;
this.fields.forEach(field => {
field.validate('', errors => {
if (errors) {
valid = false;
}
if (typeof callback === 'function' && ++count === this.fields.length) {
callback(valid);
}
});
});
},
validateField(prop, cb) {
const field = this.fields.filter(field => field.prop === prop)[0];
if (!field) { throw new Error('[iView warn]: must call validateField with valid prop string!'); }
field.validate('', cb);
}
},
watch: {
rules() {
this.validate();
}
},
events: {
'on-form-item-add' (field) {
if (field) this.fields.push(field);
return false;
},
'on-form-item-remove' (field) {
if (field.prop) this.fields.splice(this.fields.indexOf(field), 1);
return false;
}
}
};
</script>

View file

@ -1,5 +0,0 @@
import Form from './form.vue';
import FormItem from './form-item.vue';
Form.Item = FormItem;
export default Form;

View file

@ -1,32 +0,0 @@
<template>
<i :class="classes" :style="styles"></i>
</template>
<script>
const prefixCls = 'ivu-icon';
export default {
props: {
type: String,
size: [Number, String],
color: String
},
computed: {
classes () {
return `${prefixCls} ${prefixCls}-${this.type}`;
},
styles () {
let style = {};
if (this.size) {
style['font-size'] = `${this.size}px`;
}
if (this.color) {
style.color = this.color;
}
return style;
}
}
};
</script>

View file

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

View file

@ -1,2 +0,0 @@
import InputNumber from './input-number.vue';
export default InputNumber;

View file

@ -1,262 +0,0 @@
<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]*$)|(^-?0{1}$)/).test(value + '');
}
function addNum (num1, num2) {
let 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 (e) {
const targetVal = Number(e.target.value);
if (this.upDisabled && isNaN(targetVal)) {
return false;
}
this.changeStep('up', e);
},
down (e) {
const targetVal = Number(e.target.value);
if (this.downDisabled && isNaN(targetVal)) {
return false;
}
this.changeStep('down', e);
},
changeStep (type, e) {
if (this.disabled) {
return false;
}
const targetVal = Number(e.target.value);
let val = Number(this.value);
const step = Number(this.step);
if (isNaN(val)) {
return false;
}
// input a number, and key up or down
if (!isNaN(targetVal)) {
if (type === 'up') {
if (addNum(targetVal, step) <= this.max) {
val = targetVal;
} else {
return false;
}
} else if (type === 'down') {
if (addNum(targetVal, -step) >= this.min) {
val = targetVal;
} else {
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);
this.$dispatch('on-form-change', val);
});
},
focus () {
this.focused = true;
},
blur () {
this.focused = false;
},
keyDown (e) {
if (e.keyCode === 38) {
e.preventDefault();
this.up(e);
} else if (e.keyCode === 40) {
e.preventDefault();
this.down(e);
}
},
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;
}
}
},
compiled () {
this.changeVal(this.value);
},
watch: {
value (val) {
this.changeVal(val);
}
}
};
</script>

View file

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

View file

@ -1,189 +0,0 @@
<template>
<div :class="wrapClasses">
<template v-if="type !== 'textarea'">
<div :class="[prefixCls + '-group-prepend']" v-if="prepend" v-show="slotReady" v-el:prepend><slot name="prepend"></slot></div>
<i class="ivu-icon" :class="['ivu-icon-' + icon, prefixCls + '-icon']" v-if="icon" @click="handleIconClick"></i>
<i class="ivu-icon ivu-icon-load-c ivu-load-loop" :class="[prefixCls + '-icon', prefixCls + '-icon-validate']" v-else transition="fade"></i>
<input
:type="type"
:class="inputClasses"
:placeholder="placeholder"
:disabled="disabled"
:maxlength="maxlength"
:readonly="readonly"
:name="name"
v-model="value"
:number="number"
@keyup.enter="handleEnter"
@focus="handleFocus"
@blur="handleBlur"
@change="handleChange">
<div :class="[prefixCls + '-group-append']" v-if="append" v-show="slotReady" v-el:append><slot name="append"></slot></div>
</template>
<textarea
v-else
v-el:textarea
:class="textareaClasses"
:style="textareaStyles"
:placeholder="placeholder"
:disabled="disabled"
:rows="rows"
:maxlength="maxlength"
:readonly="readonly"
:name="name"
v-model="value"
@keyup.enter="handleEnter"
@focus="handleFocus"
@blur="handleBlur"
@change="handleChange">
</textarea>
</div>
</template>
<script>
import { oneOf } from '../../utils/assist';
import calcTextareaHeight from '../../utils/calcTextareaHeight';
const prefixCls = 'ivu-input';
export default {
props: {
type: {
validator (value) {
return oneOf(value, ['text', 'textarea', 'password']);
},
default: 'text'
},
value: {
type: [String, Number],
default: '',
// twoWay: true
},
size: {
validator (value) {
return oneOf(value, ['small', 'large']);
}
},
placeholder: {
type: String,
default: ''
},
maxlength: {
type: Number
},
disabled: {
type: Boolean,
default: false
},
icon: String,
autosize: {
type: [Boolean, Object],
default: false
},
rows: {
type: Number,
default: 2
},
readonly: {
type: Boolean,
default: false
},
name: {
type: String
},
number: {
type: Boolean,
default: false
}
},
data () {
return {
prefixCls: prefixCls,
prepend: true,
append: true,
slotReady: false,
textareaStyles: {}
};
},
computed: {
wrapClasses () {
return [
`${prefixCls}-wrapper`,
{
[`${prefixCls}-wrapper-${this.size}`]: !!this.size,
[`${prefixCls}-type`]: this.type,
[`${prefixCls}-group`]: this.prepend || this.append,
[`${prefixCls}-group-${this.size}`]: (this.prepend || this.append) && !!this.size
}
];
},
inputClasses () {
return [
`${prefixCls}`,
{
[`${prefixCls}-${this.size}`]: !!this.size,
[`${prefixCls}-disabled`]: this.disabled
}
];
},
textareaClasses () {
return [
`${prefixCls}`,
{
[`${prefixCls}-disabled`]: this.disabled
}
];
}
},
methods: {
handleEnter () {
this.$emit('on-enter');
},
handleIconClick () {
this.$emit('on-click');
},
handleFocus () {
this.$emit('on-focus');
},
handleBlur () {
this.$emit('on-blur');
this.$dispatch('on-form-blur', this.value);
},
handleChange (event) {
this.$emit('on-change', event);
},
resizeTextarea () {
const autosize = this.autosize;
if (!autosize || this.type !== 'textarea') {
return false;
}
const minRows = autosize.minRows;
const maxRows = autosize.maxRows;
this.textareaStyles = calcTextareaHeight(this.$els.textarea, minRows, maxRows);
},
init () {
if (this.type !== 'textarea') {
this.prepend = this.$els.prepend.innerHTML !== '';
this.append = this.$els.append.innerHTML !== '';
} else {
this.prepend = false;
this.append = false;
}
this.slotReady = true;
this.resizeTextarea();
}
},
watch: {
value () {
this.$nextTick(() => {
this.resizeTextarea();
});
this.$dispatch('on-form-change', this.value);
}
},
compiled () {
this.$nextTick(() => this.init());
}
};
</script>

View file

@ -1,71 +0,0 @@
<template>
<div :class="classes" :style="styles">
<slot></slot>
</div>
</template>
<script>
const prefixCls = 'ivu-col';
export default {
props: {
span: [Number, String],
order: [Number, String],
offset: [Number, String],
push: [Number, String],
pull: [Number, String],
className: String,
xs: [Number, Object],
sm: [Number, Object],
md: [Number, Object],
lg: [Number, Object]
},
data () {
return {
gutter: 0
};
},
computed: {
classes () {
let classList = [
`${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
}
];
['xs', 'sm', 'md', 'lg'].forEach(size => {
if (typeof this[size] === 'number') {
classList.push(`${prefixCls}-span-${size}-${this[size]}`);
} else if (typeof this[size] === 'object') {
let props = this[size];
Object.keys(props).forEach(prop => {
classList.push(
prop !== 'span'
? `${prefixCls}-${size}-${prop}-${props[prop]}`
: `${prefixCls}-span-${size}-${props[prop]}`
);
});
}
});
return classList;
},
styles () {
let style = {};
if (this.gutter !== 0) {
style = {
paddingLeft: this.gutter / 2 + 'px',
paddingRight: this.gutter / 2 + 'px'
};
}
return style;
}
}
};
</script>

View file

@ -1,4 +0,0 @@
import Row from './row.vue';
import Col from './col.vue';
export { Row, Col };

View file

@ -1,76 +0,0 @@
<template>
<div :class="classes" :style="styles">
<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']);
}
},
gutter: {
type: Number,
default: 0
},
className: String
},
computed: {
classes () {
return [
{
[`${prefixCls}`]: !this.type,
[`${prefixCls}-${this.type}`]: !!this.type,
[`${prefixCls}-${this.type}-${this.align}`]: !!this.align,
[`${prefixCls}-${this.type}-${this.justify}`]: !!this.justify,
[`${this.className}`]: !!this.className
}
];
},
styles () {
let style = {};
if (this.gutter !== 0) {
style = {
marginLeft: this.gutter / -2 + 'px',
marginRight: this.gutter / -2 + 'px'
};
}
return style;
}
},
methods: {
updateGutter (val) {
this.$children.forEach((child) => {
if (val !== 0) {
child.gutter = val;
}
});
}
},
watch: {
gutter (val) {
this.updateGutter(val);
}
},
ready () {
this.updateGutter(this.gutter);
}
};
</script>

View file

@ -1,112 +0,0 @@
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 () {
if (timer) return;
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();
}
};

View file

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

View file

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

View file

@ -1,10 +0,0 @@
import Menu from './menu.vue';
import MenuGroup from './menu-group.vue';
import MenuItem from './menu-item.vue';
import Submenu from './submenu.vue';
Menu.Group = MenuGroup;
Menu.Item = MenuItem;
Menu.Sub = Submenu;
export default Menu;

View file

@ -1,24 +0,0 @@
<template>
<li :class="[prefixCls + '-item-group']">
<div :class="[prefixCls + '-item-group-title']">{{ title }}</div>
<ul><slot></slot></ul>
</li>
</template>
<script>
const prefixCls = 'ivu-menu';
export default {
name: 'MenuGroup',
props: {
title: {
type: String,
default: ''
}
},
data () {
return {
prefixCls: prefixCls
};
}
};
</script>

View file

@ -1,43 +0,0 @@
<template>
<li :class="classes" @click.stop="handleClick"><slot></slot></li>
</template>
<script>
const prefixCls = 'ivu-menu';
export default {
name: 'MenuItem',
props: {
key: {
type: [String, Number],
required: true
},
disabled: {
type: Boolean,
default: false
}
},
data () {
return {
active: false
};
},
computed: {
classes () {
return [
`${prefixCls}-item`,
{
[`${prefixCls}-item-active`]: this.active,
[`${prefixCls}-item-selected`]: this.active,
[`${prefixCls}-item-disabled`]: this.disabled
}
];
}
},
methods: {
handleClick () {
if (this.disabled) return;
this.$dispatch('on-menu-item-select', this.key);
}
}
};
</script>

View file

@ -1,134 +0,0 @@
<template>
<ul :class="classes" :style="styles"><slot></slot></ul>
</template>
<script>
import { oneOf } from '../../utils/assist';
const prefixCls = 'ivu-menu';
export default {
props: {
mode: {
validator (value) {
return oneOf(value, ['horizontal', 'vertical']);
},
default: 'vertical'
},
theme: {
validator (value) {
return oneOf(value, ['light', 'dark', 'primary']);
},
default: 'light'
},
activeKey: {
type: [String, Number]
},
openKeys: {
type: Array,
default () {
return [];
}
},
accordion: {
type: Boolean,
default: false
},
width: {
type: String,
default: '240px'
}
},
computed: {
classes () {
let theme = this.theme;
if (this.mode === 'vertical' && this.theme === 'primary') theme = 'light';
return [
`${prefixCls}`,
`${prefixCls}-${theme}`,
{
[`${prefixCls}-${this.mode}`]: this.mode
}
];
},
styles () {
let style = {};
if (this.mode === 'vertical') style.width = this.width;
return style;
}
},
methods: {
updateActiveKey () {
this.$children.forEach((item, index) => {
if (!this.activeKey && index === 0) {
this.activeKey = -1;
}
if (item.$options.name === 'Submenu') {
item.active = false;
item.$children.forEach(subitem => {
if (subitem.$options.name === 'MenuGroup') {
subitem.$children.forEach(groupItem => {
if (groupItem.key === this.activeKey) {
groupItem.active = true;
groupItem.$parent.$parent.active = true;
} else {
groupItem.active = false;
}
});
} else if (subitem.$options.name === 'MenuItem') {
if (subitem.key === this.activeKey) {
subitem.active = true;
subitem.$parent.active = true;
} else {
subitem.active = false;
}
}
});
} else if (item.$options.name === 'MenuGroup') {
item.$children.forEach(groupItem => {
groupItem.active = groupItem.key === this.activeKey;
});
} else if (item.$options.name === 'MenuItem') {
item.active = item.key === this.activeKey;
}
});
},
updateOpenKeys (key) {
const index = this.openKeys.indexOf(key);
if (index > -1) {
this.openKeys.splice(index, 1);
} else {
this.openKeys.push(key);
}
},
updateOpened () {
this.$children.forEach(item => {
if (item.$options.name === 'Submenu') {
if (this.openKeys.indexOf(item.key) > -1) item.opened = true;
}
});
}
},
compiled () {
this.updateActiveKey();
this.updateOpened();
},
events: {
'on-menu-item-select' (key) {
this.activeKey = key;
this.$emit('on-select', key);
}
},
watch: {
openKeys () {
this.$emit('on-open-change', this.openKeys);
},
activeKey () {
this.updateActiveKey();
}
}
};
</script>

View file

@ -1,127 +0,0 @@
<template>
<li :class="classes" @mouseenter="handleMouseenter" @mouseleave="handleMouseleave">
<div :class="[prefixCls + '-submenu-title']" v-el:reference @click="handleClick">
<slot name="title"></slot>
<Icon type="ios-arrow-down" :class="[prefixCls + '-submenu-title-icon']"></Icon>
</div>
<ul :class="[prefixCls]" v-if="mode === 'vertical'" v-show="opened"><slot></slot></ul>
<Drop
v-else
v-show="opened"
placement="bottom"
transition="slide-up"
v-ref:drop
:style="dropStyle"><slot></slot></Drop>
</li>
</template>
<script>
import Drop from '../select/dropdown.vue';
import Icon from '../icon/icon.vue';
import { getStyle } from '../../utils/assist';
const prefixCls = 'ivu-menu';
export default {
name: 'Submenu',
components: { Icon, Drop },
props: {
key: {
type: [String, Number],
required: true
},
disabled: {
type: Boolean,
default: false
}
},
data () {
return {
prefixCls: prefixCls,
active: false,
opened: false,
dropWidth: parseFloat(getStyle(this.$el, 'width'))
};
},
computed: {
classes () {
return [
`${prefixCls}-submenu`,
{
[`${prefixCls}-item-active`]: this.active,
[`${prefixCls}-opened`]: this.opened,
[`${prefixCls}-submenu-disabled`]: this.disabled
}
];
},
mode () {
return this.$parent.mode;
},
accordion () {
return this.$parent.accordion;
},
dropStyle () {
let style = {};
if (this.dropWidth) style.minWidth = `${this.dropWidth}px`;
return style;
}
},
methods: {
handleMouseenter () {
if (this.disabled) return;
if (this.mode === 'vertical') return;
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.$parent.updateOpenKeys(this.key);
this.opened = true;
}, 250);
},
handleMouseleave () {
if (this.disabled) return;
if (this.mode === 'vertical') return;
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.$parent.updateOpenKeys(this.key);
this.opened = false;
}, 150);
},
handleClick () {
if (this.disabled) return;
if (this.mode === 'horizontal') return;
const opened = this.opened;
if (this.accordion) {
this.$parent.$children.forEach(item => {
if (item.$options.name === 'Submenu') item.opened = false;
});
}
this.opened = !opened;
this.$parent.updateOpenKeys(this.key);
}
},
watch: {
mode (val) {
if (val === 'horizontal') {
this.$refs.drop.update();
}
},
opened (val) {
if (this.mode === 'vertical') return;
if (val) {
// set drop a width to fixed when menu has fixed position
this.dropWidth = parseFloat(getStyle(this.$el, 'width'));
this.$refs.drop.update();
} else {
this.$refs.drop.destroy();
}
}
},
events: {
'on-menu-item-select' () {
if (this.mode === 'horizontal') this.opened = false;
return true;
}
}
};
</script>

View file

@ -1,97 +0,0 @@
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();
}
};

View file

@ -1,175 +0,0 @@
import Vue from 'vue';
import Modal from './modal.vue';
import Icon from '../icon/icon.vue';
import iButton from '../button/button.vue';
import { camelcaseToHyphen } from '../../utils/assist';
import { t } from '../../locale';
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" :scrollable.sync="scrollable">
<div class="${prefixCls}">
<div class="${prefixCls}-head">
<div class="${prefixCls}-head-title">{{{ title }}}</div>
</div>
<div class="${prefixCls}-body">
<div :class="iconTypeCls"><i :class="iconNameCls"></i></div>
{{{ body }}}
</div>
<div class="${prefixCls}-footer">
<i-button type="text" size="large" v-if="showCancel" @click="cancel">{{ cancelText }}</i-button>
<i-button type="primary" size="large" :loading="buttonLoading" @click="ok">{{ okText }}</i-button>
</div>
</div>
</Modal>
`;
document.body.appendChild(div);
const modal = new Vue({
el: div,
components: { Modal, iButton, Icon },
data: Object.assign(_props, {
visible: false,
width: 416,
title: '',
body: '',
iconType: '',
iconName: '',
okText: t('i.modal.okText'),
cancelText: t('i.modal.cancelText'),
showCancel: false,
loading: false,
buttonLoading: false,
scrollable: false
}),
computed: {
iconTypeCls () {
return [
`${prefixCls}-body-icon`,
`${prefixCls}-body-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;
}
if ('scrollable' in props) {
modal.$parent.scrollable = props.scrollable;
}
// 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;

View file

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

View file

@ -1,235 +0,0 @@
<template>
<div :class="maskClasses" v-show="visible" @click="mask" transition="fade"></div>
<div :class="wrapClasses" @click="handleWrapClick">
<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"><div :class="[prefixCls + '-header-inner']">{{ title }}</div></slot></div>
<div :class="[prefixCls + '-body']"><slot></slot></div>
<div :class="[prefixCls + '-footer']" v-if="!footerHide">
<slot name="footer">
<i-button type="text" size="large" @click="cancel">{{ cancelText }}</i-button>
<i-button type="primary" size="large" :loading="buttonLoading" @click="ok">{{ okText }}</i-button>
</slot>
</div>
</div>
</div>
</div>
</template>
<script>
import Icon from '../icon';
import iButton from '../button/button.vue';
import { getScrollBarSize } from '../../utils/assist';
import { t } from '../../locale';
const prefixCls = 'ivu-modal';
export default {
components: { Icon, iButton },
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 () {
return t('i.modal.okText');
}
},
cancelText: {
type: String,
default () {
return t('i.modal.cancelText');
}
},
loading: {
type: Boolean,
default: false
},
style: {
type: Object
},
className: {
type: String
},
// for instance
footerHide: {
type: Boolean,
default: false
},
scrollable: {
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();
}
},
handleWrapClick (event) {
// use indexOf,do not use === ,because ivu-modal-wrap can have other custom className
const className = event.target.getAttribute('class');
if (className && className.indexOf(`${prefixCls}-wrap`) > -1) this.mask();
},
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 == `<div class="${prefixCls}-header-inner"></div>` && !this.title) {
showHead = false;
}
this.showHead = showHead;
// ESC close
document.addEventListener('keydown', this.EscClose);
},
beforeDestroy () {
document.removeEventListener('keydown', this.EscClose);
this.removeScrollEffect();
},
watch: {
visible (val) {
if (val === false) {
this.buttonLoading = false;
this.timer = setTimeout(() => {
this.wrapShow = false;
this.removeScrollEffect();
}, 300);
} else {
if (this.timer) clearTimeout(this.timer);
this.wrapShow = true;
if (!this.scrollable) {
this.addScrollEffect();
}
}
},
loading (val) {
if (!val) {
this.buttonLoading = false;
}
},
scrollable (val) {
if (!val) {
this.addScrollEffect();
} else {
this.removeScrollEffect();
}
}
}
};
</script>

View file

@ -1,117 +0,0 @@
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;
const with_desc = desc === '' ? '' : ` ${prefixCls}-with-desc`;
if (type == 'normal') {
content = `
<div class="${prefixCls}-custom-content ${prefixCls}-with-normal${with_desc}">
<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 ${prefixCls}-with-${type}${with_desc}">
<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-notice',
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();
}
};

View file

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

View file

@ -1,89 +0,0 @@
<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 }} {{ t('i.page.page') }}</i-option>
</i-select>
</div>
<div v-if="showElevator" :class="ElevatorClasses">
{{ t('i.page.goto') }}
<input type="text" :value="_current" @keyup.enter="changePage">
{{ t('i.page.p') }}
</div>
</div>
</template>
<script>
import iSelect from '../../components/select/select.vue';
import iOption from '../../components/select/option.vue';
import Locale from '../../mixins/locale';
const prefixCls = 'ivu-page';
function isValueNumber (value) {
return (/^[1-9][0-9]*$/).test(value + '');
}
export default {
mixins: [ Locale ],
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>

Some files were not shown because too many files have changed in this diff Show more