├── .eslintignore ├── .gitattributes ├── assets ├── iview.png ├── logo.png ├── pay.png ├── iview2.png └── logo.svg ├── src ├── components │ ├── card │ │ ├── index.js │ │ └── card.vue │ ├── icon │ │ ├── index.js │ │ └── icon.vue │ ├── page │ │ └── index.js │ ├── rate │ │ └── index.js │ ├── spin │ │ ├── index.js │ │ └── spin.vue │ ├── tag │ │ ├── index.js │ │ └── tag.vue │ ├── tree │ │ └── index.js │ ├── affix │ │ └── index.js │ ├── alert │ │ └── index.js │ ├── badge │ │ ├── index.js │ │ └── badge.vue │ ├── input │ │ └── index.js │ ├── table │ │ ├── index.js │ │ ├── header.js │ │ ├── expand.js │ │ ├── table-tr.vue │ │ ├── mixin.js │ │ └── export-csv.js │ ├── circle │ │ └── index.js │ ├── switch │ │ ├── index.js │ │ └── switch.vue │ ├── back-top │ │ ├── index.js │ │ └── back-top.vue │ ├── poptip │ │ └── index.js │ ├── slider │ │ └── index.js │ ├── upload │ │ ├── index.js │ │ └── ajax.js │ ├── cascader │ │ ├── index.js │ │ └── casitem.vue │ ├── progress │ │ └── index.js │ ├── tooltip │ │ └── index.js │ ├── transfer │ │ ├── index.js │ │ ├── operation.vue │ │ └── search.vue │ ├── input-number │ │ └── index.js │ ├── date-picker │ │ ├── index.js │ │ ├── time-mixins.js │ │ ├── panel │ │ │ └── mixin.js │ │ ├── picker │ │ │ ├── time-picker.js │ │ │ └── date-picker.js │ │ ├── base │ │ │ ├── confirm.vue │ │ │ └── month-table.vue │ │ └── util.js │ ├── grid │ │ ├── index.js │ │ └── row.vue │ ├── time-picker │ │ └── index.js │ ├── tabs │ │ ├── index.js │ │ └── pane.vue │ ├── steps │ │ └── index.js │ ├── form │ │ └── index.js │ ├── collapse │ │ ├── index.js │ │ └── panel.vue │ ├── radio │ │ ├── index.js │ │ └── radio-group.vue │ ├── button │ │ ├── index.js │ │ ├── button-group.vue │ │ └── button.vue │ ├── dropdown │ │ ├── dropdown-menu.vue │ │ ├── index.js │ │ └── dropdown-item.vue │ ├── carousel │ │ ├── index.js │ │ └── carousel-item.vue │ ├── timeline │ │ ├── index.js │ │ ├── timeline.vue │ │ └── timeline-item.vue │ ├── checkbox │ │ ├── index.js │ │ └── checkbox-group.vue │ ├── select │ │ ├── index.js │ │ ├── option-group.vue │ │ └── option.vue │ ├── breadcrumb │ │ ├── index.js │ │ ├── breadcrumb.vue │ │ └── breadcrumb-item.vue │ ├── base │ │ ├── render.js │ │ ├── notification │ │ │ └── index.js │ │ └── collapse-transition.js │ ├── menu │ │ ├── index.js │ │ ├── menu-group.vue │ │ └── menu-item.vue │ ├── loading-bar │ │ └── loading-bar.js │ └── modal │ │ └── index.js ├── styles │ ├── components │ │ ├── checkbox.less │ │ ├── affix.less │ │ ├── circle.less │ │ ├── loading-bar.less │ │ ├── dropdown.less │ │ ├── back-top.less │ │ ├── select-dropdown.less │ │ ├── index.less │ │ ├── tooltip.less │ │ ├── card.less │ │ ├── collapse.less │ │ ├── badge.less │ │ ├── spin.less │ │ ├── tree.less │ │ ├── rate.less │ │ ├── message.less │ │ ├── form.less │ │ ├── input.less │ │ ├── upload.less │ │ ├── timeline.less │ │ ├── alert.less │ │ └── progress.less │ ├── common │ │ ├── index.less │ │ ├── iconfont │ │ │ ├── fonts │ │ │ │ ├── ionicons.eot │ │ │ │ ├── ionicons.ttf │ │ │ │ └── ionicons.woff │ │ │ ├── ionicons.less │ │ │ └── _ionicons-font.less │ │ ├── article.less │ │ ├── base.less │ │ └── layout.less │ ├── copyright.less │ ├── index.less │ ├── README.md │ ├── mixins │ │ ├── close.less │ │ ├── loading.less │ │ ├── mask.less │ │ ├── clearfix.less │ │ ├── index.less │ │ ├── size.less │ │ ├── breadcrumb.less │ │ ├── common.less │ │ ├── caret.less │ │ ├── content.less │ │ ├── select.less │ │ ├── layout.less │ │ └── tooltip.less │ └── animation │ │ ├── fade.less │ │ ├── ease.less │ │ └── index.less ├── mixins │ ├── locale.js │ └── emitter.js ├── directives │ └── clickoutside.js ├── utils │ ├── dom.js │ └── csv.js └── locale │ ├── format.js │ ├── index.js │ └── lang │ ├── vi-VN.js │ ├── zh-CN.js │ ├── zh-TW.js │ ├── ko-KR.js │ └── ja-JP.js ├── .npmignore ├── .travis.yml ├── .babelrc ├── .github └── ISSUE_TEMPLATE.md ├── .editorconfig ├── examples ├── routers │ ├── page.vue │ ├── back-top.vue │ ├── affix.vue │ ├── tooltip.vue │ ├── slider.vue │ ├── switch.vue │ ├── tag.vue │ ├── rate.vue │ ├── poptip.vue │ ├── grid.vue │ ├── radio.vue │ ├── loading-bar.vue │ ├── input-number.vue │ ├── badge.vue │ ├── checkbox.vue │ ├── dropdown.vue │ ├── input.vue │ ├── collapse.vue │ ├── breadcrumb.vue │ ├── message.vue │ ├── alert.vue │ ├── carousel.vue │ ├── circle.vue │ ├── progress.vue │ ├── transfer.vue │ ├── menu.vue │ ├── notice.vue │ ├── more.vue │ ├── timeline.vue │ ├── tree.vue │ ├── tabs.vue │ └── cascader.vue ├── components │ ├── tableExpand.vue │ └── test.vue └── index.html ├── test ├── .eslintrc.json └── unit │ ├── index.js │ ├── specs │ └── breadcrumb.spec.js │ ├── karma.conf.js │ └── util.js ├── .gitignore ├── .eslintrc.json └── CHANGE.md /.eslintignore: -------------------------------------------------------------------------------- 1 | src/directives -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | src/styles/**/* linguist-vendored=false -------------------------------------------------------------------------------- /assets/iview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gytai/iview/2.0/assets/iview.png -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gytai/iview/2.0/assets/logo.png -------------------------------------------------------------------------------- /assets/pay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gytai/iview/2.0/assets/pay.png -------------------------------------------------------------------------------- /assets/iview2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gytai/iview/2.0/assets/iview2.png -------------------------------------------------------------------------------- /src/components/card/index.js: -------------------------------------------------------------------------------- 1 | import Card from './card.vue'; 2 | export default Card; -------------------------------------------------------------------------------- /src/components/icon/index.js: -------------------------------------------------------------------------------- 1 | import Icon from './icon.vue'; 2 | export default Icon; -------------------------------------------------------------------------------- /src/components/page/index.js: -------------------------------------------------------------------------------- 1 | import Page from './page.vue'; 2 | export default Page; -------------------------------------------------------------------------------- /src/components/rate/index.js: -------------------------------------------------------------------------------- 1 | import Rate from './rate.vue'; 2 | export default Rate; -------------------------------------------------------------------------------- /src/components/spin/index.js: -------------------------------------------------------------------------------- 1 | import Spin from './spin.vue'; 2 | export default Spin; -------------------------------------------------------------------------------- /src/components/tag/index.js: -------------------------------------------------------------------------------- 1 | import Tag from './tag.vue'; 2 | export default Tag; -------------------------------------------------------------------------------- /src/components/tree/index.js: -------------------------------------------------------------------------------- 1 | import Tree from './tree.vue'; 2 | export default Tree; -------------------------------------------------------------------------------- /src/components/affix/index.js: -------------------------------------------------------------------------------- 1 | import Affix from './affix.vue'; 2 | export default Affix; -------------------------------------------------------------------------------- /src/components/alert/index.js: -------------------------------------------------------------------------------- 1 | import Alert from './alert.vue'; 2 | export default Alert; -------------------------------------------------------------------------------- /src/components/badge/index.js: -------------------------------------------------------------------------------- 1 | import Badge from './badge.vue'; 2 | export default Badge; -------------------------------------------------------------------------------- /src/components/input/index.js: -------------------------------------------------------------------------------- 1 | import Input from './input.vue'; 2 | export default Input; -------------------------------------------------------------------------------- /src/components/table/index.js: -------------------------------------------------------------------------------- 1 | import Table from './table.vue'; 2 | export default Table; -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .* 2 | *.md 3 | *.yml 4 | build/ 5 | node_modules/ 6 | test/ 7 | gulpfile.js 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "4" 4 | script: 5 | - npm run test 6 | -------------------------------------------------------------------------------- /src/components/circle/index.js: -------------------------------------------------------------------------------- 1 | import Circle from './circle.vue'; 2 | export default Circle; -------------------------------------------------------------------------------- /src/components/switch/index.js: -------------------------------------------------------------------------------- 1 | import Switch from './switch.vue'; 2 | export default Switch; -------------------------------------------------------------------------------- /src/components/back-top/index.js: -------------------------------------------------------------------------------- 1 | import BackTop from './back-top.vue'; 2 | export default BackTop; -------------------------------------------------------------------------------- /src/components/poptip/index.js: -------------------------------------------------------------------------------- 1 | import Poptip from './poptip.vue'; 2 | 3 | export default Poptip; -------------------------------------------------------------------------------- /src/components/slider/index.js: -------------------------------------------------------------------------------- 1 | import Slider from './slider.vue'; 2 | 3 | export default Slider; -------------------------------------------------------------------------------- /src/components/upload/index.js: -------------------------------------------------------------------------------- 1 | import Upload from './upload.vue'; 2 | 3 | export default Upload; -------------------------------------------------------------------------------- /src/components/cascader/index.js: -------------------------------------------------------------------------------- 1 | import Cascader from './cascader.vue'; 2 | export default Cascader; -------------------------------------------------------------------------------- /src/components/progress/index.js: -------------------------------------------------------------------------------- 1 | import Progress from './progress.vue'; 2 | export default Progress; -------------------------------------------------------------------------------- /src/components/tooltip/index.js: -------------------------------------------------------------------------------- 1 | import Tooltip from './tooltip.vue'; 2 | 3 | export default Tooltip; -------------------------------------------------------------------------------- /src/components/transfer/index.js: -------------------------------------------------------------------------------- 1 | import Transfer from './transfer.vue'; 2 | export default Transfer; -------------------------------------------------------------------------------- /src/styles/components/checkbox.less: -------------------------------------------------------------------------------- 1 | @checkbox-prefix-cls: ~"@{css-prefix}checkbox"; 2 | .checkboxFn(); -------------------------------------------------------------------------------- /src/components/input-number/index.js: -------------------------------------------------------------------------------- 1 | import InputNumber from './input-number.vue'; 2 | export default InputNumber; -------------------------------------------------------------------------------- /src/styles/components/affix.less: -------------------------------------------------------------------------------- 1 | .ivu-affix { 2 | position: fixed; 3 | z-index: @zindex-affix; 4 | } 5 | -------------------------------------------------------------------------------- /src/components/date-picker/index.js: -------------------------------------------------------------------------------- 1 | import DatePicker from './picker/date-picker'; 2 | 3 | export default DatePicker; -------------------------------------------------------------------------------- /src/components/grid/index.js: -------------------------------------------------------------------------------- 1 | import Row from './row.vue'; 2 | import Col from './col.vue'; 3 | 4 | export { Row, Col }; -------------------------------------------------------------------------------- /src/styles/common/index.less: -------------------------------------------------------------------------------- 1 | @import "base"; 2 | @import "iconfont/ionicons"; 3 | @import "layout"; 4 | @import "article"; -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"], 3 | "plugins": ["transform-runtime"], 4 | "comments": false 5 | } 6 | -------------------------------------------------------------------------------- /src/components/time-picker/index.js: -------------------------------------------------------------------------------- 1 | import TimePicker from '../date-picker/picker/time-picker'; 2 | export default TimePicker; -------------------------------------------------------------------------------- /src/styles/common/iconfont/fonts/ionicons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gytai/iview/2.0/src/styles/common/iconfont/fonts/ionicons.eot -------------------------------------------------------------------------------- /src/styles/common/iconfont/fonts/ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gytai/iview/2.0/src/styles/common/iconfont/fonts/ionicons.ttf -------------------------------------------------------------------------------- /src/styles/common/iconfont/fonts/ionicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gytai/iview/2.0/src/styles/common/iconfont/fonts/ionicons.woff -------------------------------------------------------------------------------- /src/styles/common/iconfont/ionicons.less: -------------------------------------------------------------------------------- 1 | @import "_ionicons-variables"; 2 | @import "_ionicons-font"; 3 | @import "_ionicons-icons"; 4 | -------------------------------------------------------------------------------- /src/components/tabs/index.js: -------------------------------------------------------------------------------- 1 | import Tabs from './tabs.vue'; 2 | import Pane from './pane.vue'; 3 | 4 | Tabs.Pane = Pane; 5 | export default Tabs; -------------------------------------------------------------------------------- /src/styles/copyright.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * iView 3 | * Web: https://www.iviewui.com 4 | * Github: https://github.com/iview/iview 5 | * Author: Aresn 6 | */ -------------------------------------------------------------------------------- /src/components/steps/index.js: -------------------------------------------------------------------------------- 1 | import Steps from './steps.vue'; 2 | import Step from './step.vue'; 3 | 4 | Steps.Step = Step; 5 | export default Steps; -------------------------------------------------------------------------------- /src/components/form/index.js: -------------------------------------------------------------------------------- 1 | import Form from './form.vue'; 2 | import FormItem from './form-item.vue'; 3 | 4 | Form.Item = FormItem; 5 | export default Form; -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/components/collapse/index.js: -------------------------------------------------------------------------------- 1 | import Collapse from './collapse.vue'; 2 | import Panel from './panel.vue'; 3 | 4 | Collapse.Panel = Panel; 5 | export default Collapse; -------------------------------------------------------------------------------- /src/components/radio/index.js: -------------------------------------------------------------------------------- 1 | import Radio from './radio.vue'; 2 | import RadioGroup from './radio-group.vue'; 3 | 4 | Radio.Group = RadioGroup; 5 | export default Radio; -------------------------------------------------------------------------------- /src/styles/index.less: -------------------------------------------------------------------------------- 1 | @import "./custom"; 2 | @import "./mixins/index"; 3 | @import "./common/index"; 4 | @import "./animation/index"; 5 | @import "./components/index"; 6 | -------------------------------------------------------------------------------- /src/components/button/index.js: -------------------------------------------------------------------------------- 1 | import Button from './button.vue'; 2 | import ButtonGroup from './button-group.vue'; 3 | 4 | Button.Group = ButtonGroup; 5 | export default Button; -------------------------------------------------------------------------------- /src/styles/README.md: -------------------------------------------------------------------------------- 1 | # 样式库说明 2 | 3 | ## 目录 4 | 5 | |-- animation (动画) 6 | 7 | |-- common (全局样式) 8 | 9 | |-- components (组件样式) 10 | 11 | |-- mixins (混入) 12 | -------------------------------------------------------------------------------- /src/components/dropdown/dropdown-menu.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | charset = utf-8 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | -------------------------------------------------------------------------------- /src/components/carousel/index.js: -------------------------------------------------------------------------------- 1 | import Carousel from './carousel.vue'; 2 | import CarouselItem from './carousel-item.vue'; 3 | 4 | Carousel.Item = CarouselItem; 5 | export default Carousel; -------------------------------------------------------------------------------- /src/components/timeline/index.js: -------------------------------------------------------------------------------- 1 | import Timeline from './timeline.vue'; 2 | import TimelineItem from './timeline-item.vue'; 3 | 4 | Timeline.Item = TimelineItem; 5 | export default Timeline; -------------------------------------------------------------------------------- /examples/routers/page.vue: -------------------------------------------------------------------------------- 1 | 4 | 9 | -------------------------------------------------------------------------------- /src/components/checkbox/index.js: -------------------------------------------------------------------------------- 1 | import Checkbox from './checkbox.vue'; 2 | import CheckboxGroup from './checkbox-group.vue'; 3 | 4 | Checkbox.Group = CheckboxGroup; 5 | export default Checkbox; -------------------------------------------------------------------------------- /src/components/select/index.js: -------------------------------------------------------------------------------- 1 | import Select from './select.vue'; 2 | import Option from './option.vue'; 3 | import OptionGroup from './option-group.vue'; 4 | 5 | export { Select, Option, OptionGroup }; -------------------------------------------------------------------------------- /src/components/breadcrumb/index.js: -------------------------------------------------------------------------------- 1 | import Breadcrumb from './breadcrumb.vue'; 2 | import BreadcrumbItem from './breadcrumb-item.vue'; 3 | 4 | Breadcrumb.Item = BreadcrumbItem; 5 | export default Breadcrumb; -------------------------------------------------------------------------------- /src/mixins/locale.js: -------------------------------------------------------------------------------- 1 | import { t } from '../locale'; 2 | 3 | export default { 4 | methods: { 5 | t(...args) { 6 | return t.apply(this, args); 7 | } 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /src/components/base/render.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'RenderCell', 3 | functional: true, 4 | props: { 5 | render: Function 6 | }, 7 | render: (h, ctx) => { 8 | return ctx.props.render(h); 9 | } 10 | }; -------------------------------------------------------------------------------- /src/components/dropdown/index.js: -------------------------------------------------------------------------------- 1 | import Dropdown from './dropdown.vue'; 2 | import DropdownMenu from './dropdown-menu.vue'; 3 | import DropdownItem from './dropdown-item.vue'; 4 | 5 | Dropdown.Menu = DropdownMenu; 6 | Dropdown.Item = DropdownItem; 7 | export default Dropdown; -------------------------------------------------------------------------------- /src/styles/mixins/close.less: -------------------------------------------------------------------------------- 1 | .close-base(@top: 0, @icon-font-size: 22px) { 2 | font-size: @icon-font-size; 3 | color: @legend-color; 4 | transition: color @transition-time ease; 5 | position: relative; 6 | top: @top; 7 | &:hover { 8 | color: #444; 9 | } 10 | } -------------------------------------------------------------------------------- /src/components/menu/index.js: -------------------------------------------------------------------------------- 1 | import Menu from './menu.vue'; 2 | import MenuGroup from './menu-group.vue'; 3 | import MenuItem from './menu-item.vue'; 4 | import Submenu from './submenu.vue'; 5 | 6 | Menu.Group = MenuGroup; 7 | Menu.Item = MenuItem; 8 | Menu.Sub = Submenu; 9 | 10 | export default Menu; -------------------------------------------------------------------------------- /src/styles/mixins/loading.less: -------------------------------------------------------------------------------- 1 | // Loading for loop 2 | .ivu-load-loop{ 3 | animation: ani-load-loop 1s linear infinite; 4 | } 5 | 6 | @keyframes ani-load-loop { 7 | from { transform: rotate(0deg);} 8 | 50% { transform: rotate(180deg);} 9 | to { transform: rotate(360deg);} 10 | } 11 | -------------------------------------------------------------------------------- /src/styles/mixins/mask.less: -------------------------------------------------------------------------------- 1 | .mask() { 2 | position: fixed; 3 | top: 0; 4 | bottom: 0; 5 | left: 0; 6 | right: 0; 7 | background-color: rgba(55, 55, 55, 0.6); 8 | height: 100%; 9 | z-index: @zindex-modal; 10 | 11 | &-hidden { 12 | display: none; 13 | } 14 | } -------------------------------------------------------------------------------- /src/styles/mixins/clearfix.less: -------------------------------------------------------------------------------- 1 | .clearfix() { 2 | zoom: 1; 3 | &:before, 4 | &:after { 5 | content: ""; 6 | display: table; 7 | } 8 | &:after { 9 | clear: both; 10 | visibility: hidden; 11 | font-size: 0; 12 | height: 0; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "../.eslintrc.json" 4 | ], 5 | "globals": { 6 | "expect": true 7 | }, 8 | "env": { 9 | "node": true, 10 | "mocha": true 11 | }, 12 | "rules": { 13 | "indent": ["error",2, { "SwitchCase": 1 }] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea 3 | .ipr 4 | .iws 5 | *.diff 6 | *.patch 7 | *.bak 8 | .DS_Store 9 | node_modules/ 10 | node_modules2/ 11 | .project 12 | .settings 13 | npm-debug.log 14 | .*proj 15 | .svn/ 16 | *.swp 17 | *.swo 18 | *.log 19 | examples/dist/ 20 | dist/ 21 | yarn-error.log 22 | test/unit/coverage 23 | .vscode -------------------------------------------------------------------------------- /src/styles/mixins/index.less: -------------------------------------------------------------------------------- 1 | @import "common"; 2 | @import "clearfix"; 3 | @import "button"; 4 | @import "layout"; 5 | @import "size"; 6 | @import "loading"; 7 | @import "close"; 8 | @import "checkbox"; 9 | @import "input"; 10 | @import "breadcrumb"; 11 | @import "mask"; 12 | @import "content"; // card、modal 13 | @import "tooltip"; 14 | @import "select"; 15 | @import "caret"; -------------------------------------------------------------------------------- /examples/routers/back-top.vue: -------------------------------------------------------------------------------- 1 | 6 | 11 | 23 | -------------------------------------------------------------------------------- /examples/components/tableExpand.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/styles/components/circle.less: -------------------------------------------------------------------------------- 1 | @circle-prefix-cls: ~"@{css-prefix}chart-circle"; 2 | 3 | .@{circle-prefix-cls}{ 4 | display: inline-block; 5 | position: relative; 6 | 7 | &-inner { 8 | width: 100%; 9 | text-align: center; 10 | position: absolute; 11 | left: 0; 12 | top: 50%; 13 | transform: translateY(-50%); 14 | line-height: 1; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/routers/affix.vue: -------------------------------------------------------------------------------- 1 | 12 | 17 | 22 | -------------------------------------------------------------------------------- /src/components/table/header.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'TableRenderHeader', 3 | functional: true, 4 | props: { 5 | render: Function, 6 | column: Object, 7 | index: Number 8 | }, 9 | render: (h, ctx) => { 10 | const params = { 11 | column: ctx.props.column, 12 | index: ctx.props.index 13 | }; 14 | return ctx.props.render(h, params); 15 | } 16 | }; -------------------------------------------------------------------------------- /examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | iView test page 6 | 7 | 8 | 9 |
10 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/routers/tooltip.vue: -------------------------------------------------------------------------------- 1 | 11 | 16 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parserOptions": { 4 | "ecmaVersion": 6, 5 | "sourceType": "module" 6 | }, 7 | "env": { 8 | "browser": true 9 | }, 10 | "extends": "eslint:recommended", 11 | "plugins": [ "html" ], 12 | "rules": { 13 | "indent": ["error", 4, { "SwitchCase": 1 }], 14 | "quotes": ["error", "single"], 15 | "semi": ["error", "always"], 16 | "no-console": ["error"] 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/routers/slider.vue: -------------------------------------------------------------------------------- 1 | 7 | 21 | -------------------------------------------------------------------------------- /src/styles/components/loading-bar.less: -------------------------------------------------------------------------------- 1 | @loading-bar-prefix-cls: ~"@{css-prefix}loading-bar"; 2 | 3 | .@{loading-bar-prefix-cls} { 4 | width: 100%; 5 | position: fixed; 6 | top: 0; 7 | left: 0; 8 | right: 0; 9 | z-index: @zindex-loading-bar; 10 | 11 | &-inner { 12 | transition: width @transition-time linear; 13 | 14 | &-color-primary { 15 | background-color: @primary-color; 16 | } 17 | 18 | &-failed-color-error { 19 | background-color: @error-color; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/styles/mixins/size.less: -------------------------------------------------------------------------------- 1 | .size(@width; @height) { 2 | width: @width; 3 | height: @height; 4 | } 5 | 6 | .square(@size) { 7 | .size(@size; @size); 8 | } 9 | 10 | // fix chrome 12px bug, support ie 11 | .iconfont-size-under-12px(@size, @rotate: 0deg) { 12 | display: inline-block; 13 | @font-scale: unit(@size / @font-size-base); 14 | font-size: @font-size-base; 15 | font-size: ~"@{size} \9"; // ie8-9 16 | transform: scale(@font-scale) rotate(@rotate); 17 | :root & { 18 | font-size: @font-size-base; // reset ie9 and above 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/components/table/expand.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'TableExpand', 3 | functional: true, 4 | props: { 5 | row: Object, 6 | render: Function, 7 | index: Number, 8 | column: { 9 | type: Object, 10 | default: null 11 | } 12 | }, 13 | render: (h, ctx) => { 14 | const params = { 15 | row: ctx.props.row, 16 | index: ctx.props.index 17 | }; 18 | if (ctx.props.column) params.column = ctx.props.column; 19 | return ctx.props.render(h, params); 20 | } 21 | }; -------------------------------------------------------------------------------- /examples/routers/switch.vue: -------------------------------------------------------------------------------- 1 | 11 | 25 | -------------------------------------------------------------------------------- /examples/routers/tag.vue: -------------------------------------------------------------------------------- 1 | 7 | 22 | -------------------------------------------------------------------------------- /examples/components/test.vue: -------------------------------------------------------------------------------- 1 | 16 | 21 | -------------------------------------------------------------------------------- /examples/routers/rate.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /src/components/menu/menu-group.vue: -------------------------------------------------------------------------------- 1 | 7 | 25 | -------------------------------------------------------------------------------- /src/components/date-picker/time-mixins.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | disabledHours: { 4 | type: Array, 5 | default () { 6 | return []; 7 | } 8 | }, 9 | disabledMinutes: { 10 | type: Array, 11 | default () { 12 | return []; 13 | } 14 | }, 15 | disabledSeconds: { 16 | type: Array, 17 | default () { 18 | return []; 19 | } 20 | }, 21 | hideDisabledOptions: { 22 | type: Boolean, 23 | default: false 24 | } 25 | } 26 | }; -------------------------------------------------------------------------------- /src/directives/clickoutside.js: -------------------------------------------------------------------------------- 1 | export default { 2 | bind (el, binding, vnode) { 3 | function documentHandler (e) { 4 | if (el.contains(e.target)) { 5 | return false; 6 | } 7 | if (binding.expression) { 8 | binding.value(e); 9 | } 10 | } 11 | el.__vueClickOutside__ = documentHandler; 12 | document.addEventListener('click', documentHandler); 13 | }, 14 | update () { 15 | 16 | }, 17 | unbind (el, binding) { 18 | document.removeEventListener('click', el.__vueClickOutside__); 19 | delete el.__vueClickOutside__; 20 | } 21 | }; -------------------------------------------------------------------------------- /src/styles/components/dropdown.less: -------------------------------------------------------------------------------- 1 | @dropdown-prefix-cls: ~"@{css-prefix}dropdown"; 2 | @dropdown-item-prefix-cls: ~"@{dropdown-prefix-cls}-item"; 3 | 4 | .@{dropdown-prefix-cls} { 5 | display: inline-block; 6 | //position: relative; 7 | 8 | .@{select-dropdown-prefix-cls} { 9 | overflow: visible; 10 | max-height: none; 11 | } 12 | .@{dropdown-prefix-cls} { 13 | width: 100%; 14 | } 15 | 16 | &-rel{ 17 | //display: inline-block; 18 | position: relative; 19 | } 20 | 21 | &-menu{ 22 | min-width: 100px; 23 | } 24 | } 25 | 26 | .select-item(@dropdown-prefix-cls, @dropdown-item-prefix-cls); -------------------------------------------------------------------------------- /src/styles/animation/fade.less: -------------------------------------------------------------------------------- 1 | .fade-motion(@className, @keyframeName) { 2 | .make-motion(@className, @keyframeName); 3 | .@{className}-enter-active, .@{className}-appear { 4 | opacity: 0; 5 | animation-timing-function: linear; 6 | } 7 | .@{className}-leave-active { 8 | animation-timing-function: linear; 9 | } 10 | } 11 | 12 | .fade-motion(fade, ivuFade); 13 | 14 | @keyframes ivuFadeIn { 15 | 0% { 16 | opacity: 0; 17 | } 18 | 100% { 19 | opacity: 1; 20 | } 21 | } 22 | 23 | @keyframes ivuFadeOut { 24 | 0% { 25 | opacity: 1; 26 | } 27 | 100% { 28 | opacity: 0; 29 | } 30 | } -------------------------------------------------------------------------------- /examples/routers/poptip.vue: -------------------------------------------------------------------------------- 1 | 17 | 22 | -------------------------------------------------------------------------------- /examples/routers/grid.vue: -------------------------------------------------------------------------------- 1 | 6 | 16 | 30 | -------------------------------------------------------------------------------- /src/components/timeline/timeline.vue: -------------------------------------------------------------------------------- 1 | 6 | 29 | -------------------------------------------------------------------------------- /src/styles/components/back-top.less: -------------------------------------------------------------------------------- 1 | @backtop-prefix-cls: ~"@{css-prefix}back-top"; 2 | 3 | .@{backtop-prefix-cls} { 4 | z-index: @zindex-back-top; 5 | position: fixed; 6 | cursor: pointer; 7 | display: none; 8 | 9 | &.@{backtop-prefix-cls}-show { 10 | display: block; 11 | } 12 | 13 | &-inner { 14 | background-color: rgba(0,0,0,.6); 15 | border-radius: 2px; 16 | box-shadow: 0 1px 3px rgba(0,0,0,.2); 17 | transition: all @transition-time @ease-in-out; 18 | 19 | &:hover { 20 | background-color: rgba(0,0,0,.7); 21 | } 22 | } 23 | 24 | i{ 25 | color: #fff; 26 | font-size: 24px; 27 | padding: 8px 12px; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/styles/components/select-dropdown.less: -------------------------------------------------------------------------------- 1 | @select-dropdown-prefix-cls: ~"@{css-prefix}select-dropdown"; 2 | 3 | .@{select-dropdown-prefix-cls} { 4 | width: inherit; 5 | max-height: 200px; 6 | overflow: auto; 7 | margin: 5px 0; 8 | padding: 5px 0; 9 | background-color: #fff; 10 | box-sizing: border-box; 11 | //border: 1px solid @border-color-split; 12 | border-radius: @btn-border-radius; 13 | //box-shadow: 0 1px 3px rgba(0,0,0,.2); 14 | box-shadow: @shadow-base; 15 | position: absolute; 16 | z-index: @zindex-select; 17 | &-transfer{ 18 | z-index: @zindex-transfer; 19 | } 20 | } 21 | .@{modal-prefix-cls} { 22 | .@{select-dropdown-prefix-cls} { 23 | position: absolute !important; 24 | } 25 | } -------------------------------------------------------------------------------- /examples/routers/radio.vue: -------------------------------------------------------------------------------- 1 | 12 | 30 | -------------------------------------------------------------------------------- /src/styles/mixins/breadcrumb.less: -------------------------------------------------------------------------------- 1 | @breadcrumb-prefix-cls: ~"@{css-prefix}breadcrumb"; 2 | 3 | .@{breadcrumb-prefix-cls} { 4 | color: #999; 5 | font-size: @font-size-base; 6 | 7 | a { 8 | color: @text-color; 9 | transition: color @transition-time @ease-in-out; 10 | &:hover { 11 | color: tint(@primary-color, 20%); 12 | } 13 | } 14 | 15 | & > span:last-child { 16 | font-weight: bold; 17 | color: @text-color; 18 | } 19 | 20 | & > span:last-child &-item-separator { 21 | display: none; 22 | } 23 | 24 | &-item-separator { 25 | margin: 0 8px; 26 | color: @border-color-base; 27 | } 28 | 29 | &-item-link { 30 | > .ivu-icon + span { 31 | margin-left: 4px; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | Vue.config.productionTip = false; 3 | 4 | // Polyfill fn.bind() for PhantomJS 5 | /* eslint-disable no-extend-native */ 6 | Function.prototype.bind = require('function-bind'); 7 | 8 | // require all test files (files that ends with .spec.js) 9 | const testsContext = require.context('./specs', true, /\.spec$/); 10 | testsContext.keys().forEach(testsContext); 11 | 12 | // require all src files except main.js for coverage. 13 | // you can also change this to match only the subset of files that 14 | // you want coverage for. 15 | // const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/); 16 | // @todo 17 | const srcContext = require.context('../../src/components/breadcrumb', true, /^\.\/(?!styles.*?(\.less)?$)/); 18 | srcContext.keys().forEach(srcContext); 19 | -------------------------------------------------------------------------------- /src/styles/mixins/common.less: -------------------------------------------------------------------------------- 1 | .placeholder(@color: @input-placeholder-color) { 2 | // Firefox 3 | &::-moz-placeholder { 4 | color: @color; 5 | opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526 6 | } 7 | // Internet Explorer 10+ 8 | &:-ms-input-placeholder { 9 | color: @color; 10 | } 11 | // Safari and Chrome 12 | &::-webkit-input-placeholder { 13 | color: @color; 14 | } 15 | } 16 | 17 | // for select and input like component's arrow 18 | .inner-arrow() { 19 | position: absolute; 20 | top: 50%; 21 | right: 8px; 22 | line-height: 1; 23 | margin-top: -7px; 24 | font-size: @font-size-base; 25 | color: @subsidiary-color; 26 | transition: all @transition-time @ease-in-out; 27 | } 28 | -------------------------------------------------------------------------------- /examples/routers/loading-bar.vue: -------------------------------------------------------------------------------- 1 | 8 | 30 | -------------------------------------------------------------------------------- /src/styles/mixins/caret.less: -------------------------------------------------------------------------------- 1 | // sortable 2 | .sortable() { 3 | display: inline-block; 4 | width: 9px; 5 | height: 12px; 6 | margin-left: 4px; 7 | margin-top: -1px; 8 | vertical-align: middle; 9 | overflow: hidden; 10 | cursor: pointer; 11 | position: relative; 12 | 13 | i { 14 | display: block; 15 | height: 6px; 16 | line-height: 6px; 17 | overflow: hidden; 18 | position: absolute; 19 | color: @btn-disable-color; 20 | transition: color @transition-time @ease-in-out; 21 | 22 | &:hover{ 23 | color: inherit; 24 | } 25 | 26 | &.on{ 27 | color: @primary-color; 28 | } 29 | 30 | &:first-child{ 31 | top: 0; 32 | } 33 | &:last-child{ 34 | bottom: 0; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/routers/input-number.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /examples/routers/badge.vue: -------------------------------------------------------------------------------- 1 | 10 | 15 | 23 | -------------------------------------------------------------------------------- /src/components/icon/icon.vue: -------------------------------------------------------------------------------- 1 | 4 | 34 | -------------------------------------------------------------------------------- /src/styles/animation/ease.less: -------------------------------------------------------------------------------- 1 | .ease-motion(@className, @keyframeName) { 2 | .make-motion(@className, @keyframeName); 3 | .@{className}-enter-active, .@{className}-appear { 4 | opacity: 0; 5 | animation-timing-function: linear; 6 | animation-duration: @transition-time; 7 | } 8 | .@{className}-leave-active { 9 | animation-timing-function: linear; 10 | animation-duration: @transition-time; 11 | } 12 | } 13 | 14 | .ease-motion(ease, ivuEase); 15 | 16 | @keyframes ivuEaseIn { 17 | 0% { 18 | opacity: 0; 19 | transform: scale(0.9); 20 | } 21 | 100% { 22 | opacity: 1; 23 | transform: scale(1); 24 | } 25 | } 26 | 27 | @keyframes ivuEaseOut { 28 | 0% { 29 | opacity: 1; 30 | transform: scale(1); 31 | } 32 | 100% { 33 | opacity: 0; 34 | transform: scale(0.9); 35 | } 36 | } -------------------------------------------------------------------------------- /src/styles/mixins/content.less: -------------------------------------------------------------------------------- 1 | @icon-prefix-cls: ~"@{css-prefix}icon"; 2 | 3 | .content-header() { 4 | border-bottom: 1px solid @border-color-split; 5 | padding: 14px 16px; 6 | line-height: 1; 7 | 8 | p, 9 | &-inner 10 | { 11 | display: inline-block; 12 | width: 100%; 13 | height: 20px; 14 | line-height: 20px; 15 | font-size: @font-size-base; 16 | color: @title-color; 17 | font-weight: bold; 18 | overflow: hidden; 19 | text-overflow: ellipsis; 20 | white-space: nowrap; 21 | } 22 | } 23 | 24 | .content-close(@top: 0, @icon-font-size: 22px) { 25 | font-size: @font-size-small; 26 | position: absolute; 27 | right: 16px; 28 | top: 8px; 29 | overflow: hidden; 30 | cursor: pointer; 31 | 32 | .@{icon-prefix-cls}-ios-close-empty { 33 | .close-base(@top, @icon-font-size); 34 | } 35 | } -------------------------------------------------------------------------------- /src/components/date-picker/panel/mixin.js: -------------------------------------------------------------------------------- 1 | const prefixCls = 'ivu-picker-panel'; 2 | const datePrefixCls = 'ivu-date-picker'; 3 | 4 | export default { 5 | methods: { 6 | iconBtnCls (direction, type = '') { 7 | return [ 8 | `${prefixCls}-icon-btn`, 9 | `${datePrefixCls}-${direction}-btn`, 10 | `${datePrefixCls}-${direction}-btn-arrow${type}`, 11 | ]; 12 | }, 13 | handleShortcutClick (shortcut) { 14 | if (shortcut.value) this.$emit('on-pick', shortcut.value()); 15 | if (shortcut.onClick) shortcut.onClick(this); 16 | }, 17 | handlePickClear () { 18 | this.$emit('on-pick-clear'); 19 | }, 20 | handlePickSuccess () { 21 | this.$emit('on-pick-success'); 22 | }, 23 | handlePickClick () { 24 | this.$emit('on-pick-click'); 25 | } 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /src/styles/components/index.less: -------------------------------------------------------------------------------- 1 | @import "button"; 2 | @import "affix"; 3 | @import "back-top"; 4 | @import "badge"; 5 | @import "circle"; 6 | @import "spin"; 7 | @import "alert"; 8 | @import "collapse"; 9 | @import "card"; 10 | @import "message"; 11 | @import "notice"; 12 | @import "radio"; 13 | @import "checkbox"; 14 | @import "switch"; 15 | @import "input-number"; 16 | @import "tag"; 17 | @import "loading-bar"; 18 | @import "progress"; 19 | @import "timeline"; 20 | @import "page"; 21 | @import "steps"; 22 | @import "modal"; 23 | @import "select"; 24 | @import "select-dropdown"; 25 | @import "tooltip"; 26 | @import "poptip"; 27 | @import "input"; 28 | @import "slider"; 29 | @import "cascader"; 30 | @import "transfer"; 31 | @import "table"; 32 | @import "dropdown"; 33 | @import "tabs"; 34 | @import "menu"; 35 | @import "date-picker"; 36 | @import "time-picker"; 37 | @import "form"; 38 | @import "carousel"; 39 | @import "rate"; 40 | @import "upload"; 41 | @import "tree"; -------------------------------------------------------------------------------- /test/unit/specs/breadcrumb.spec.js: -------------------------------------------------------------------------------- 1 | import { createVue, destroyVM } from '../util'; 2 | 3 | describe('Breadcrumb.vue', () => { 4 | let vm; 5 | afterEach(() => { 6 | destroyVM(vm); 7 | }); 8 | it('create', done => { 9 | vm = createVue(` 10 | 11 | Home4 12 | Components 13 | Breadcrumb 14 | 15 | `); 16 | expect(vm.$el.querySelectorAll('.ivu-breadcrumb-item-link').length).to.equal(3); 17 | 18 | vm.$nextTick(_ => { 19 | // console.log(vm.$el.querySelector('.ivu-breadcrumb-item-separator').innerHTML); 20 | expect(vm.$el.querySelector('.ivu-breadcrumb-item-separator').innerHTML).to.equal('=>'); 21 | done(); 22 | }); 23 | }); 24 | }); -------------------------------------------------------------------------------- /examples/routers/checkbox.vue: -------------------------------------------------------------------------------- 1 | 9 | 35 | -------------------------------------------------------------------------------- /src/components/carousel/carousel-item.vue: -------------------------------------------------------------------------------- 1 | 4 | 35 | -------------------------------------------------------------------------------- /src/styles/animation/index.less: -------------------------------------------------------------------------------- 1 | .motion-common() { 2 | animation-duration: @animation-time; 3 | animation-fill-mode: both; 4 | } 5 | 6 | .make-motion(@className, @keyframeName) { 7 | .@{className}-enter-active, .@{className}-appear { 8 | .motion-common(); 9 | animation-play-state: paused; 10 | } 11 | .@{className}-leave-active { 12 | .motion-common(); 13 | animation-play-state: paused; 14 | } 15 | .@{className}-enter-active, .@{className}-appear { 16 | animation-name: ~"@{keyframeName}In"; 17 | animation-play-state: running; 18 | } 19 | .@{className}-leave-active { 20 | animation-name: ~"@{keyframeName}Out"; 21 | animation-play-state: running; 22 | } 23 | } 24 | 25 | @import "fade"; 26 | @import "move"; 27 | @import "ease"; 28 | @import "slide"; 29 | 30 | .collapse-transition { 31 | transition: @transition-time height ease-in-out, @transition-time padding-top ease-in-out, @transition-time padding-bottom ease-in-out; 32 | } -------------------------------------------------------------------------------- /examples/routers/dropdown.vue: -------------------------------------------------------------------------------- 1 | 25 | 30 | -------------------------------------------------------------------------------- /src/components/date-picker/picker/time-picker.js: -------------------------------------------------------------------------------- 1 | import Picker from '../picker.vue'; 2 | import TimePanel from '../panel/time.vue'; 3 | import TimeRangePanel from '../panel/time-range.vue'; 4 | import Options from '../time-mixins'; 5 | 6 | const getPanel = function (type) { 7 | if (type === 'timerange') { 8 | return TimeRangePanel; 9 | } 10 | return TimePanel; 11 | }; 12 | 13 | import { oneOf } from '../../../utils/assist'; 14 | 15 | export default { 16 | mixins: [Picker, Options], 17 | props: { 18 | type: { 19 | validator (value) { 20 | return oneOf(value, ['time', 'timerange']); 21 | }, 22 | default: 'time' 23 | }, 24 | value: {} 25 | }, 26 | created () { 27 | if (!this.currentValue) { 28 | if (this.type === 'timerange') { 29 | this.currentValue = ['','']; 30 | } else { 31 | this.currentValue = ''; 32 | } 33 | } 34 | this.panel = getPanel(this.type); 35 | } 36 | }; -------------------------------------------------------------------------------- /src/components/table/table-tr.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /src/styles/common/iconfont/_ionicons-font.less: -------------------------------------------------------------------------------- 1 | // Ionicons Font Path 2 | // -------------------------- 3 | 4 | @font-face { 5 | font-family: @ionicons-font-family; 6 | src:url("@{ionicons-font-path}/ionicons.eot?v=@{ionicons-version}"); 7 | src:url("@{ionicons-font-path}/ionicons.eot?v=@{ionicons-version}#iefix") format("embedded-opentype"), 8 | url("@{ionicons-font-path}/ionicons.ttf?v=@{ionicons-version}") format("truetype"), 9 | url("@{ionicons-font-path}/ionicons.woff?v=@{ionicons-version}") format("woff"), 10 | url("@{ionicons-font-path}/ionicons.svg?v=@{ionicons-version}#Ionicons") format("svg"); 11 | font-weight: normal; 12 | font-style: normal; 13 | } 14 | 15 | .ivu-icon() { 16 | display: inline-block; 17 | font-family: @ionicons-font-family; 18 | speak: none; 19 | font-style: normal; 20 | font-weight: normal; 21 | font-variant: normal; 22 | text-transform: none; 23 | text-rendering: auto; 24 | line-height: 1; 25 | -webkit-font-smoothing: antialiased; 26 | -moz-osx-font-smoothing: grayscale; 27 | } 28 | 29 | .ivu-icon { 30 | .ivu-icon(); 31 | } -------------------------------------------------------------------------------- /examples/routers/input.vue: -------------------------------------------------------------------------------- 1 | 18 | 32 | -------------------------------------------------------------------------------- /src/components/base/notification/index.js: -------------------------------------------------------------------------------- 1 | import Notification from './notification.vue'; 2 | import Vue from 'vue'; 3 | 4 | Notification.newInstance = properties => { 5 | const _props = properties || {}; 6 | 7 | const Instance = new Vue({ 8 | data: _props, 9 | render (h) { 10 | return h(Notification, { 11 | props: _props 12 | }); 13 | } 14 | }); 15 | 16 | const component = Instance.$mount(); 17 | document.body.appendChild(component.$el); 18 | const notification = Instance.$children[0]; 19 | 20 | return { 21 | notice (noticeProps) { 22 | notification.add(noticeProps); 23 | }, 24 | remove (name) { 25 | notification.close(name); 26 | }, 27 | component: notification, 28 | destroy (element) { 29 | notification.closeAll(); 30 | setTimeout(function() { 31 | document.body.removeChild(document.getElementsByClassName(element)[0]); 32 | }, 500); 33 | } 34 | }; 35 | }; 36 | 37 | export default Notification; 38 | -------------------------------------------------------------------------------- /src/styles/components/tooltip.less: -------------------------------------------------------------------------------- 1 | @tooltip-prefix-cls: ~"@{css-prefix}tooltip"; 2 | @tooltip-arrow: ~"@{tooltip-prefix-cls}-arrow"; 3 | @tooltip-max-width: 250px; 4 | @tooltip-arrow-width: 5px; 5 | @tooltip-distance: @tooltip-arrow-width - 1 + 4; 6 | 7 | .@{tooltip-prefix-cls} { 8 | display: inline-block; 9 | 10 | &-rel{ 11 | display: inline-block; 12 | position: relative; 13 | } 14 | 15 | &-popper{ 16 | .popper(@tooltip-arrow, @tooltip-arrow-width, @tooltip-distance, @tooltip-bg); 17 | } 18 | 19 | &-inner{ 20 | max-width: @tooltip-max-width; 21 | min-height: 34px; 22 | padding: 8px 12px; 23 | color: @tooltip-color; 24 | text-align: left; 25 | text-decoration: none; 26 | background-color: @tooltip-bg; 27 | border-radius: @border-radius-small; 28 | box-shadow: @shadow-base; 29 | white-space: nowrap; 30 | } 31 | 32 | &-arrow{ 33 | position: absolute; 34 | width: 0; 35 | height: 0; 36 | border-color: transparent; 37 | border-style: solid; 38 | } 39 | } -------------------------------------------------------------------------------- /src/components/date-picker/picker/date-picker.js: -------------------------------------------------------------------------------- 1 | import Picker from '../picker.vue'; 2 | import DatePanel from '../panel/date.vue'; 3 | import DateRangePanel from '../panel/date-range.vue'; 4 | 5 | const getPanel = function (type) { 6 | if (type === 'daterange' || type === 'datetimerange') { 7 | return DateRangePanel; 8 | } 9 | return DatePanel; 10 | }; 11 | 12 | import { oneOf } from '../../../utils/assist'; 13 | 14 | export default { 15 | mixins: [Picker], 16 | props: { 17 | type: { 18 | validator (value) { 19 | return oneOf(value, ['year', 'month', 'date', 'daterange', 'datetime', 'datetimerange']); 20 | }, 21 | default: 'date' 22 | }, 23 | value: {} 24 | }, 25 | created () { 26 | if (!this.currentValue) { 27 | if (this.type === 'daterange' || this.type === 'datetimerange') { 28 | this.currentValue = ['','']; 29 | } else { 30 | this.currentValue = ''; 31 | } 32 | } 33 | 34 | this.panel = getPanel(this.type); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | // This is a karma config file. For more details see 2 | // http://karma-runner.github.io/0.13/config/configuration-file.html 3 | // we are also using it with karma-webpack 4 | // https://github.com/webpack/karma-webpack 5 | 6 | var webpackConfig = require('../../build/webpack.test.config.js'); 7 | 8 | module.exports = function (config) { 9 | config.set({ 10 | // to run in additional browsers: 11 | // 1. install corresponding karma launcher 12 | // http://karma-runner.github.io/0.13/config/browsers.html 13 | // 2. add it to the `browsers` array below. 14 | browsers: ['PhantomJS'], 15 | frameworks: ['mocha', 'sinon-chai'], 16 | reporters: ['spec', 'coverage'], 17 | files: ['./index.js'], 18 | preprocessors: { 19 | './index.js': ['webpack', 'sourcemap'] 20 | }, 21 | webpack: webpackConfig, 22 | webpackMiddleware: { 23 | noInfo: true, 24 | }, 25 | coverageReporter: { 26 | dir: './coverage', 27 | reporters: [ 28 | { type: 'lcov', subdir: '.' }, 29 | { type: 'text-summary' }, 30 | ] 31 | }, 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /examples/routers/collapse.vue: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /src/components/breadcrumb/breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 6 | 44 | -------------------------------------------------------------------------------- /src/styles/components/card.less: -------------------------------------------------------------------------------- 1 | @card-prefix-cls: ~"@{css-prefix}card"; 2 | 3 | .@{card-prefix-cls}{ 4 | background: #fff; 5 | border-radius: @border-radius-small; 6 | font-size: @font-size-base; 7 | position: relative; 8 | //overflow: hidden; 9 | transition: all @transition-time @ease-in-out; 10 | 11 | &-bordered { 12 | border: 1px solid @border-color-base; 13 | border-color: @border-color-split; 14 | } 15 | 16 | &-shadow{ 17 | box-shadow: @shadow-card; 18 | } 19 | 20 | &:hover { 21 | box-shadow: @shadow-base; 22 | border-color: #eee; 23 | } 24 | &&-dis-hover:hover{ 25 | box-shadow: none; 26 | border-color: transparent; 27 | } 28 | 29 | &&-dis-hover&-bordered:hover{ 30 | border-color: @border-color-split; 31 | } 32 | 33 | &&-shadow:hover{ 34 | box-shadow: @shadow-card; 35 | } 36 | 37 | &-head { 38 | .content-header; 39 | } 40 | 41 | &-extra { 42 | position: absolute; 43 | right: 16px; 44 | top: 14px; 45 | } 46 | 47 | &-body { 48 | padding: 16px; 49 | } 50 | } -------------------------------------------------------------------------------- /src/components/transfer/operation.vue: -------------------------------------------------------------------------------- 1 | 11 | 34 | -------------------------------------------------------------------------------- /examples/routers/breadcrumb.vue: -------------------------------------------------------------------------------- 1 | 7 | 31 | -------------------------------------------------------------------------------- /src/components/loading-bar/loading-bar.js: -------------------------------------------------------------------------------- 1 | import LoadingBar from './loading-bar.vue'; 2 | import Vue from 'vue'; 3 | 4 | LoadingBar.newInstance = properties => { 5 | const _props = properties || {}; 6 | 7 | const Instance = new Vue({ 8 | data: _props, 9 | render (h) { 10 | return h(LoadingBar, { 11 | props: _props 12 | }); 13 | } 14 | }); 15 | 16 | const component = Instance.$mount(); 17 | document.body.appendChild(component.$el); 18 | const loading_bar = Instance.$children[0]; 19 | 20 | return { 21 | update (options) { 22 | if ('percent' in options) { 23 | loading_bar.percent = options.percent; 24 | } 25 | if (options.status) { 26 | loading_bar.status = options.status; 27 | } 28 | if ('show' in options) { 29 | loading_bar.show = options.show; 30 | } 31 | }, 32 | component: loading_bar, 33 | destroy () { 34 | document.body.removeChild(document.getElementsByClassName('ivu-loading-bar')[0]); 35 | } 36 | }; 37 | }; 38 | 39 | export default LoadingBar; 40 | -------------------------------------------------------------------------------- /src/utils/dom.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | const isServer = Vue.prototype.$isServer; 3 | 4 | /* istanbul ignore next */ 5 | export const on = (function() { 6 | if (!isServer && document.addEventListener) { 7 | return function(element, event, handler) { 8 | if (element && event && handler) { 9 | element.addEventListener(event, handler, false); 10 | } 11 | }; 12 | } else { 13 | return function(element, event, handler) { 14 | if (element && event && handler) { 15 | element.attachEvent('on' + event, handler); 16 | } 17 | }; 18 | } 19 | })(); 20 | 21 | /* istanbul ignore next */ 22 | export const off = (function() { 23 | if (!isServer && document.removeEventListener) { 24 | return function(element, event, handler) { 25 | if (element && event) { 26 | element.removeEventListener(event, handler, false); 27 | } 28 | }; 29 | } else { 30 | return function(element, event, handler) { 31 | if (element && event) { 32 | element.detachEvent('on' + event, handler); 33 | } 34 | }; 35 | } 36 | })(); -------------------------------------------------------------------------------- /src/mixins/emitter.js: -------------------------------------------------------------------------------- 1 | function broadcast(componentName, eventName, params) { 2 | this.$children.forEach(child => { 3 | const name = child.$options.name; 4 | 5 | if (name === componentName) { 6 | child.$emit.apply(child, [eventName].concat(params)); 7 | } else { 8 | // todo 如果 params 是空数组,接收到的会是 undefined 9 | broadcast.apply(child, [componentName, eventName].concat([params])); 10 | } 11 | }); 12 | } 13 | export default { 14 | methods: { 15 | dispatch(componentName, eventName, params) { 16 | let parent = this.$parent || this.$root; 17 | let name = parent.$options.name; 18 | 19 | while (parent && (!name || name !== componentName)) { 20 | parent = parent.$parent; 21 | 22 | if (parent) { 23 | name = parent.$options.name; 24 | } 25 | } 26 | if (parent) { 27 | parent.$emit.apply(parent, [eventName].concat(params)); 28 | } 29 | }, 30 | broadcast(componentName, eventName, params) { 31 | broadcast.call(this, componentName, eventName, params); 32 | } 33 | } 34 | }; -------------------------------------------------------------------------------- /src/components/cascader/casitem.vue: -------------------------------------------------------------------------------- 1 | 8 | 35 | -------------------------------------------------------------------------------- /src/components/button/button-group.vue: -------------------------------------------------------------------------------- 1 | 6 | 43 | -------------------------------------------------------------------------------- /examples/routers/message.vue: -------------------------------------------------------------------------------- 1 | 10 | 39 | -------------------------------------------------------------------------------- /src/styles/common/article.less: -------------------------------------------------------------------------------- 1 | .ivu-article { 2 | h1{ 3 | font-size: 26px; 4 | font-weight: normal; 5 | } 6 | h2{ 7 | font-size: 20px; 8 | font-weight: normal; 9 | } 10 | h3{ 11 | font-size: 16px; 12 | font-weight: normal; 13 | } 14 | h4{ 15 | font-size: 14px; 16 | font-weight: normal; 17 | } 18 | h5{ 19 | font-size: 12px; 20 | font-weight: normal; 21 | } 22 | h6{ 23 | font-size: 12px; 24 | font-weight: normal; 25 | } 26 | 27 | blockquote{ 28 | padding: 5px 5px 3px 10px; 29 | line-height: 1.5; 30 | border-left: 4px solid #ddd; 31 | margin-bottom: 20px; 32 | color: #666; 33 | font-size: 14px; 34 | } 35 | 36 | ul:not([class^="ivu-"]){ 37 | padding-left: 40px; 38 | list-style-type: disc; 39 | } 40 | li:not([class^="ivu-"]){ 41 | margin-bottom: 5px; 42 | font-size: 14px; 43 | } 44 | ul ul:not([class^="ivu-"]), ol ul:not([class^="ivu-"]){ 45 | list-style-type: circle; 46 | } 47 | 48 | p{ 49 | margin: 5px; 50 | font-size: 14px; 51 | } 52 | 53 | a[target="_blank"]:after{ 54 | content: "\F220"; 55 | font-family: Ionicons; 56 | color: #aaa; 57 | margin-left: 3px; 58 | } 59 | } -------------------------------------------------------------------------------- /src/locale/format.js: -------------------------------------------------------------------------------- 1 | /** 2 | * String format template 3 | * - Inspired: 4 | * https://github.com/Matt-Esch/string-template/index.js 5 | */ 6 | 7 | const RE_NARGS = /(%|)\{([0-9a-zA-Z_]+)\}/g; 8 | 9 | export default function() { 10 | // const { hasOwn } = Vue.util; 11 | function hasOwn (obj, key) { 12 | return Object.prototype.hasOwnProperty.call(obj, key); 13 | } 14 | 15 | /** 16 | * template 17 | * 18 | * @param {String} string 19 | * @param {Array} ...args 20 | * @return {String} 21 | */ 22 | 23 | function template(string, ...args) { 24 | if (args.length === 1 && typeof args[0] === 'object') { 25 | args = args[0]; 26 | } 27 | 28 | if (!args || !args.hasOwnProperty) { 29 | args = {}; 30 | } 31 | 32 | return string.replace(RE_NARGS, (match, prefix, i, index) => { 33 | let result; 34 | 35 | if (string[index - 1] === '{' && 36 | string[index + match.length] === '}') { 37 | return i; 38 | } else { 39 | result = hasOwn(args, i) ? args[i] : null; 40 | if (result === null || result === undefined) { 41 | return ''; 42 | } 43 | 44 | return result; 45 | } 46 | }); 47 | } 48 | 49 | return template; 50 | } 51 | -------------------------------------------------------------------------------- /src/components/transfer/search.vue: -------------------------------------------------------------------------------- 1 | 11 | 49 | -------------------------------------------------------------------------------- /src/styles/components/collapse.less: -------------------------------------------------------------------------------- 1 | @collapse-prefix-cls: ~"@{css-prefix}collapse"; 2 | 3 | .@{collapse-prefix-cls}{ 4 | background-color: @background-color-base; 5 | border-radius: 3px; 6 | border: 1px solid @border-color-base; 7 | 8 | & > &-item{ 9 | border-top: 1px solid @border-color-base; 10 | &:first-child { 11 | border-top: 0; 12 | } 13 | 14 | > .@{collapse-prefix-cls}-header{ 15 | height: 38px; 16 | line-height: 38px; 17 | padding-left: 32px; 18 | color: #666; 19 | cursor: pointer; 20 | position: relative; 21 | 22 | > i{ 23 | transition: transform @transition-time @ease-in-out; 24 | } 25 | } 26 | } 27 | & > &-item&-item-active > &-header > i{ 28 | transform: rotate(90deg); 29 | } 30 | 31 | &-content{ 32 | //display: none; 33 | overflow: hidden; 34 | color: @text-color; 35 | padding: 0 16px; 36 | background-color: #fff; 37 | 38 | & > &-box { 39 | padding-top: 16px; 40 | padding-bottom: 16px; 41 | } 42 | } 43 | &-item-active > &-content{ 44 | //display: block; 45 | } 46 | &-item:last-child { 47 | > .@{collapse-prefix-cls}-content { 48 | border-radius: 0 0 3px 3px; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/routers/alert.vue: -------------------------------------------------------------------------------- 1 | 34 | 40 | -------------------------------------------------------------------------------- /examples/routers/carousel.vue: -------------------------------------------------------------------------------- 1 | 23 | 40 | -------------------------------------------------------------------------------- /examples/routers/circle.vue: -------------------------------------------------------------------------------- 1 | 35 | 53 | 58 | -------------------------------------------------------------------------------- /examples/routers/progress.vue: -------------------------------------------------------------------------------- 1 | 20 | 43 | -------------------------------------------------------------------------------- /src/styles/components/badge.less: -------------------------------------------------------------------------------- 1 | @badge-prefix-cls: ~"@{css-prefix}badge"; 2 | 3 | .@{badge-prefix-cls} { 4 | position: relative; 5 | display: inline-block; 6 | line-height: 1; 7 | vertical-align: middle; 8 | 9 | &-count { 10 | position: absolute; 11 | transform: translateX(50%); 12 | top: -10px; 13 | right: 0; 14 | height: 20px; 15 | border-radius: 10px; 16 | min-width: 20px; 17 | background: @error-color; 18 | border: 1px solid transparent; 19 | color: #fff; 20 | line-height: 18px; 21 | text-align: center; 22 | padding: 0 6px; 23 | font-size: 12px; 24 | white-space: nowrap; 25 | transform-origin: -10% center; 26 | z-index: 10; 27 | box-shadow: 0 0 0 1px #fff; 28 | 29 | a, 30 | a:hover { 31 | color: #fff; 32 | } 33 | 34 | &-alone { 35 | top: auto; 36 | display: block; 37 | position: relative; 38 | transform: translateX(0); 39 | } 40 | } 41 | 42 | &-dot { 43 | position: absolute; 44 | transform: translateX(-50%); 45 | transform-origin: 0 center; 46 | top: -4px; 47 | right: -8px; 48 | height: 8px; 49 | width: 8px; 50 | border-radius: 100%; 51 | background: @error-color; 52 | z-index: 10; 53 | box-shadow: 0 0 0 1px #fff; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/locale/index.js: -------------------------------------------------------------------------------- 1 | // https://github.com/ElemeFE/element/blob/dev/src/locale/index.js 2 | 3 | import defaultLang from './lang/zh-CN'; 4 | import Vue from 'vue'; 5 | import deepmerge from 'deepmerge'; 6 | import Format from './format'; 7 | 8 | const format = Format(Vue); 9 | let lang = defaultLang; 10 | let merged = false; 11 | let i18nHandler = function() { 12 | const vuei18n = Object.getPrototypeOf(this || Vue).$t; 13 | if (typeof vuei18n === 'function') { 14 | if (!merged) { 15 | merged = true; 16 | Vue.locale( 17 | Vue.config.lang, 18 | deepmerge(lang, Vue.locale(Vue.config.lang) || {}, { clone: true }) 19 | ); 20 | } 21 | return vuei18n.apply(this, arguments); 22 | } 23 | }; 24 | 25 | export const t = function(path, options) { 26 | let value = i18nHandler.apply(this, arguments); 27 | if (value !== null && value !== undefined) return value; 28 | 29 | const array = path.split('.'); 30 | let current = lang; 31 | 32 | for (let i = 0, j = array.length; i < j; i++) { 33 | const property = array[i]; 34 | value = current[property]; 35 | if (i === j - 1) return format(value, options); 36 | if (!value) return ''; 37 | current = value; 38 | } 39 | return ''; 40 | }; 41 | 42 | export const use = function(l) { 43 | lang = l || lang; 44 | }; 45 | 46 | export const i18n = function(fn) { 47 | i18nHandler = fn || i18nHandler; 48 | }; 49 | 50 | export default { use, t, i18n }; -------------------------------------------------------------------------------- /examples/routers/transfer.vue: -------------------------------------------------------------------------------- 1 | 8 | 46 | -------------------------------------------------------------------------------- /src/styles/components/spin.less: -------------------------------------------------------------------------------- 1 | @spin-prefix-cls: ~"@{css-prefix}spin"; 2 | @spin-dot-size-small: 12px; 3 | @spin-dot-size: 20px; 4 | @spin-dot-size-large: 32px; 5 | 6 | .@{spin-prefix-cls} { 7 | color: @primary-color; 8 | vertical-align: middle; 9 | text-align: center; 10 | 11 | &-dot { 12 | position: relative; 13 | display: block; 14 | border-radius: 50%; 15 | background-color: @primary-color; 16 | .square(@spin-dot-size); 17 | animation: ani-spin-bounce 1s 0s ease-in-out infinite; 18 | } 19 | 20 | &-large &-dot { 21 | .square(@spin-dot-size-large); 22 | } 23 | 24 | &-small &-dot { 25 | .square(@spin-dot-size-small); 26 | } 27 | 28 | &-fix { 29 | position: absolute; 30 | top: 0; 31 | bottom: 0; 32 | left: 0; 33 | right: 0; 34 | z-index: @zindex-spin; 35 | display: table; 36 | .square(100%); 37 | background-color: #fff; 38 | } 39 | 40 | &-fix &-main { 41 | display: table-cell; 42 | vertical-align: middle; 43 | .square(inherit); 44 | } 45 | 46 | &-fix &-dot { 47 | display: inline-block; 48 | } 49 | 50 | &-text, 51 | &-show-text &-dot { 52 | display: none; 53 | } 54 | 55 | &-show-text &-text { 56 | display: block; 57 | } 58 | } 59 | 60 | @keyframes ani-spin-bounce { 61 | 0% { 62 | transform: scale(0); 63 | } 64 | 65 | 100% { 66 | transform: scale(1); 67 | opacity: 0; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/styles/components/tree.less: -------------------------------------------------------------------------------- 1 | @tree-prefix-cls: ~"@{css-prefix}tree"; 2 | 3 | .@{tree-prefix-cls} { 4 | ul{ 5 | list-style: none; 6 | margin: 0; 7 | padding: 0; 8 | font-size: @font-size-small; 9 | li{ 10 | list-style: none; 11 | margin: 8px 0; 12 | padding: 0; 13 | white-space: nowrap; 14 | outline: none; 15 | } 16 | } 17 | li{ 18 | ul{ 19 | margin: 0; 20 | padding: 0 0 0 18px; 21 | } 22 | } 23 | &-title { 24 | display: inline-block; 25 | margin: 0; 26 | padding: 0 4px; 27 | border-radius: @btn-border-radius-small; 28 | cursor: pointer; 29 | vertical-align: top; 30 | color: @text-color; 31 | transition: all @transition-time @ease-in-out; 32 | &:hover { 33 | background-color: tint(@primary-color, 90%); 34 | } 35 | &-selected, &-selected:hover{ 36 | background-color: tint(@primary-color, 80%); 37 | } 38 | } 39 | &-arrow{ 40 | cursor: pointer; 41 | i{ 42 | transition: all @transition-time @ease-in-out; 43 | } 44 | &-open{ 45 | i { 46 | transform: rotate(90deg); 47 | } 48 | } 49 | &-hidden{ 50 | cursor: auto; 51 | i{ 52 | display: none; 53 | } 54 | } 55 | &-disabled{ 56 | cursor: @cursor-disabled; 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/components/select/option-group.vue: -------------------------------------------------------------------------------- 1 | 9 | 49 | -------------------------------------------------------------------------------- /src/components/tabs/pane.vue: -------------------------------------------------------------------------------- 1 | 4 | 61 | -------------------------------------------------------------------------------- /src/components/modal/index.js: -------------------------------------------------------------------------------- 1 | import Modal from './confirm'; 2 | 3 | let modalInstance; 4 | 5 | function getModalInstance (render = undefined) { 6 | modalInstance = modalInstance || Modal.newInstance({ 7 | closable: false, 8 | maskClosable: false, 9 | footerHide: true, 10 | render: render 11 | }); 12 | 13 | return modalInstance; 14 | } 15 | 16 | function confirm (options) { 17 | const render = ('render' in options) ? options.render : undefined; 18 | let instance = getModalInstance(render); 19 | 20 | options.onRemove = function () { 21 | modalInstance = null; 22 | }; 23 | 24 | instance.show(options); 25 | } 26 | 27 | Modal.info = function (props = {}) { 28 | props.icon = 'info'; 29 | props.showCancel = false; 30 | return confirm(props); 31 | }; 32 | 33 | Modal.success = function (props = {}) { 34 | props.icon = 'success'; 35 | props.showCancel = false; 36 | return confirm(props); 37 | }; 38 | 39 | Modal.warning = function (props = {}) { 40 | props.icon = 'warning'; 41 | props.showCancel = false; 42 | return confirm(props); 43 | }; 44 | 45 | Modal.error = function (props = {}) { 46 | props.icon = 'error'; 47 | props.showCancel = false; 48 | return confirm(props); 49 | }; 50 | 51 | Modal.confirm = function (props = {}) { 52 | props.icon = 'confirm'; 53 | props.showCancel = true; 54 | return confirm(props); 55 | }; 56 | 57 | Modal.remove = function () { 58 | if (!modalInstance) { // at loading status, remove after Cancel 59 | return false; 60 | } 61 | 62 | const instance = getModalInstance(); 63 | 64 | instance.remove(); 65 | }; 66 | 67 | export default Modal; -------------------------------------------------------------------------------- /examples/routers/menu.vue: -------------------------------------------------------------------------------- 1 | 41 | 50 | -------------------------------------------------------------------------------- /CHANGE.md: -------------------------------------------------------------------------------- 1 | ### Button 2 | 可以考虑是否支持 @click,而不用 @click.native 3 | ### Input 4 | 使用 v-model,增加 on-input-change // todo 考虑更名 5 | ### RadioGroup 6 | 使用 v-model 7 | ### Radio 8 | value 改为了 label,使用 v-model,废弃 checked 9 | ### Checkbox 10 | 使用 v-model 11 | ### CheckboxGroup 12 | value 改为了 label,使用 v-model,废弃 checked 13 | ### Switch 14 | 废弃checked, 改为了 value,使用 v-model 15 | ### Badge 16 | class 改为了 className 17 | ### InputNumber 18 | 使用 v-model 19 | ### Progress (名称有警告) 20 | 新增 on-status-change 事件 21 | ### Upload 22 | 父级不能 computed Upload 的 fileList 了 23 | ### Collapse 24 | 废弃 activeKey,使用 v-model,key 是保留的,更名为 name 25 | ### Carousel 26 | 废弃 activeIndex,使用 v-model,v-for="n in slides.length",Vue2的数字循环,是从1开始的 27 | ### Tree 28 | 废弃 data,改为 value,使用 v-model,key 更名为 name,不能再 template 的prop 上使用 this 29 | ### Circle 30 | 改名为 iCircle 31 | ### Tabs 32 | 废弃 activeKey,改用 value,使用 v-model,key 更名为 name 33 | ### popper.js 将 prop: visible 移至 data 里 34 | ### Slider 35 | 支持 v-model 36 | ### Dropdown 37 | DropdownItem key 改为 name, Dropdown 的 visible 要使用 @on-visible-change 捕获,不再 sync 38 | DropdownItem 里,this.$parent.$parent 与1.0 有区别 39 | ### Menu 40 | MenuItem 和 Submenu 的 key 改为了 name 41 | Menu 的 activeKey 改为 activeName,openKeys 改为 openNames 42 | ### Cascader 43 | Caspanel 的 sublist 从 prop -> data 44 | ### Select 45 | model 改为 value,支持 v-model 46 | ### Page 47 | class 改为 className 48 | ### DatePicker 49 | 使用 v-model 50 | ### LoadingBar 51 | 部分 prop 移至 data 52 | ### Modal 53 | visible 改为 value,使用 v-model,style 改为 styles,$Modal 的关闭有改动,建议后面在纯 html 模式下测试 54 | ### Table 55 | i-table 改为 Table 56 | ### Message 57 | notice.vue 的 key 改为了 name,style 改为 styles 58 | notification.vue 的 key 改为了 name,style 改为 styles 59 | ## Popper 60 | 移除了 visible,使用 value 受控,可能涉及到的组件:Poptip、Tooltip -------------------------------------------------------------------------------- /src/styles/common/base.less: -------------------------------------------------------------------------------- 1 | @import "normalize"; 2 | 3 | * { 4 | box-sizing: border-box; 5 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 6 | } 7 | 8 | *:before, 9 | *:after { 10 | box-sizing: border-box; 11 | } 12 | 13 | body { 14 | font-family: @font-family; 15 | font-size: @font-size-small; 16 | line-height: @line-height-base; 17 | color: @text-color; 18 | background-color: @body-background; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; 21 | } 22 | 23 | body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { 24 | margin: 0; 25 | padding: 0; 26 | } 27 | 28 | button, input, select, textarea { 29 | font-family: inherit; 30 | font-size: inherit; 31 | line-height: inherit; 32 | } 33 | 34 | ul, 35 | ol { 36 | list-style: none; 37 | } 38 | 39 | input::-ms-clear, input::-ms-reveal { 40 | display: none; 41 | } 42 | 43 | a { 44 | color: @link-color; 45 | background: transparent; 46 | text-decoration: none; 47 | outline: none; 48 | cursor: pointer; 49 | transition: color @transition-time ease; 50 | 51 | &:hover { 52 | color: @link-hover-color; 53 | } 54 | 55 | &:active { 56 | color: @link-active-color; 57 | } 58 | 59 | &:active, 60 | &:hover { 61 | outline: 0; 62 | text-decoration: none; 63 | } 64 | 65 | &[disabled] { 66 | color: #ccc; 67 | cursor: @cursor-disabled; 68 | pointer-events: none; 69 | } 70 | } 71 | 72 | code, 73 | kbd, 74 | pre, 75 | samp { 76 | font-family: @code-family; 77 | } 78 | -------------------------------------------------------------------------------- /src/utils/csv.js: -------------------------------------------------------------------------------- 1 | // https://github.com/Terminux/react-csv-downloader/blob/master/src/lib/csv.js 2 | 3 | const newLine = '\r\n'; 4 | 5 | export default function csv(columns, datas, separator = ',', noHeader = false) { 6 | let columnOrder; 7 | const content = []; 8 | const column = []; 9 | 10 | if (columns) { 11 | columnOrder = columns.map(v => { 12 | if (typeof v === 'string') { 13 | return v; 14 | } 15 | if (!noHeader) { 16 | column.push((typeof v.title !== 'undefined') ? v.title : v.key); 17 | } 18 | return v.key; 19 | }); 20 | if (column.length > 0) { 21 | content.push(column.join(separator)); 22 | } 23 | } else { 24 | columnOrder = []; 25 | datas.forEach(v => { 26 | if (!Array.isArray(v)) { 27 | columnOrder = columnOrder.concat(Object.keys(v)); 28 | } 29 | }); 30 | if (columnOrder.length > 0) { 31 | columnOrder = columnOrder.filter((value, index, self) => self.indexOf(value) === index); 32 | 33 | if (!noHeader) { 34 | content.push(columnOrder.join(separator)); 35 | } 36 | } 37 | } 38 | 39 | if (Array.isArray(datas)) { 40 | datas.map(v => { 41 | if (Array.isArray(v)) { 42 | return v; 43 | } 44 | return columnOrder.map(k => { 45 | if (typeof v[k] !== 'undefined') { 46 | return v[k]; 47 | } 48 | return ''; 49 | }); 50 | }).forEach(v => { 51 | content.push(v.join(separator)); 52 | }); 53 | } 54 | return content.join(newLine); 55 | } -------------------------------------------------------------------------------- /src/components/date-picker/base/confirm.vue: -------------------------------------------------------------------------------- 1 | 11 | 51 | -------------------------------------------------------------------------------- /src/components/breadcrumb/breadcrumb-item.vue: -------------------------------------------------------------------------------- 1 | 15 | 58 | -------------------------------------------------------------------------------- /src/components/spin/spin.vue: -------------------------------------------------------------------------------- 1 | 11 | 60 | -------------------------------------------------------------------------------- /examples/routers/notice.vue: -------------------------------------------------------------------------------- 1 | 16 | 49 | -------------------------------------------------------------------------------- /src/styles/components/rate.less: -------------------------------------------------------------------------------- 1 | @rate-prefix-cls: ~"@{css-prefix}rate"; 2 | 3 | .@{rate-prefix-cls} { 4 | display: inline-block; 5 | margin: 0; 6 | padding: 0; 7 | font-size: 20px; 8 | vertical-align: middle; 9 | font-weight: normal; 10 | font-style: normal; 11 | 12 | &-disabled &-star { 13 | &:before, 14 | &-content:before { 15 | cursor: default; 16 | } 17 | &:hover { 18 | transform: scale(1); 19 | } 20 | } 21 | 22 | &-star { 23 | display: inline-block; 24 | margin: 0; 25 | padding: 0; 26 | margin-right: 8px; 27 | position: relative; 28 | font-family: 'Ionicons'; 29 | transition: all 0.3s ease; 30 | 31 | &:hover { 32 | transform: scale(1.1); 33 | } 34 | 35 | &:before, 36 | &-content:before { 37 | color: #e9e9e9; 38 | cursor: pointer; 39 | content: "\F4B3"; 40 | transition: all @transition-time @ease-in-out; 41 | display: block; 42 | } 43 | 44 | &-content { 45 | position: absolute; 46 | left: 0; 47 | top: 0; 48 | width: 50%; 49 | height: 100%; 50 | overflow: hidden; 51 | &:before { 52 | color: transparent; 53 | } 54 | } 55 | 56 | &-half &-content:before, 57 | &-full:before { 58 | color: @rate-star-color; 59 | } 60 | 61 | &-half:hover &-content:before, 62 | &-full:hover:before { 63 | color: tint(@rate-star-color, 20%); 64 | } 65 | } 66 | &-text { 67 | margin-left: 8px; 68 | vertical-align: middle; 69 | display: inline-block; 70 | font-size: @font-size-small; 71 | } 72 | } -------------------------------------------------------------------------------- /src/styles/components/message.less: -------------------------------------------------------------------------------- 1 | @message-prefix-cls: ~"@{css-prefix}message"; 2 | @icon-prefix-cls: ~"@{css-prefix}icon"; 3 | 4 | .@{message-prefix-cls} { 5 | font-size: @font-size-small; 6 | position: fixed; 7 | z-index: @zindex-message; 8 | width: 100%; 9 | top: 16px; 10 | left: 0; 11 | 12 | &-notice { 13 | width: auto; 14 | vertical-align: middle; 15 | position: absolute; 16 | left: 50%; 17 | 18 | &-close { 19 | position: absolute; 20 | right: 4px; 21 | top: 9px; 22 | color: #999; 23 | outline: none; 24 | 25 | i.@{icon-prefix-cls}{ 26 | .close-base(-3px); 27 | } 28 | } 29 | } 30 | 31 | &-notice-content { 32 | position: relative; 33 | right: 50%; 34 | padding: 8px 16px; 35 | //border: 1px solid @border-color-split; 36 | border-radius: @border-radius-small; 37 | box-shadow: @shadow-base; 38 | background: #fff; 39 | display: block; 40 | &-text{ 41 | display: inline-block; 42 | } 43 | } 44 | 45 | &-notice-closable{ 46 | .@{message-prefix-cls}-notice-content-text{ 47 | padding-right: 32px; 48 | } 49 | } 50 | 51 | &-success .@{icon-prefix-cls} { 52 | color: @success-color; 53 | } 54 | 55 | &-error .@{icon-prefix-cls} { 56 | color: @error-color; 57 | } 58 | 59 | &-warning .@{icon-prefix-cls} { 60 | color: @warning-color; 61 | } 62 | 63 | &-info .@{icon-prefix-cls}, 64 | &-loading .@{icon-prefix-cls} { 65 | color: @primary-color; 66 | } 67 | 68 | .@{icon-prefix-cls} { 69 | margin-right: 8px; 70 | font-size: 14px; 71 | top: 1px; 72 | position: relative; 73 | } 74 | } -------------------------------------------------------------------------------- /src/styles/components/form.less: -------------------------------------------------------------------------------- 1 | @form-prefix-cls: ~"@{css-prefix}form"; 2 | @form-item-prefix-cls: ~"@{form-prefix-cls}-item"; 3 | 4 | .@{form-prefix-cls} { 5 | .@{form-item-prefix-cls}-label { 6 | text-align: right; 7 | vertical-align: middle; 8 | float: left; 9 | font-size: @font-size-small; 10 | color: @text-color; 11 | line-height: 1; 12 | padding: 10px 12px 10px 0; 13 | box-sizing: border-box; 14 | } 15 | &-label-left .@{form-item-prefix-cls}-label { 16 | text-align: left; 17 | } 18 | &-label-top .@{form-item-prefix-cls}-label { 19 | float: none; 20 | display: inline-block; 21 | padding: 0 0 10px 0; 22 | } 23 | &-inline{ 24 | .@{form-item-prefix-cls} { 25 | display: inline-block; 26 | margin-right: 10px; 27 | vertical-align: top; 28 | } 29 | } 30 | } 31 | 32 | .@{form-item-prefix-cls} { 33 | margin-bottom: 24px; 34 | vertical-align: top; 35 | .clearfix(); 36 | &-content { 37 | position: relative; 38 | line-height: 32px; 39 | font-size: @font-size-small; 40 | } 41 | & & { 42 | margin-bottom: 0; 43 | } 44 | & & &-content { 45 | margin-left: 0!important; 46 | } 47 | 48 | &-error-tip{ 49 | position: absolute; 50 | top: 100%; 51 | left: 0; 52 | line-height: 1; 53 | padding-top: 6px; 54 | color: @error-color; 55 | } 56 | 57 | &-required { 58 | .@{form-item-prefix-cls}-label:before { 59 | content: '*'; 60 | display: inline-block; 61 | margin-right: 4px; 62 | line-height: 1; 63 | font-family: SimSun; 64 | font-size: @font-size-small; 65 | color: @error-color; 66 | } 67 | } 68 | &-error { 69 | // todo 70 | } 71 | } -------------------------------------------------------------------------------- /examples/routers/more.vue: -------------------------------------------------------------------------------- 1 | 17 | 40 | 68 | -------------------------------------------------------------------------------- /src/components/collapse/panel.vue: -------------------------------------------------------------------------------- 1 | 14 | 62 | -------------------------------------------------------------------------------- /src/styles/common/layout.less: -------------------------------------------------------------------------------- 1 | .@{row-prefix-cls} { 2 | .make-row(); 3 | display: block; 4 | 5 | &-flex { 6 | display: flex; 7 | flex-direction: row; 8 | flex-wrap: wrap; 9 | 10 | &:before, 11 | &:after { 12 | display: flex; 13 | } 14 | // x轴原点 15 | &-start { 16 | justify-content: flex-start; 17 | } 18 | // x轴居中 19 | &-center { 20 | justify-content: center; 21 | } 22 | // x轴反方向 23 | &-end { 24 | justify-content: flex-end; 25 | } 26 | // x轴平分 27 | &-space-between { 28 | justify-content: space-between; 29 | } 30 | // x轴有间隔地平分 31 | &-space-around { 32 | justify-content: space-around; 33 | } 34 | // 顶部对齐 35 | &-top { 36 | align-items: flex-start; 37 | } 38 | // 居中对齐 39 | &-middle { 40 | align-items: center; 41 | } 42 | // 底部对齐 43 | &-bottom { 44 | align-items: flex-end; 45 | } 46 | }; 47 | } 48 | 49 | .@{col-prefix-cls} { 50 | position: relative; 51 | display: block; 52 | } 53 | 54 | .make-grid(); 55 | 56 | // Extra small grid 57 | // 58 | // Columns, offsets, pushes, and pulls for extra small devices like 59 | // smartphones. 60 | 61 | .make-grid(-xs); 62 | 63 | // Small grid 64 | // 65 | // Columns, offsets, pushes, and pulls for the small device range, from phones 66 | // to tablets. 67 | 68 | @media (min-width: @screen-sm-min) { 69 | .make-grid(-sm); 70 | } 71 | 72 | 73 | // Medium grid 74 | // 75 | // Columns, offsets, pushes, and pulls for the desktop device range. 76 | 77 | @media (min-width: @screen-md-min) { 78 | .make-grid(-md); 79 | } 80 | 81 | 82 | // Large grid 83 | // 84 | // Columns, offsets, pushes, and pulls for the large desktop device range. 85 | 86 | @media (min-width: @screen-lg-min) { 87 | .make-grid(-lg); 88 | } 89 | -------------------------------------------------------------------------------- /src/components/dropdown/dropdown-item.vue: -------------------------------------------------------------------------------- 1 | 4 | 59 | -------------------------------------------------------------------------------- /examples/routers/timeline.vue: -------------------------------------------------------------------------------- 1 | 10 | 57 | 62 | -------------------------------------------------------------------------------- /src/components/checkbox/checkbox-group.vue: -------------------------------------------------------------------------------- 1 | 6 | 66 | -------------------------------------------------------------------------------- /src/styles/mixins/select.less: -------------------------------------------------------------------------------- 1 | .select-item(@size-class, @item-class) { 2 | .@{item-class} { 3 | margin: 0; 4 | line-height: normal; 5 | padding: 7px 16px; 6 | clear: both; 7 | color: @text-color; 8 | font-size: @font-size-small !important; 9 | white-space: nowrap; 10 | list-style: none; 11 | cursor: pointer; 12 | transition: background @transition-time @ease-in-out; 13 | 14 | &:hover{ 15 | background: @background-color-select-hover; 16 | } 17 | 18 | &-focus { 19 | background: @background-color-select-hover; 20 | } 21 | 22 | &-disabled { 23 | color: @btn-disable-color; 24 | cursor: @cursor-disabled; 25 | 26 | &:hover { 27 | color: @btn-disable-color; 28 | background-color: #fff; 29 | cursor: @cursor-disabled; 30 | } 31 | } 32 | 33 | &-selected ,&-selected:hover{ 34 | color: #fff; 35 | background: @selected-color; 36 | } 37 | 38 | &-selected&-focus { 39 | background: shade(@selected-color, 10%); 40 | } 41 | 42 | &-divided{ 43 | margin-top: 5px; 44 | border-top: 1px solid @border-color-split; 45 | &:before{ 46 | content: ''; 47 | height: 5px; 48 | display: block; 49 | margin: 0 -16px; 50 | background-color: #fff; 51 | position: relative; 52 | top: -7px; 53 | } 54 | } 55 | } 56 | 57 | .@{size-class}-large .@{item-class} { 58 | padding: 7px 16px 8px; 59 | font-size: @font-size-base !important; 60 | } 61 | // http://browserhacks.com/ 62 | // https://bugzilla.mozilla.org/show_bug.cgi?id=488725 63 | // fixed #1224 #1143 #1127 64 | @-moz-document url-prefix() { 65 | .@{item-class} { 66 | white-space: normal; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/styles/mixins/layout.less: -------------------------------------------------------------------------------- 1 | @row-prefix-cls: ~"@{css-prefix}row"; 2 | @col-prefix-cls: ~"@{css-prefix}col"; 3 | 4 | .make-row(@gutter: @grid-gutter-width) { 5 | position: relative; 6 | margin-left: (@gutter / -2); 7 | margin-right: (@gutter / -2); 8 | height: auto; 9 | .clearfix; 10 | } 11 | 12 | .float-grid-columns(@class) { 13 | .col(@index) { // initial 14 | @item: ~".@{col-prefix-cls}-span@{class}-@{index}"; 15 | .col((@index + 1), @item); 16 | } 17 | .col(@index, @list) when (@index =< @grid-columns) { // general 18 | @item: ~".@{col-prefix-cls}-span@{class}-@{index}"; 19 | .col((@index + 1), ~"@{list}, @{item}"); 20 | } 21 | .col(@index, @list) when (@index > @grid-columns) { // terminal 22 | @{list} { 23 | float: left; 24 | flex: 0 0 auto; 25 | } 26 | } 27 | .col(1); // kickstart it 28 | } 29 | 30 | .loop-grid-columns(@index, @class) when (@index > 0) { 31 | .@{col-prefix-cls}-span@{class}-@{index} { 32 | display: block; 33 | width: percentage((@index / @grid-columns)); 34 | } 35 | .@{col-prefix-cls}@{class}-push-@{index} { 36 | left: percentage((@index / @grid-columns)); 37 | } 38 | .@{col-prefix-cls}@{class}-pull-@{index} { 39 | right: percentage((@index / @grid-columns)); 40 | } 41 | .@{col-prefix-cls}@{class}-offset-@{index} { 42 | margin-left: percentage((@index / @grid-columns)); 43 | } 44 | .@{col-prefix-cls}@{class}-order-@{index} { 45 | order: @index; 46 | } 47 | .loop-grid-columns((@index - 1), @class); 48 | } 49 | 50 | .loop-grid-columns(@index, @class) when (@index = 0) { 51 | .@{col-prefix-cls}-span@{class}-@{index} { 52 | display: none; 53 | } 54 | .@{col-prefix-cls}@{class}-push-@{index} { 55 | left: auto; 56 | } 57 | .@{col-prefix-cls}@{class}-pull-@{index} { 58 | right: auto; 59 | } 60 | } 61 | 62 | .make-grid(@class: ~'') { 63 | .float-grid-columns(@class); 64 | .loop-grid-columns(@grid-columns, @class); 65 | } -------------------------------------------------------------------------------- /src/components/table/mixin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | methods: { 3 | alignCls (column, row = {}) { 4 | let cellClassName = ''; 5 | if (row.cellClassName && column.key && row.cellClassName[column.key]) { 6 | cellClassName = row.cellClassName[column.key]; 7 | } 8 | return [ 9 | { 10 | [`${cellClassName}`]: cellClassName, // cell className 11 | [`${column.className}`]: column.className, // column className 12 | [`${this.prefixCls}-column-${column.align}`]: column.align, 13 | [`${this.prefixCls}-hidden`]: (this.fixed === 'left' && column.fixed !== 'left') || (this.fixed === 'right' && column.fixed !== 'right') || (!this.fixed && column.fixed && (column.fixed === 'left' || column.fixed === 'right')) 14 | } 15 | ]; 16 | }, 17 | isPopperShow (column) { 18 | return column.filters && ((!this.fixed && !column.fixed) || (this.fixed === 'left' && column.fixed === 'left') || (this.fixed === 'right' && column.fixed === 'right')); 19 | }, 20 | setCellWidth (column, index, top) { 21 | let width = ''; 22 | if (column.width) { 23 | width = column.width; 24 | } else if (this.columnsWidth[column._index]) { 25 | width = this.columnsWidth[column._index].width; 26 | } 27 | // when browser has scrollBar,set a width to resolve scroll position bug 28 | if (this.columns.length === index + 1 && top && this.$parent.bodyHeight !== 0) { 29 | width += this.$parent.scrollBarWidth; 30 | } 31 | // when fixed type,reset first right fixed column's width 32 | if (this.fixed === 'right') { 33 | const firstFixedIndex = this.columns.findIndex((col) => col.fixed === 'right'); 34 | if (firstFixedIndex === index) width += this.$parent.scrollBarWidth; 35 | } 36 | if (width === '0') width = ''; 37 | return width; 38 | } 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /examples/routers/tree.vue: -------------------------------------------------------------------------------- 1 | 4 | 60 | -------------------------------------------------------------------------------- /src/components/timeline/timeline-item.vue: -------------------------------------------------------------------------------- 1 | 10 | 67 | -------------------------------------------------------------------------------- /src/styles/components/input.less: -------------------------------------------------------------------------------- 1 | @input-prefix-cls: ~"@{css-prefix}input"; 2 | 3 | .@{input-prefix-cls} { 4 | .input; 5 | &-wrapper{ 6 | display: inline-block; 7 | width: 100%; 8 | position: relative; 9 | vertical-align: middle; 10 | } 11 | &-icon { 12 | width: 32px; 13 | height: @input-height-base; 14 | line-height: @input-height-base; 15 | font-size: 16px; 16 | text-align: center; 17 | color: @subsidiary-color; 18 | position: absolute; 19 | right: 0; 20 | z-index: 3; 21 | } 22 | &-hide-icon &-icon{ 23 | display: none; 24 | } 25 | &-icon-validate{ 26 | display: none; 27 | } 28 | 29 | &-icon-normal + &{ 30 | padding-right: 32px; 31 | } 32 | // #554 33 | &-hide-icon &-icon-normal + &{ 34 | padding-right: @input-padding-horizontal; 35 | } 36 | 37 | &-wrapper-large &-icon{ 38 | font-size: 18px; 39 | height: @input-height-large; 40 | line-height: @input-height-large; 41 | } 42 | &-wrapper-small &-icon{ 43 | width: 24px; 44 | font-size: 14px; 45 | height: @input-height-small; 46 | line-height: @input-height-small; 47 | 48 | //+ .@{input-prefix-cls} { 49 | // padding-right: 24px; 50 | //} 51 | } 52 | } 53 | 54 | .@{input-prefix-cls}-group{ 55 | .input-group(~"@{input-prefix-cls}"); 56 | } 57 | 58 | .@{form-item-prefix-cls}-error{ 59 | .@{input-prefix-cls}{ 60 | .input-error; 61 | &-icon{ 62 | color: @error-color; 63 | } 64 | } 65 | .@{input-prefix-cls}-group{ 66 | .input-group-error; 67 | } 68 | .@{transfer-prefix-cls} { 69 | .@{input-prefix-cls} { 70 | .input; 71 | &-icon{ 72 | color: @subsidiary-color; 73 | } 74 | } 75 | } 76 | } 77 | .@{form-item-prefix-cls}-validating{ 78 | .@{input-prefix-cls}{ 79 | &-icon-validate{ 80 | display: inline-block; 81 | } 82 | &-icon + .@{input-prefix-cls}{ 83 | padding-right: 32px; 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /src/components/tag/tag.vue: -------------------------------------------------------------------------------- 1 | 8 | 68 | -------------------------------------------------------------------------------- /src/components/menu/menu-item.vue: -------------------------------------------------------------------------------- 1 | 4 | 68 | -------------------------------------------------------------------------------- /src/components/badge/badge.vue: -------------------------------------------------------------------------------- 1 | 11 | 71 | -------------------------------------------------------------------------------- /src/components/switch/switch.vue: -------------------------------------------------------------------------------- 1 | 9 | 73 | -------------------------------------------------------------------------------- /src/components/upload/ajax.js: -------------------------------------------------------------------------------- 1 | // https://github.com/ElemeFE/element/blob/dev/packages/upload/src/ajax.js 2 | 3 | function getError(action, option, xhr) { 4 | const msg = `fail to post ${action} ${xhr.status}'`; 5 | const err = new Error(msg); 6 | err.status = xhr.status; 7 | err.method = 'post'; 8 | err.url = action; 9 | return err; 10 | } 11 | 12 | function getBody(xhr) { 13 | const text = xhr.responseText || xhr.response; 14 | if (!text) { 15 | return text; 16 | } 17 | 18 | try { 19 | return JSON.parse(text); 20 | } catch (e) { 21 | return text; 22 | } 23 | } 24 | 25 | export default function upload(option) { 26 | if (typeof XMLHttpRequest === 'undefined') { 27 | return; 28 | } 29 | 30 | const xhr = new XMLHttpRequest(); 31 | const action = option.action; 32 | 33 | if (xhr.upload) { 34 | xhr.upload.onprogress = function progress(e) { 35 | if (e.total > 0) { 36 | e.percent = e.loaded / e.total * 100; 37 | } 38 | option.onProgress(e); 39 | }; 40 | } 41 | 42 | const formData = new FormData(); 43 | 44 | if (option.data) { 45 | Object.keys(option.data).map(key => { 46 | formData.append(key, option.data[key]); 47 | }); 48 | } 49 | 50 | formData.append(option.filename, option.file); 51 | 52 | xhr.onerror = function error(e) { 53 | option.onError(e); 54 | }; 55 | 56 | xhr.onload = function onload() { 57 | if (xhr.status < 200 || xhr.status >= 300) { 58 | return option.onError(getError(action, option, xhr), getBody(xhr)); 59 | } 60 | 61 | option.onSuccess(getBody(xhr)); 62 | }; 63 | 64 | xhr.open('post', action, true); 65 | 66 | if (option.withCredentials && 'withCredentials' in xhr) { 67 | xhr.withCredentials = true; 68 | } 69 | 70 | const headers = option.headers || {}; 71 | 72 | // if (headers['X-Requested-With'] !== null) { 73 | // xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 74 | // } 75 | 76 | for (let item in headers) { 77 | if (headers.hasOwnProperty(item) && headers[item] !== null) { 78 | xhr.setRequestHeader(item, headers[item]); 79 | } 80 | } 81 | xhr.send(formData); 82 | } 83 | -------------------------------------------------------------------------------- /test/unit/util.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import iView from '../../src/index'; 3 | 4 | Vue.use(iView); 5 | 6 | let id = 0; 7 | 8 | const createElm = function() { 9 | const elm = document.createElement('div'); 10 | 11 | elm.id = 'app' + ++id; 12 | document.body.appendChild(elm); 13 | 14 | return elm; 15 | }; 16 | 17 | /** 18 | * 回收 vm 19 | * @param {Object} vm 20 | */ 21 | exports.destroyVM = function(vm) { 22 | vm.$el && 23 | vm.$el.parentNode && 24 | vm.$el.parentNode.removeChild(vm.$el); 25 | }; 26 | 27 | /** 28 | * 创建一个 Vue 的实例对象 29 | * @param {Object|String} Compo 组件配置,可直接传 template 30 | * @param {Boolean=false} mounted 是否添加到 DOM 上 31 | * @return {Object} vm 32 | */ 33 | exports.createVue = function(Compo, mounted = false) { 34 | const elm = createElm(); 35 | 36 | if (Object.prototype.toString.call(Compo) === '[object String]') { 37 | Compo = { template: Compo }; 38 | } 39 | return new Vue(Compo).$mount(mounted === false ? null : elm); 40 | }; 41 | 42 | /** 43 | * 创建一个测试组件实例 44 | * @link http://vuejs.org/guide/unit-testing.html#Writing-Testable-Components 45 | * @param {Object} Compo - 组件对象 46 | * @param {Object} propsData - props 数据 47 | * @param {Boolean=false} mounted - 是否添加到 DOM 上 48 | * @return {Object} vm 49 | */ 50 | exports.createTest = function(Compo, propsData = {}, mounted = false) { 51 | if (propsData === true || propsData === false) { 52 | mounted = propsData; 53 | propsData = {}; 54 | } 55 | const elm = createElm(); 56 | const Ctor = Vue.extend(Compo); 57 | return new Ctor({ propsData }).$mount(mounted === false ? null : elm); 58 | }; 59 | 60 | /** 61 | * 触发一个事件 62 | * mouseenter, mouseleave, mouseover, keyup, change, click 等 63 | * @param {Element} elm 64 | * @param {String} name 65 | * @param {*} opts 66 | */ 67 | exports.triggerEvent = function(elm, name, ...opts) { 68 | let eventName; 69 | 70 | if (/^mouse|click/.test(name)) { 71 | eventName = 'MouseEvents'; 72 | } else if (/^key/.test(name)) { 73 | eventName = 'KeyboardEvent'; 74 | } else { 75 | eventName = 'HTMLEvents'; 76 | } 77 | const evt = document.createEvent(eventName); 78 | 79 | evt.initEvent(name, ...opts); 80 | elm.dispatchEvent 81 | ? elm.dispatchEvent(evt) 82 | : elm.fireEvent('on' + name, evt); 83 | 84 | return elm; 85 | }; 86 | -------------------------------------------------------------------------------- /src/styles/components/upload.less: -------------------------------------------------------------------------------- 1 | @upload-prefix-cls: ~"@{css-prefix}upload"; 2 | 3 | .@{upload-prefix-cls} { 4 | input[type="file"]{ 5 | display: none; 6 | } 7 | 8 | &-list{ 9 | margin-top: 8px; 10 | 11 | &-file{ 12 | padding: 4px; 13 | color: @text-color; 14 | border-radius: @border-radius-small; 15 | transition: background-color @transition-time @ease-in-out; 16 | overflow: hidden; 17 | position: relative; 18 | 19 | & > span{ 20 | cursor: pointer; 21 | transition: color @transition-time @ease-in-out; 22 | i{ 23 | display: inline-block; 24 | width: @font-size-small; 25 | height: @font-size-small; 26 | color: @text-color; 27 | text-align: center; 28 | } 29 | } 30 | 31 | &:hover{ 32 | background: @input-disabled-bg; 33 | & > span{ 34 | color: @primary-color; 35 | i{ 36 | color: @text-color; 37 | } 38 | } 39 | .@{upload-prefix-cls}-list-remove{ 40 | opacity: 1; 41 | } 42 | } 43 | } 44 | &-remove{ 45 | opacity: 0; 46 | font-size: 18px; 47 | cursor: pointer; 48 | float: right; 49 | margin-right: 4px; 50 | color: @legend-color; 51 | transition: all @transition-time ease; 52 | &:hover{ 53 | color: #444; 54 | } 55 | } 56 | } 57 | 58 | &-select { 59 | display: inline-block; 60 | } 61 | 62 | &-drag{ 63 | background: #fff; 64 | border: 1px dashed @border-color-base; 65 | border-radius: @border-radius-small; 66 | text-align: center; 67 | cursor: pointer; 68 | position: relative; 69 | overflow: hidden; 70 | transition: border-color @transition-time ease; 71 | 72 | &:hover{ 73 | border: 1px dashed @primary-color; 74 | } 75 | } 76 | &-dragOver{ 77 | border: 2px dashed @primary-color; 78 | } 79 | } -------------------------------------------------------------------------------- /src/locale/lang/vi-VN.js: -------------------------------------------------------------------------------- 1 | export default { 2 | i: { 3 | select: { 4 | placeholder: 'Chọn', 5 | noMatch: 'Không tìm thấy', 6 | loading: 'Đang tải' 7 | }, 8 | table: { 9 | noDataText: 'Không có dữ liệu', 10 | noFilteredDataText: 'Không có dữ liệu lọc', 11 | confirmFilter: 'Xác nhận', 12 | resetFilter: 'Làm lại', 13 | clearFilter: 'Xóa hết' 14 | }, 15 | datepicker: { 16 | selectDate: 'Chọn ngày', 17 | selectTime: 'Chọn giờ', 18 | startTime: 'Ngày bắt đầu', 19 | endTime: 'Ngày kết thúc', 20 | clear: 'Xóa', 21 | ok: 'Đồng ý', 22 | month: '', 23 | month1: 'Tháng 1', 24 | month2: 'Tháng 2', 25 | month3: 'Tháng 3', 26 | month4: 'Tháng 4', 27 | month5: 'Tháng 5', 28 | month6: 'Tháng 6', 29 | month7: 'Tháng 7', 30 | month8: 'Tháng 8', 31 | month9: 'Tháng 9', 32 | month10: 'Tháng 10', 33 | month11: 'Tháng 11', 34 | month12: 'Tháng 12', 35 | year: '', 36 | weeks: { 37 | sun: 'CN', 38 | mon: 'T2', 39 | tue: 'T3', 40 | wed: 'T4', 41 | thu: 'T5', 42 | fri: 'T6', 43 | sat: 'T7' 44 | }, 45 | months: { 46 | m1: 'Th.1', 47 | m2: 'Th.2', 48 | m3: 'Th.3', 49 | m4: 'Th.4', 50 | m5: 'Th.5', 51 | m6: 'Th.6', 52 | m7: 'Th.7', 53 | m8: 'Th.8', 54 | m9: 'Th.9', 55 | m10: 'Th.10', 56 | m11: 'Th.11', 57 | m12: 'Th.12' 58 | } 59 | }, 60 | transfer: { 61 | titles: { 62 | source: 'Nguồn', 63 | target: 'Đích' 64 | }, 65 | filterPlaceholder: 'Nhập từ khóa', 66 | notFoundText: 'Không tìm thấy' 67 | }, 68 | modal: { 69 | okText: 'Đồng ý', 70 | cancelText: 'Hủy bỏ' 71 | }, 72 | poptip: { 73 | okText: 'Đồng ý', 74 | cancelText: 'Hủy bỏ' 75 | }, 76 | page: { 77 | prev: 'Trang trước', 78 | next: 'Trang kế', 79 | total: 'Tổng', 80 | item: 'kết quả', 81 | items: 'kết quả', 82 | prev5: '5 trang trước', 83 | next5: '5 trang kế', 84 | page: '/trang', 85 | goto: 'Tới trang', 86 | p: '' 87 | }, 88 | rate: { 89 | star: 'Sao', 90 | stars: 'Sao' 91 | }, 92 | tree: { 93 | emptyText: 'Không có dữ liệu' 94 | } 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /src/components/grid/row.vue: -------------------------------------------------------------------------------- 1 | 6 | 75 | -------------------------------------------------------------------------------- /src/components/card/card.vue: -------------------------------------------------------------------------------- 1 | 8 | 75 | -------------------------------------------------------------------------------- /src/styles/components/timeline.less: -------------------------------------------------------------------------------- 1 | @timeline-prefix-cls: ~"@{css-prefix}timeline"; 2 | @timeline-color: @border-color-split; 3 | 4 | .@{timeline-prefix-cls} { 5 | list-style: none; 6 | margin: 0; 7 | padding: 0; 8 | 9 | &-item { 10 | margin: 0 !important; 11 | padding: 0 0 12px 0; 12 | list-style: none; 13 | position: relative; 14 | 15 | &-tail { 16 | height: 100%; 17 | border-left: 1px solid @timeline-color; 18 | position: absolute; 19 | left: 6px; 20 | top: 0; 21 | } 22 | 23 | &-pending &-tail { 24 | display: none; 25 | } 26 | 27 | &-head { 28 | width: 13px; 29 | height: 13px; 30 | background-color: #fff; 31 | border-radius: 50%; 32 | border: 1px solid transparent; 33 | position: absolute; 34 | 35 | &-blue { 36 | border-color: @primary-color; 37 | color: @primary-color; 38 | } 39 | &-red { 40 | border-color: @error-color; 41 | color: @error-color; 42 | } 43 | &-green { 44 | border-color: @success-color; 45 | color: @success-color; 46 | } 47 | } 48 | 49 | &-head-custom { 50 | width: 40px; 51 | height: auto; 52 | margin-top: 6px; 53 | padding: 3px 0; 54 | text-align: center; 55 | line-height: 1; 56 | border: 0; 57 | border-radius: 0; 58 | font-size: @font-size-base; 59 | position: absolute; 60 | left: -13px; 61 | transform: translateY(-50%); 62 | } 63 | 64 | &-content { 65 | padding: 1px 1px 10px 24px; 66 | font-size: @font-size-small; 67 | position: relative; 68 | top: -3px; 69 | } 70 | 71 | &:last-child { 72 | .@{timeline-prefix-cls}-item-tail { 73 | display: none; 74 | } 75 | } 76 | } 77 | 78 | &&-pending &-item:nth-last-of-type(2) { 79 | 80 | .@{timeline-prefix-cls}-item-tail { 81 | border-left: 1px dotted @timeline-color; 82 | } 83 | .@{timeline-prefix-cls}-item-content { 84 | min-height: 48px; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /examples/routers/tabs.vue: -------------------------------------------------------------------------------- 1 | 21 | 71 | -------------------------------------------------------------------------------- /src/components/date-picker/util.js: -------------------------------------------------------------------------------- 1 | import dateUtil from '../../utils/date'; 2 | 3 | export const toDate = function(date) { 4 | date = new Date(date); 5 | if (isNaN(date.getTime())) return null; 6 | return date; 7 | }; 8 | 9 | export const formatDate = function(date, format) { 10 | date = toDate(date); 11 | if (!date) return ''; 12 | return dateUtil.format(date, format || 'yyyy-MM-dd'); 13 | }; 14 | 15 | export const parseDate = function(string, format) { 16 | return dateUtil.parse(string, format || 'yyyy-MM-dd'); 17 | }; 18 | 19 | export const getDayCountOfMonth = function(year, month) { 20 | if (month === 3 || month === 5 || month === 8 || month === 10) { 21 | return 30; 22 | } 23 | 24 | if (month === 1) { 25 | if (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) { 26 | return 29; 27 | } else { 28 | return 28; 29 | } 30 | } 31 | 32 | return 31; 33 | }; 34 | 35 | export const getFirstDayOfMonth = function(date) { 36 | const temp = new Date(date.getTime()); 37 | temp.setDate(1); 38 | return temp.getDay(); 39 | }; 40 | 41 | export const prevMonth = function(src) { 42 | const year = src.getFullYear(); 43 | const month = src.getMonth(); 44 | const date = src.getDate(); 45 | 46 | const newYear = month === 0 ? year - 1 : year; 47 | const newMonth = month === 0 ? 11 : month - 1; 48 | 49 | const newMonthDayCount = getDayCountOfMonth(newYear, newMonth); 50 | if (newMonthDayCount < date) { 51 | src.setDate(newMonthDayCount); 52 | } 53 | 54 | src.setMonth(newMonth); 55 | src.setFullYear(newYear); 56 | 57 | return new Date(src.getTime()); 58 | }; 59 | 60 | export const nextMonth = function(src) { 61 | const year = src.getFullYear(); 62 | const month = src.getMonth(); 63 | const date = src.getDate(); 64 | 65 | const newYear = month === 11 ? year + 1 : year; 66 | const newMonth = month === 11 ? 0 : month + 1; 67 | 68 | const newMonthDayCount = getDayCountOfMonth(newYear, newMonth); 69 | if (newMonthDayCount < date) { 70 | src.setDate(newMonthDayCount); 71 | } 72 | 73 | src.setMonth(newMonth); 74 | src.setFullYear(newYear); 75 | 76 | return new Date(src.getTime()); 77 | }; 78 | 79 | export const initTimeDate = function () { 80 | const date = new Date(); 81 | date.setHours(0); 82 | date.setMinutes(0); 83 | date.setSeconds(0); 84 | return date; 85 | }; -------------------------------------------------------------------------------- /examples/routers/cascader.vue: -------------------------------------------------------------------------------- 1 | 13 | 69 | -------------------------------------------------------------------------------- /src/styles/mixins/tooltip.less: -------------------------------------------------------------------------------- 1 | .popper(@arrow, @arrow-width, @arrow-distance, @bg){ 2 | display: block; 3 | visibility: visible; 4 | font-size: @font-size-small; 5 | line-height: @line-height-base; 6 | position: absolute; 7 | z-index: @zindex-tooltip; 8 | 9 | &[x-placement^="top"] { 10 | padding: @arrow-width 0 @arrow-distance 0; 11 | } 12 | &[x-placement^="right"] { 13 | padding: 0 @arrow-width 0 @arrow-distance; 14 | } 15 | &[x-placement^="bottom"] { 16 | padding: @arrow-distance 0 @arrow-width 0; 17 | } 18 | &[x-placement^="left"] { 19 | padding: 0 @arrow-distance 0 @arrow-width; 20 | } 21 | 22 | &[x-placement^="top"] .@{arrow} { 23 | bottom: @arrow-distance - @arrow-width; 24 | border-width: @arrow-width @arrow-width 0; 25 | border-top-color: @bg; 26 | } 27 | &[x-placement="top"] .@{arrow} { 28 | left: 50%; 29 | margin-left: -@arrow-width; 30 | } 31 | &[x-placement="top-start"] .@{arrow} { 32 | left: 16px; 33 | } 34 | &[x-placement="top-end"] .@{arrow} { 35 | right: 16px; 36 | } 37 | 38 | &[x-placement^="right"] .@{arrow} { 39 | left: @arrow-distance - @arrow-width; 40 | border-width: @arrow-width @arrow-width @arrow-width 0; 41 | border-right-color: @bg; 42 | } 43 | &[x-placement="right"] .@{arrow} { 44 | top: 50%; 45 | margin-top: -@arrow-width; 46 | } 47 | &[x-placement="right-start"] .@{arrow} { 48 | top: 8px; 49 | } 50 | &[x-placement="right-end"] .@{arrow} { 51 | bottom: 8px; 52 | } 53 | 54 | &[x-placement^="left"] .@{arrow} { 55 | right: @arrow-distance - @arrow-width; 56 | border-width: @arrow-width 0 @arrow-width @arrow-width; 57 | border-left-color: @bg; 58 | } 59 | &[x-placement="left"] .@{arrow} { 60 | top: 50%; 61 | margin-top: -@arrow-width; 62 | } 63 | &[x-placement="left-start"] .@{arrow} { 64 | top: 8px; 65 | } 66 | &[x-placement="left-end"] .@{arrow} { 67 | bottom: 8px; 68 | } 69 | 70 | &[x-placement^="bottom"] .@{arrow} { 71 | top: @arrow-distance - @arrow-width; 72 | border-width: 0 @arrow-width @arrow-width; 73 | border-bottom-color: @bg; 74 | } 75 | &[x-placement="bottom"] .@{arrow} { 76 | left: 50%; 77 | margin-left: -@arrow-width; 78 | } 79 | &[x-placement="bottom-start"] .@{arrow} { 80 | left: 16px; 81 | } 82 | &[x-placement="bottom-end"] .@{arrow} { 83 | right: 16px; 84 | } 85 | } -------------------------------------------------------------------------------- /src/components/radio/radio-group.vue: -------------------------------------------------------------------------------- 1 | 6 | 83 | -------------------------------------------------------------------------------- /src/locale/lang/zh-CN.js: -------------------------------------------------------------------------------- 1 | export default { 2 | i: { 3 | select: { 4 | placeholder: '请选择', 5 | noMatch: '无匹配数据', 6 | loading: '加载中' 7 | }, 8 | table: { 9 | noDataText: '暂无数据', 10 | noFilteredDataText: '暂无筛选结果', 11 | confirmFilter: '筛选', 12 | resetFilter: '重置', 13 | clearFilter: '全部' 14 | }, 15 | datepicker: { 16 | selectDate: '选择日期', 17 | selectTime: '选择时间', 18 | startTime: '开始时间', 19 | endTime: '结束时间', 20 | clear: '清空', 21 | ok: '确定', 22 | month: '月', 23 | month1: '1 月', 24 | month2: '2 月', 25 | month3: '3 月', 26 | month4: '4 月', 27 | month5: '5 月', 28 | month6: '6 月', 29 | month7: '7 月', 30 | month8: '8 月', 31 | month9: '9 月', 32 | month10: '10 月', 33 | month11: '11 月', 34 | month12: '12 月', 35 | year: '年', 36 | weeks: { 37 | sun: '日', 38 | mon: '一', 39 | tue: '二', 40 | wed: '三', 41 | thu: '四', 42 | fri: '五', 43 | sat: '六' 44 | }, 45 | months: { 46 | m1: '1月', 47 | m2: '2月', 48 | m3: '3月', 49 | m4: '4月', 50 | m5: '5月', 51 | m6: '6月', 52 | m7: '7月', 53 | m8: '8月', 54 | m9: '9月', 55 | m10: '10月', 56 | m11: '11月', 57 | m12: '12月' 58 | } 59 | }, 60 | transfer: { 61 | titles: { 62 | source: '源列表', 63 | target: '目的列表' 64 | }, 65 | filterPlaceholder: '请输入搜索内容', 66 | notFoundText: '列表为空' 67 | }, 68 | modal: { 69 | okText: '确定', 70 | cancelText: '取消' 71 | }, 72 | poptip: { 73 | okText: '确定', 74 | cancelText: '取消' 75 | }, 76 | page: { 77 | prev: '上一页', 78 | next: '下一页', 79 | total: '共', 80 | item: '条', 81 | items: '条', 82 | prev5: '向前 5 页', 83 | next5: '向后 5 页', 84 | page: '条/页', 85 | goto: '跳至', 86 | p: '页' 87 | }, 88 | rate: { 89 | star: '星', 90 | stars: '星' 91 | }, 92 | tree: { 93 | emptyText: '暂无数据' 94 | } 95 | } 96 | }; -------------------------------------------------------------------------------- /src/locale/lang/zh-TW.js: -------------------------------------------------------------------------------- 1 | export default { 2 | i: { 3 | select: { 4 | placeholder: '請選擇', 5 | noMatch: '無匹配數據', 6 | loading: '加載中' 7 | }, 8 | table: { 9 | noDataText: '暫無數據', 10 | noFilteredDataText: '暫無篩選結果', 11 | confirmFilter: '篩選', 12 | resetFilter: '重置', 13 | clearFilter: '全部' 14 | }, 15 | datepicker: { 16 | selectDate: '選擇日期', 17 | selectTime: '選擇時間', 18 | startTime: '開始時間', 19 | endTime: '結束時間', 20 | clear: '清空', 21 | ok: '確定', 22 | month: '月', 23 | month1: '1 月', 24 | month2: '2 月', 25 | month3: '3 月', 26 | month4: '4 月', 27 | month5: '5 月', 28 | month6: '6 月', 29 | month7: '7 月', 30 | month8: '8 月', 31 | month9: '9 月', 32 | month10: '10 月', 33 | month11: '11 月', 34 | month12: '12 月', 35 | year: '年', 36 | weeks: { 37 | sun: '日', 38 | mon: '一', 39 | tue: '二', 40 | wed: '三', 41 | thu: '四', 42 | fri: '五', 43 | sat: '六' 44 | }, 45 | months: { 46 | m1: '1月', 47 | m2: '2月', 48 | m3: '3月', 49 | m4: '4月', 50 | m5: '5月', 51 | m6: '6月', 52 | m7: '7月', 53 | m8: '8月', 54 | m9: '9月', 55 | m10: '10月', 56 | m11: '11月', 57 | m12: '12月' 58 | } 59 | }, 60 | transfer: { 61 | titles: { 62 | source: '源列表', 63 | target: '目的列表' 64 | }, 65 | filterPlaceholder: '請輸入搜索內容', 66 | notFoundText: '列表爲空' 67 | }, 68 | modal: { 69 | okText: '確定', 70 | cancelText: '取消' 71 | }, 72 | poptip: { 73 | okText: '確定', 74 | cancelText: '取消' 75 | }, 76 | page: { 77 | prev: '上壹頁', 78 | next: '下壹頁', 79 | total: '共', 80 | item: '條', 81 | items: '條', 82 | prev5: '向前 5 頁', 83 | next5: '向後 5 頁', 84 | page: '條/頁', 85 | goto: '跳至', 86 | p: '頁' 87 | }, 88 | rate: { 89 | star: '星', 90 | stars: '星' 91 | }, 92 | tree: { 93 | emptyText: '暫無數據' 94 | } 95 | } 96 | }; -------------------------------------------------------------------------------- /src/styles/components/alert.less: -------------------------------------------------------------------------------- 1 | @alert-prefix-cls: ~"@{css-prefix}alert"; 2 | @icon-prefix-cls: ~"@{css-prefix}icon"; 3 | 4 | .@{alert-prefix-cls}{ 5 | position: relative; 6 | padding: 8px 48px 8px 16px; 7 | border-radius: @border-radius-base; 8 | color: @text-color; 9 | font-size: @font-size-small; 10 | line-height: 16px; 11 | margin-bottom: 10px; 12 | 13 | &&-with-icon{ 14 | padding: 8px 48px 8px 38px; 15 | } 16 | 17 | &-icon { 18 | font-size: @font-size-base; 19 | top: 8px; 20 | left: 16px; 21 | position: absolute; 22 | } 23 | 24 | &-desc { 25 | font-size: @font-size-small; 26 | color: @text-color; 27 | line-height: 21px; 28 | display: none; 29 | text-align: justify; 30 | } 31 | 32 | &-success { 33 | border: 1px solid tint(@success-color, 80%); 34 | background-color: tint(@success-color, 90%); 35 | .@{alert-prefix-cls}-icon { 36 | color: @success-color; 37 | } 38 | } 39 | 40 | &-info { 41 | border: 1px solid tint(@primary-color, 80%); 42 | background-color: tint(@primary-color, 90%); 43 | .@{alert-prefix-cls}-icon { 44 | color: @primary-color; 45 | } 46 | } 47 | 48 | &-warning { 49 | border: 1px solid tint(@warning-color, 80%); 50 | background-color: tint(@warning-color, 90%); 51 | .@{alert-prefix-cls}-icon { 52 | color: @warning-color; 53 | } 54 | } 55 | 56 | &-error { 57 | border: 1px solid tint(@error-color, 80%); 58 | background-color: tint(@error-color, 90%); 59 | .@{alert-prefix-cls}-icon { 60 | color: @error-color; 61 | } 62 | } 63 | 64 | &-close { 65 | .content-close(-3px); 66 | } 67 | 68 | &-with-desc { 69 | padding: 16px; 70 | position: relative; 71 | border-radius: @border-radius-base; 72 | margin-bottom: 10px; 73 | color: @text-color; 74 | line-height: 1.5; 75 | } 76 | 77 | &-with-desc&-with-icon{ 78 | padding: 16px 16px 16px 69px; 79 | } 80 | 81 | &-with-desc &-desc{ 82 | display: block; 83 | } 84 | 85 | &-with-desc &-message { 86 | font-size: 14px; 87 | color: @title-color; 88 | display: block; 89 | } 90 | 91 | &-with-desc &-icon { 92 | top: 50%; 93 | left: 24px; 94 | margin-top: -21px; 95 | font-size: 28px; 96 | } 97 | 98 | &-with-banner{ 99 | border-radius: 0; 100 | } 101 | } -------------------------------------------------------------------------------- /src/locale/lang/ko-KR.js: -------------------------------------------------------------------------------- 1 | export default { 2 | i: { 3 | select: { 4 | placeholder: '선택', 5 | noMatch: '일치하는 데이터 없음', 6 | loading: '로딩' 7 | }, 8 | table: { 9 | noDataText: '데이터 없음', 10 | noFilteredDataText: '필터된 데이터 없음', 11 | confirmFilter: '확인', 12 | resetFilter: '초기화', 13 | clearFilter: '전부' 14 | }, 15 | datepicker: { 16 | selectDate: '날짜 선택', 17 | selectTime: '시간 선택', 18 | startTime: '시작 시간', 19 | endTime: '종료 시간', 20 | clear: '삭제', 21 | ok: '예', 22 | month: '월', 23 | month1: '1월', 24 | month2: '2월', 25 | month3: '3월', 26 | month4: '4월', 27 | month5: '5월', 28 | month6: '6월', 29 | month7: '7월', 30 | month8: '8월', 31 | month9: '9월', 32 | month10: '10월', 33 | month11: '11월', 34 | month12: '12월', 35 | year: '년', 36 | weeks: { 37 | sun: '일', 38 | mon: '월', 39 | tue: '화', 40 | wed: '수', 41 | thu: '목', 42 | fri: '금', 43 | sat: '토' 44 | }, 45 | months: { 46 | m1: '1월', 47 | m2: '2월', 48 | m3: '3월', 49 | m4: '4월', 50 | m5: '5월', 51 | m6: '6월', 52 | m7: '7월', 53 | m8: '8월', 54 | m9: '9월', 55 | m10: '10월', 56 | m11: '11월', 57 | m12: '12월' 58 | } 59 | }, 60 | transfer: { 61 | titles: { 62 | source: '소스', 63 | target: '타겟' 64 | }, 65 | filterPlaceholder: '여기서 찾기', 66 | notFoundText: '아무 것도 찾을 수 없음' 67 | }, 68 | modal: { 69 | okText: '예', 70 | cancelText: '취소' 71 | }, 72 | poptip: { 73 | okText: '예', 74 | cancelText: '취소' 75 | }, 76 | page: { 77 | prev: '이전 페이지', 78 | next: '다음 페이지', 79 | total: '전체', 80 | item: '항목', 81 | items: '항목', 82 | prev5: '이전 5 페이지', 83 | next5: '다음 5 페이지', 84 | page: '/페이지', 85 | goto: '이동', 86 | p: '' 87 | }, 88 | rate: { 89 | star: '중요', 90 | stars: '중요' 91 | }, 92 | tree: { 93 | emptyText: '데이터 없음' 94 | } 95 | } 96 | }; -------------------------------------------------------------------------------- /src/components/button/button.vue: -------------------------------------------------------------------------------- 1 | 8 | 77 | -------------------------------------------------------------------------------- /src/locale/lang/ja-JP.js: -------------------------------------------------------------------------------- 1 | export default { 2 | i: { 3 | select: { 4 | placeholder: '選んでください', 5 | noMatch: 'マッチするデータなし', 6 | loading: 'ロード中' 7 | }, 8 | table: { 9 | noDataText: 'データなし', 10 | noFilteredDataText: 'スクリーニングしたデータなし', 11 | confirmFilter: 'スクリーニング', 12 | resetFilter: 'リセット', 13 | clearFilter: '全部' 14 | }, 15 | datepicker: { 16 | selectDate: '日時を選んでください', 17 | selectTime: '時間を選んでください', 18 | startTime: 'スタート時間', 19 | endTime: '終了時間', 20 | clear: 'クリーア', 21 | ok: '確定', 22 | month: '月', 23 | month1: '1 月', 24 | month2: '2 月', 25 | month3: '3 月', 26 | month4: '4 月', 27 | month5: '5 月', 28 | month6: '6 月', 29 | month7: '7 月', 30 | month8: '8 月', 31 | month9: '9 月', 32 | month10: '10 月', 33 | month11: '11 月', 34 | month12: '12 月', 35 | year: '年', 36 | weeks: { 37 | sun: '日', 38 | mon: '月', 39 | tue: '火', 40 | wed: '水', 41 | thu: '木', 42 | fri: '金', 43 | sat: '土' 44 | }, 45 | months: { 46 | m1: '1月', 47 | m2: '2月', 48 | m3: '3月', 49 | m4: '4月', 50 | m5: '5月', 51 | m6: '6月', 52 | m7: '7月', 53 | m8: '8月', 54 | m9: '9月', 55 | m10: '10月', 56 | m11: '11月', 57 | m12: '12月' 58 | } 59 | }, 60 | transfer: { 61 | titles: { 62 | source: 'ソースリスト', 63 | target: 'ターゲットリスト' 64 | }, 65 | filterPlaceholder: '検索内容を入力ください', 66 | notFoundText: '内容が見つかってなかった' 67 | }, 68 | modal: { 69 | okText: '確定', 70 | cancelText: 'キャンセル' 71 | }, 72 | poptip: { 73 | okText: '確定', 74 | cancelText: 'キャンセル' 75 | }, 76 | page: { 77 | prev: '前へ', 78 | next: '次へ', 79 | total: '全部', 80 | item: '件', 81 | items: '件', 82 | prev5: '前の5ページへ', 83 | next5: '次の5ページへ', 84 | page: '件/ページ', 85 | goto: '', 86 | p: 'ページ目へ' 87 | }, 88 | rate: { 89 | star: '点', 90 | stars: '点' 91 | }, 92 | tree: { 93 | emptyText: 'データなし' 94 | } 95 | } 96 | }; -------------------------------------------------------------------------------- /src/components/back-top/back-top.vue: -------------------------------------------------------------------------------- 1 | 10 | 82 | -------------------------------------------------------------------------------- /src/components/base/collapse-transition.js: -------------------------------------------------------------------------------- 1 | // Thanks to https://github.com/ElemeFE/element/blob/dev/src/transitions/collapse-transition.js 2 | 3 | import { addClass, removeClass } from '../../utils/assist'; 4 | 5 | const Transition = { 6 | beforeEnter(el) { 7 | addClass(el, 'collapse-transition'); 8 | if (!el.dataset) el.dataset = {}; 9 | 10 | el.dataset.oldPaddingTop = el.style.paddingTop; 11 | el.dataset.oldPaddingBottom = el.style.paddingBottom; 12 | 13 | el.style.height = '0'; 14 | el.style.paddingTop = 0; 15 | el.style.paddingBottom = 0; 16 | }, 17 | 18 | enter(el) { 19 | el.dataset.oldOverflow = el.style.overflow; 20 | if (el.scrollHeight !== 0) { 21 | el.style.height = el.scrollHeight + 'px'; 22 | el.style.paddingTop = el.dataset.oldPaddingTop; 23 | el.style.paddingBottom = el.dataset.oldPaddingBottom; 24 | } else { 25 | el.style.height = ''; 26 | el.style.paddingTop = el.dataset.oldPaddingTop; 27 | el.style.paddingBottom = el.dataset.oldPaddingBottom; 28 | } 29 | 30 | el.style.overflow = 'hidden'; 31 | }, 32 | 33 | afterEnter(el) { 34 | // for safari: remove class then reset height is necessary 35 | removeClass(el, 'collapse-transition'); 36 | el.style.height = ''; 37 | el.style.overflow = el.dataset.oldOverflow; 38 | }, 39 | 40 | beforeLeave(el) { 41 | if (!el.dataset) el.dataset = {}; 42 | el.dataset.oldPaddingTop = el.style.paddingTop; 43 | el.dataset.oldPaddingBottom = el.style.paddingBottom; 44 | el.dataset.oldOverflow = el.style.overflow; 45 | 46 | el.style.height = el.scrollHeight + 'px'; 47 | el.style.overflow = 'hidden'; 48 | }, 49 | 50 | leave(el) { 51 | if (el.scrollHeight !== 0) { 52 | // for safari: add class after set height, or it will jump to zero height suddenly, weired 53 | addClass(el, 'collapse-transition'); 54 | el.style.height = 0; 55 | el.style.paddingTop = 0; 56 | el.style.paddingBottom = 0; 57 | } 58 | }, 59 | 60 | afterLeave(el) { 61 | removeClass(el, 'collapse-transition'); 62 | el.style.height = ''; 63 | el.style.overflow = el.dataset.oldOverflow; 64 | el.style.paddingTop = el.dataset.oldPaddingTop; 65 | el.style.paddingBottom = el.dataset.oldPaddingBottom; 66 | } 67 | }; 68 | 69 | export default { 70 | name: 'CollapseTransition', 71 | functional: true, 72 | render(h, { children }) { 73 | const data = { 74 | on: Transition 75 | }; 76 | 77 | return h('transition', data, children); 78 | } 79 | }; 80 | -------------------------------------------------------------------------------- /src/components/select/option.vue: -------------------------------------------------------------------------------- 1 | 4 | 81 | -------------------------------------------------------------------------------- /src/components/table/export-csv.js: -------------------------------------------------------------------------------- 1 | function has (browser) { 2 | const ua = navigator.userAgent; 3 | if (browser === 'ie') { 4 | const isIE = ua.indexOf('compatible') > -1 && ua.indexOf('MSIE') > -1; 5 | if (isIE) { 6 | const reIE = new RegExp('MSIE (\\d+\\.\\d+);'); 7 | reIE.test(ua); 8 | return parseFloat(RegExp['$1']); 9 | } else { 10 | return false; 11 | } 12 | } else { 13 | return ua.indexOf(browser) > -1; 14 | } 15 | } 16 | 17 | const csv = { 18 | _isIE11 () { 19 | let iev = 0; 20 | const ieold = (/MSIE (\d+\.\d+);/.test(navigator.userAgent)); 21 | const trident = !!navigator.userAgent.match(/Trident\/7.0/); 22 | const rv = navigator.userAgent.indexOf('rv:11.0'); 23 | 24 | if (ieold) { 25 | iev = Number(RegExp.$1); 26 | } 27 | if (navigator.appVersion.indexOf('MSIE 10') !== -1) { 28 | iev = 10; 29 | } 30 | if (trident && rv !== -1) { 31 | iev = 11; 32 | } 33 | 34 | return iev === 11; 35 | }, 36 | 37 | _isEdge () { 38 | return /Edge/.test(navigator.userAgent); 39 | }, 40 | 41 | _getDownloadUrl (text) { 42 | const BOM = '\uFEFF'; 43 | // Add BOM to text for open in excel correctly 44 | if (window.Blob && window.URL && window.URL.createObjectURL && !has('Safari')) { 45 | const csvData = new Blob([BOM + text], { type: 'text/csv' }); 46 | return URL.createObjectURL(csvData); 47 | } else { 48 | return 'data:attachment/csv;charset=utf-8,' + BOM + encodeURIComponent(text); 49 | } 50 | }, 51 | 52 | download (filename, text) { 53 | if (has('ie') && has('ie') < 10) { 54 | // has module unable identify ie11 and Edge 55 | const oWin = window.top.open('about:blank', '_blank'); 56 | oWin.document.charset = 'utf-8'; 57 | oWin.document.write(text); 58 | oWin.document.close(); 59 | oWin.document.execCommand('SaveAs', filename); 60 | oWin.close(); 61 | } else if (has('ie') === 10 || this._isIE11() || this._isEdge()) { 62 | const BOM = '\uFEFF'; 63 | const csvData = new Blob([BOM + text], { type: 'text/csv' }); 64 | navigator.msSaveBlob(csvData, filename); 65 | } else { 66 | const link = document.createElement('a'); 67 | link.download = filename; 68 | link.href = this._getDownloadUrl(text); 69 | link.target = '_blank'; 70 | document.body.appendChild(link); 71 | link.click(); 72 | document.body.removeChild(link); 73 | } 74 | } 75 | }; 76 | 77 | export default csv; -------------------------------------------------------------------------------- /src/components/date-picker/base/month-table.vue: -------------------------------------------------------------------------------- 1 | 6 | 80 | -------------------------------------------------------------------------------- /src/styles/components/progress.less: -------------------------------------------------------------------------------- 1 | @progress-prefix-cls: ~"@{css-prefix}progress"; 2 | 3 | .@{progress-prefix-cls} { 4 | display: inline-block; 5 | 6 | width: 100%; 7 | &-vertical { 8 | height: 100%; 9 | width: auto; 10 | } 11 | 12 | font-size: @font-size-small; 13 | position: relative; 14 | 15 | &-outer { 16 | display: inline-block; 17 | width: 100%; 18 | margin-right: 0; 19 | padding-right: 0; 20 | 21 | .@{progress-prefix-cls}-show-info & { 22 | padding-right: 55px; 23 | margin-right: -55px; 24 | } 25 | } 26 | &-vertical &-outer { 27 | height: 100%; 28 | width: auto; 29 | } 30 | 31 | &-inner { 32 | display: inline-block; 33 | width: 100%; 34 | background-color: #f3f3f3; 35 | border-radius: 100px; 36 | vertical-align: middle; 37 | } 38 | &-vertical &-inner { 39 | height: 100%; 40 | width: auto; 41 | 42 | & > *, &:after { 43 | display: inline-block; 44 | vertical-align: bottom; 45 | } 46 | 47 | &:after { 48 | content: ''; 49 | height: 100%; 50 | } 51 | } 52 | 53 | &-bg { 54 | border-radius: 100px; 55 | background-color: @info-color; 56 | transition: all @transition-time linear; 57 | position: relative; 58 | } 59 | 60 | &-text { 61 | display: inline-block; 62 | margin-left: 5px; 63 | text-align: left; 64 | font-size: 1em; 65 | vertical-align: middle; 66 | } 67 | 68 | &-active { 69 | .@{progress-prefix-cls}-bg:before { 70 | content: ''; 71 | opacity: 0; 72 | position: absolute; 73 | top: 0; 74 | left: 0; 75 | right: 0; 76 | bottom: 0; 77 | background: #fff; 78 | border-radius: 10px; 79 | animation: ivu-progress-active 2s @ease-in-out infinite; 80 | } 81 | } 82 | 83 | &-wrong { 84 | .@{progress-prefix-cls}-bg { 85 | background-color: @error-color; 86 | } 87 | .@{progress-prefix-cls}-text { 88 | color: @error-color; 89 | } 90 | } 91 | 92 | &-success { 93 | .@{progress-prefix-cls}-bg { 94 | background-color: @success-color; 95 | } 96 | .@{progress-prefix-cls}-text { 97 | color: @success-color; 98 | } 99 | } 100 | } 101 | 102 | @keyframes ivu-progress-active { 103 | 0% { 104 | opacity: .3; 105 | width: 0; 106 | } 107 | 100% { 108 | opacity: 0; 109 | width: 100%; 110 | } 111 | } 112 | --------------------------------------------------------------------------------