Commit e355dd49d3434c503c21db078ee684a490e250fc

Authored by 梁灏
1 parent c13471b5

add Select Component

add Select Component
components/collapse/panel.vue
@@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
4 <Icon type="arrow-right-b"></Icon> 4 <Icon type="arrow-right-b"></Icon>
5 <slot></slot> 5 <slot></slot>
6 </div> 6 </div>
7 - <div :class="concentClasses" v-show="isActive" transition="height"> 7 + <div :class="concentClasses" v-show="isActive">
8 <div :class="boxClasses"><slot name="content"></slot></div> 8 <div :class="boxClasses"><slot name="content"></slot></div>
9 </div> 9 </div>
10 </div> 10 </div>
components/page/options.vue
1 <template> 1 <template>
2 <div v-if="showSizer || showElevator" :class="optsClasses"> 2 <div v-if="showSizer || showElevator" :class="optsClasses">
3 <div v-if="showSizer" :class="sizerClasses"> 3 <div v-if="showSizer" :class="sizerClasses">
4 - <select v-model="pageSize" @change="changeSize">  
5 - <option :value="item" v-for="item in pageSizeOpts">{{ item }} 条/页</option>  
6 - </select> 4 + <i-select :model.sync="pageSize" @on-change="changeSize">
  5 + <i-option v-for="item in pageSizeOpts" :value="item" style="text-align:center;">{{ item }} 条/页</i-option>
  6 + </i-select>
7 </div> 7 </div>
8 <div v-if="showElevator" :class="ElevatorClasses"> 8 <div v-if="showElevator" :class="ElevatorClasses">
9 跳至 9 跳至
@@ -13,6 +13,9 @@ @@ -13,6 +13,9 @@
13 </div> 13 </div>
14 </template> 14 </template>
15 <script> 15 <script>
  16 + import iSelect from '../../components/select/select.vue';
  17 + import iOption from '../../components/select/option.vue';
  18 +
16 const prefixCls = 'ivu-page'; 19 const prefixCls = 'ivu-page';
17 20
18 function isValueNumber (value) { 21 function isValueNumber (value) {
@@ -20,6 +23,7 @@ @@ -20,6 +23,7 @@
20 } 23 }
21 24
22 export default { 25 export default {
  26 + components: { iSelect, iOption },
23 props: { 27 props: {
24 pageSizeOpts: Array, 28 pageSizeOpts: Array,
25 showSizer: Boolean, 29 showSizer: Boolean,
components/select/dropdown.vue 0 → 100644
  1 +<template>
  2 + <div class="ivu-select-dropdown"><slot></slot></div>
  3 +</template>
  4 +<script>
  5 + import Popper from 'popper.js';
  6 +
  7 + export default {
  8 + data () {
  9 + return {
  10 + popper: null
  11 + }
  12 + },
  13 + methods: {
  14 + update () {
  15 + if (this.popper) {
  16 + this.$nextTick(() => {
  17 + this.popper.update();
  18 + });
  19 + } else {
  20 + this.$nextTick(() => {
  21 + this.popper = new Popper(this.$parent.$els.reference, this.$el, {
  22 + gpuAcceleration: false,
  23 + placement: 'bottom-start',
  24 + boundariesPadding: 0,
  25 + forceAbsolute: true
  26 + });
  27 + this.popper.onCreate(popper => {
  28 + this.resetTransformOrigin(popper);
  29 + });
  30 + });
  31 + }
  32 + },
  33 + destroy () {
  34 + if (this.popper) {
  35 + this.resetTransformOrigin(this.popper);
  36 + setTimeout(() => {
  37 + this.popper.destroy();
  38 + this.popper = null;
  39 + }, 300);
  40 + }
  41 + },
  42 + resetTransformOrigin(popper) {
  43 + let placementMap = {top: 'bottom', bottom: 'top'};
  44 + let placement = popper._popper.getAttribute('x-placement').split('-')[0];
  45 + let origin = placementMap[placement];
  46 + popper._popper.style.transformOrigin = `center ${ origin }`;
  47 + }
  48 + },
  49 + ready () {
  50 + this.$on('on-update-popper', this.update);
  51 + this.$on('on-destroy-popper', this.destroy);
  52 + },
  53 + beforeDestroy () {
  54 + if (this.popper) {
  55 + this.popper.destroy();
  56 + }
  57 + }
  58 + }
  59 +</script>
0 \ No newline at end of file 60 \ No newline at end of file
components/select/index.js 0 → 100644
  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 };
0 \ No newline at end of file 6 \ No newline at end of file
components/select/option-group.vue 0 → 100644
  1 +<template>
  2 + <li :class="[`${prefixCls}-wrap`]">
  3 + <div :class="[`${prefixCls}-title`]">{{ label }}</div>
  4 + <ul>
  5 + <li :class="[`${prefixCls}`]"><slot></slot></li>
  6 + </ul>
  7 + </li>
  8 +</template>
  9 +<script>
  10 + const prefixCls = 'ivu-select-group';
  11 +
  12 + export default {
  13 + props: {
  14 + label: {
  15 + type: String,
  16 + default: ''
  17 + }
  18 + },
  19 + data () {
  20 + return {
  21 + prefixCls: prefixCls
  22 + }
  23 + }
  24 + }
  25 +</script>
0 \ No newline at end of file 26 \ No newline at end of file
components/select/option.vue 0 → 100644
  1 +<template>
  2 + <li :class="classes" @click.stop="select" @mouseout.stop="blur"><slot>{{ showLabel }}</slot></li>
  3 +</template>
  4 +<script>
  5 + const prefixCls = 'ivu-select-item';
  6 +
  7 + export default {
  8 + props: {
  9 + value: {
  10 + type: [String, Number],
  11 + required: true
  12 + },
  13 + label: {
  14 + type: [String, Number]
  15 + },
  16 + disabled: {
  17 + type: Boolean,
  18 + default: false
  19 + }
  20 + },
  21 + componentName: 'select-item',
  22 + data () {
  23 + return {
  24 + selected: false,
  25 + index: 0, // for up and down to focus
  26 + isFocus: false
  27 + }
  28 + },
  29 + computed: {
  30 + classes () {
  31 + return [
  32 + `${prefixCls}`,
  33 + {
  34 + [`${prefixCls}-disabled`]: this.disabled,
  35 + [`${prefixCls}-selected`]: this.selected,
  36 + [`${prefixCls}-focus`]: this.isFocus
  37 + }
  38 + ]
  39 + },
  40 + showLabel () {
  41 + return (!!this.label) ? this.label : this.value;
  42 + }
  43 + },
  44 + methods: {
  45 + select () {
  46 + if (this.disabled) {
  47 + return false;
  48 + }
  49 +
  50 + this.$dispatch('on-select-selected', this.value);
  51 + },
  52 + blur () {
  53 + this.isFocus = false;
  54 + }
  55 + },
  56 + ready () {
  57 +
  58 + },
  59 + events: {
  60 + 'on-select-close' () {
  61 + this.isFocus = false;
  62 + }
  63 + }
  64 + }
  65 +</script>
0 \ No newline at end of file 66 \ No newline at end of file
components/select/select.vue 0 → 100644
  1 +<template>
  2 + <div :class="classes" v-clickoutside="handleClose">
  3 + <div
  4 + :class="[`${prefixCls}-selection`]"
  5 + v-el:reference
  6 + @click="toggleMenu">
  7 + <div class="ivu-tag" v-if="multiple" v-for="item in selectedMultiple">
  8 + <span class="ivu-tag-text">{{ item.label }}</span>
  9 + <Icon type="ios-close-empty" @click.stop="removeTag($index)"></Icon>
  10 + </div>
  11 + <span :class="[`${prefixCls}-placeholder`]" v-show="showPlaceholder && !filterable">{{ placeholder }}</span>
  12 + <span :class="[`${prefixCls}-selected-value`]" v-show="!showPlaceholder && !multiple && !filterable">{{ selectedSingle }}</span>
  13 + <input
  14 + type="text"
  15 + :class="[`${prefixCls}-input`]"
  16 + v-if="filterable"
  17 + v-model="query"
  18 + :placeholder="placeholder"
  19 + :style="inputStyle">
  20 + <Icon type="ios-close" :class="[`${prefixCls}-arrow`]" v-show="showCloseIcon" @click.stop="clearSingleSelect"></Icon>
  21 + <Icon type="arrow-down-b" :class="[`${prefixCls}-arrow`]"></Icon>
  22 + </div>
  23 + <Dropdown v-show="visible" transition="slide-up" v-ref:dropdown>
  24 + <ul :class="[`${prefixCls}-dropdown-list`]"><slot></slot></ul>
  25 + </Dropdown>
  26 + </div>
  27 +</template>
  28 +<script>
  29 + import Icon from '../icon';
  30 + import Dropdown from './dropdown.vue';
  31 + import clickoutside from '../../directives/clickoutside';
  32 + import { oneOf } from '../../utils/assist';
  33 +
  34 + const prefixCls = 'ivu-select';
  35 +
  36 + export default {
  37 + components: { Icon, Dropdown },
  38 + directives: { clickoutside },
  39 + props: {
  40 + model: {
  41 + type: [String, Number, Array],
  42 + default: ''
  43 + },
  44 + multiple: {
  45 + type: Boolean,
  46 + default: false
  47 + },
  48 + disabled: {
  49 + type: Boolean,
  50 + default: false
  51 + },
  52 + clearable: {
  53 + type: Boolean,
  54 + default: false
  55 + },
  56 + placeholder: {
  57 + type: String,
  58 + default: '请选择'
  59 + },
  60 + filterable: {
  61 + type: Boolean,
  62 + default: false
  63 + },
  64 + filterMethod: {
  65 + type: Function
  66 + },
  67 + size: {
  68 + validator (value) {
  69 + return oneOf(value, ['small', 'large']);
  70 + }
  71 + },
  72 + labelInValue: {
  73 + type: Boolean,
  74 + default: false
  75 + }
  76 + },
  77 + data () {
  78 + return {
  79 + prefixCls: prefixCls,
  80 + visible: false,
  81 + options: [],
  82 + optionInstances: [],
  83 + selectedSingle: '', // label
  84 + selectedMultiple: [],
  85 + focusIndex: 0,
  86 + query: '',
  87 + inputLength: 20
  88 + }
  89 + },
  90 + computed: {
  91 + classes () {
  92 + return [
  93 + `${prefixCls}`,
  94 + {
  95 + [`${prefixCls}-visible`]: this.visible,
  96 + [`${prefixCls}-disabled`]: this.disabled,
  97 + [`${prefixCls}-multiple`]: this.multiple,
  98 + [`${prefixCls}-single`]: !this.multiple,
  99 + [`${prefixCls}-show-clear`]: this.showCloseIcon,
  100 + [`${prefixCls}-${this.size}`]: !!this.size
  101 + }
  102 + ]
  103 + },
  104 + showPlaceholder () {
  105 + let status = false;
  106 +
  107 + if ((typeof this.model) === 'string') {
  108 + if (this.model === '') {
  109 + status = true;
  110 + }
  111 + } else if (Array.isArray(this.model)) {
  112 + if (!this.model.length) {
  113 + status = true;
  114 + }
  115 + }
  116 +
  117 + return status;
  118 + },
  119 + showCloseIcon () {
  120 + return !this.multiple && this.clearable && !this.showPlaceholder;
  121 + },
  122 + inputStyle () {
  123 + let style = {};
  124 +
  125 + if (this.multiple) {
  126 + style.width = `${this.inputLength}px`;
  127 + }
  128 +
  129 + return style;
  130 + }
  131 + },
  132 + methods: {
  133 + toggleMenu () {
  134 + if (this.disabled) {
  135 + return false;
  136 + }
  137 +
  138 + this.visible = !this.visible;
  139 + },
  140 + hideMenu () {
  141 + this.visible = false;
  142 + this.focusIndex = 0;
  143 + this.$broadcast('on-select-close');
  144 + },
  145 + // find option component
  146 + findChild (cb) {
  147 + const find = function (child) {
  148 + const name = child.$options.componentName;
  149 +
  150 + if (name) {
  151 + cb(child);
  152 + } else if (child.$children.length) {
  153 + child.$children.forEach((innerChild) => {
  154 + find(innerChild, cb);
  155 + });
  156 + }
  157 + };
  158 +
  159 + if (this.optionInstances.length) {
  160 + this.optionInstances.forEach((child) => {
  161 + find(child);
  162 + })
  163 + } else {
  164 + this.$children.forEach((child) => {
  165 + find(child);
  166 + });
  167 + }
  168 + },
  169 + updateOptions (init) {
  170 + let options = [];
  171 + let index = 1;
  172 +
  173 + this.findChild((child) => {
  174 + options.push({
  175 + value: child.value,
  176 + label: (child.label === undefined) ? child.$el.innerHTML : child.label
  177 + });
  178 + child.index = index++;
  179 +
  180 + if (init) {
  181 + this.optionInstances.push(child);
  182 + }
  183 + });
  184 +
  185 + this.options = options;
  186 +
  187 + if (init) {
  188 + this.updateSingleSelected(true);
  189 + this.updateMultipleSelected(true);
  190 + }
  191 + },
  192 + updateSingleSelected (init = false) {
  193 + const type = typeof this.model;
  194 +
  195 + if (type === 'string' || type === 'number') {
  196 + for (let i = 0; i < this.options.length; i++) {
  197 + if (this.model === this.options[i].value) {
  198 + this.selectedSingle = this.options[i].label;
  199 + break;
  200 + }
  201 + }
  202 + }
  203 +
  204 + this.toggleSingleSelected(this.model, init);
  205 + },
  206 + clearSingleSelect () {
  207 + if (this.showCloseIcon) {
  208 + this.findChild((child) => {
  209 + child.selected = false;
  210 + });
  211 + this.model = '';
  212 + }
  213 + },
  214 + updateMultipleSelected (init = false) {
  215 + if (this.multiple && Array.isArray(this.model)) {
  216 + let selected = [];
  217 +
  218 + for (let i = 0; i < this.model.length; i++) {
  219 + const model = this.model[i];
  220 +
  221 + for (let j = 0; j < this.options.length; j++) {
  222 + const option = this.options[j];
  223 +
  224 + if (model === option.value) {
  225 + selected.push({
  226 + value: option.value,
  227 + label: option.label
  228 + })
  229 + }
  230 + }
  231 + }
  232 +
  233 + this.selectedMultiple = selected;
  234 + }
  235 +
  236 + this.toggleMultipleSelected(this.model, init);
  237 + },
  238 + removeTag (index) {
  239 + if (this.disabled) {
  240 + return false;
  241 + }
  242 + this.model.splice(index, 1);
  243 + },
  244 + // to select option for single
  245 + toggleSingleSelected (value, init = false) {
  246 + if (!this.multiple) {
  247 + let label = '';
  248 +
  249 + this.findChild((child) => {
  250 + if (child.value === value) {
  251 + child.selected = true;
  252 + label = (child.label === undefined) ? child.$el.innerHTML : child.label;
  253 + } else {
  254 + child.selected = false;
  255 + }
  256 + });
  257 +
  258 + this.hideMenu();
  259 +
  260 + if (!init) {
  261 + if (this.labelInValue) {
  262 + this.$emit('on-change', {
  263 + value: value,
  264 + label: label
  265 + });
  266 + } else {
  267 + this.$emit('on-change', value);
  268 + }
  269 + }
  270 + }
  271 + },
  272 + // to select option for multiple
  273 + toggleMultipleSelected (value, init = false) {
  274 + if (this.multiple) {
  275 + let hybridValue = [];
  276 + for (let i = 0; i < value.length; i++) {
  277 + hybridValue.push({
  278 + value: value[i]
  279 + });
  280 + }
  281 +
  282 + this.findChild((child) => {
  283 + const index = value.indexOf(child.value);
  284 +
  285 + if (index >= 0) {
  286 + child.selected = true;
  287 + hybridValue[index].label = (child.label === undefined) ? child.$el.innerHTML : child.label;
  288 + } else {
  289 + child.selected = false;
  290 + }
  291 + });
  292 +
  293 + if (!init) {
  294 + if (this.labelInValue) {
  295 + this.$emit('on-change', hybridValue);
  296 + } else {
  297 + this.$emit('on-change', value);
  298 + }
  299 + }
  300 + }
  301 + },
  302 + handleClose () {
  303 + this.hideMenu();
  304 + },
  305 + handleKeydown (e) {
  306 + if (this.visible) {
  307 + const keyCode = e.keyCode;
  308 + // Esc slide-up
  309 + if (keyCode === 27) {
  310 + e.preventDefault();
  311 + this.hideMenu();
  312 + }
  313 + // next
  314 + if (keyCode === 40) {
  315 + e.preventDefault();
  316 + this.navigateOptions('next');
  317 + }
  318 + // prev
  319 + if (keyCode === 38) {
  320 + e.preventDefault();
  321 + this.navigateOptions('prev');
  322 + }
  323 + // enter
  324 + if (keyCode === 13) {
  325 + e.preventDefault();
  326 +
  327 + this.findChild((child) => {
  328 + if (child.isFocus) {
  329 + child.select();
  330 + }
  331 + });
  332 + }
  333 + }
  334 + },
  335 + navigateOptions (direction) {
  336 + if (direction === 'next') {
  337 + const next = this.focusIndex + 1;
  338 + this.focusIndex = (this.focusIndex === this.options.length) ? 1 : next;
  339 + } else if (direction === 'prev') {
  340 + const prev = this.focusIndex - 1;
  341 + this.focusIndex = (this.focusIndex <= 1) ? this.options.length : prev;
  342 + }
  343 +
  344 + let child_status = {
  345 + disabled: false
  346 + };
  347 +
  348 + this.findChild((child) => {
  349 + if (child.index === this.focusIndex) {
  350 + child_status.disabled = child.disabled;
  351 +
  352 + if (!child.disabled) {
  353 + child.isFocus = true;
  354 + }
  355 + } else {
  356 + child.isFocus = false;
  357 + }
  358 + });
  359 +
  360 + this.resetScrollTop();
  361 +
  362 + if (child_status.disabled) {
  363 + this.navigateOptions(direction);
  364 + }
  365 + },
  366 + resetScrollTop () {
  367 + const index = this.focusIndex - 1;
  368 + let bottomOverflowDistance = this.optionInstances[index].$el.getBoundingClientRect().bottom - this.$refs.dropdown.$el.getBoundingClientRect().bottom;
  369 + let topOverflowDistance = this.optionInstances[index].$el.getBoundingClientRect().top - this.$refs.dropdown.$el.getBoundingClientRect().top;
  370 +
  371 + if (bottomOverflowDistance > 0) {
  372 + this.$refs.dropdown.$el.scrollTop += bottomOverflowDistance;
  373 + }
  374 + if (topOverflowDistance < 0) {
  375 + this.$refs.dropdown.$el.scrollTop += topOverflowDistance;
  376 + }
  377 + }
  378 + },
  379 + ready () {
  380 + this.updateOptions(true);
  381 + document.addEventListener('keydown', this.handleKeydown);
  382 + },
  383 + beforeDestroy () {
  384 + document.removeEventListener('keydown', this.handleKeydown);
  385 + },
  386 + watch: {
  387 + model () {
  388 + if (this.multiple) {
  389 + this.updateMultipleSelected();
  390 + } else {
  391 + this.updateSingleSelected();
  392 + }
  393 + },
  394 + visible (val) {
  395 + if (val) {
  396 + this.$broadcast('on-update-popper');
  397 + } else {
  398 +
  399 + }
  400 + }
  401 + },
  402 + events: {
  403 + 'on-select-selected' (value) {
  404 + if (this.model === value) {
  405 + this.hideMenu();
  406 + } else {
  407 + if (this.multiple) {
  408 + const index = this.model.indexOf(value);
  409 + if (index >= 0) {
  410 + this.removeTag(index);
  411 + } else {
  412 + this.model.push(value);
  413 + }
  414 + } else {
  415 + this.model = value;
  416 + }
  417 + }
  418 + }
  419 + }
  420 + }
  421 +</script>
0 \ No newline at end of file 422 \ No newline at end of file
directives/clickoutside.js 0 → 100644
  1 +export default {
  2 + bind () {
  3 + this.documentHandler = (e) => {
  4 + if (this.el.contains(e.target)) {
  5 + return false;
  6 + }
  7 + if (this.expression) {
  8 + this.vm[this.expression]();
  9 + }
  10 + };
  11 + document.addEventListener('click', this.documentHandler);
  12 + },
  13 + update () {
  14 +
  15 + },
  16 + unbind () {
  17 + document.removeEventListener('click', this.documentHandler);
  18 + }
  19 +}
0 \ No newline at end of file 20 \ No newline at end of file
@@ -24,6 +24,7 @@ import Message from &#39;./components/message&#39;; @@ -24,6 +24,7 @@ import Message from &#39;./components/message&#39;;
24 import Notice from './components/notice'; 24 import Notice from './components/notice';
25 import LoadingBar from './components/loading-bar'; 25 import LoadingBar from './components/loading-bar';
26 import Modal from './components/modal'; 26 import Modal from './components/modal';
  27 +import { Select, Option, OptionGroup } from './components/select';
27 28
28 const iview = { 29 const iview = {
29 Button, 30 Button,
@@ -52,7 +53,10 @@ const iview = { @@ -52,7 +53,10 @@ const iview = {
52 Message, 53 Message,
53 Notice, 54 Notice,
54 LoadingBar, 55 LoadingBar,
55 - Modal 56 + Modal,
  57 + iSelect: Select,
  58 + iOption: Option,
  59 + iOptionGroup: OptionGroup
56 }; 60 };
57 61
58 module.exports = iview; 62 module.exports = iview;
59 \ No newline at end of file 63 \ No newline at end of file
@@ -60,6 +60,11 @@ router.map({ @@ -60,6 +60,11 @@ router.map({
60 component: function (resolve) { 60 component: function (resolve) {
61 require(['./routers/tag.vue'], resolve); 61 require(['./routers/tag.vue'], resolve);
62 } 62 }
  63 + },
  64 + '/select': {
  65 + component: function (resolve) {
  66 + require(['./routers/select.vue'], resolve);
  67 + }
63 } 68 }
64 }); 69 });
65 70
local/routers/index.vue
@@ -2,14 +2,7 @@ @@ -2,14 +2,7 @@
2 2
3 </style> 3 </style>
4 <template> 4 <template>
5 - <Button @click="info">info</Button>  
6 - <Button @click="success">success</Button>  
7 - <Button @click="warning">warning</Button>  
8 - <Button @click="error">error</Button>  
9 - <Button @click="confirm">confirm</Button>  
10 - <Page :total="11" :page-size="11"></Page>  
11 - <Page :total="11" size="small"></Page>  
12 - <Page :current="1" :total="0" simple></Page> 5 + <Page :total="100" show-sizer></Page>
13 </template> 6 </template>
14 <script> 7 <script>
15 import { Modal, Button, Message, Page } from 'iview'; 8 import { Modal, Button, Message, Page } from 'iview';
local/routers/select.vue 0 → 100644
  1 +<template>
  2 + <div>
  3 + <!--<br><br><br><br><br><br><br><br><br><br><br>-->
  4 + {{ city | json }}<br>
  5 + <Button @click="city = 'hangzhou'">切换城市</Button>
  6 + <br>
  7 + <i-select :model.sync="city" style="width:200px" @on-change="change">
  8 + <i-option-group label="热门城市">
  9 + <i-option value="beijing">北京市</i-option>
  10 + <i-option value="shanghai" disabled label="上海市">上海市2</i-option>
  11 + <i-option value="shenzhen">深圳市</i-option>
  12 + </i-option-group>
  13 + <i-option-group label="二线城市">
  14 + <i-option value="nanjing">南京市</i-option>
  15 + <i-option value="hangzhou">杭州市</i-option>
  16 + </i-option-group>
  17 + <i-option value="heilongjiang" disabled>黑龙江市</i-option>
  18 + <i-option-group label="热门城市">
  19 + <i-option value="beijing">北京市</i-option>
  20 + <i-option value="shanghai" disabled label="上海市">上海市2</i-option>
  21 + <i-option value="shenzhen">深圳市</i-option>
  22 + </i-option-group>
  23 + <i-option-group label="二线城市">
  24 + <i-option value="nanjing">南京市</i-option>
  25 + <i-option value="hangzhou">杭州市</i-option>
  26 + </i-option-group>
  27 + <i-option value="heilongjiang" disabled>黑龙江市</i-option>
  28 + <i-option-group label="热门城市">
  29 + <i-option value="beijing">北京市</i-option>
  30 + <i-option value="shanghai" disabled label="上海市">上海市2</i-option>
  31 + <i-option value="shenzhen">深圳市</i-option>
  32 + </i-option-group>
  33 + <i-option-group label="二线城市">
  34 + <i-option value="nanjing">南京市</i-option>
  35 + <i-option value="hangzhou">杭州市</i-option>
  36 + </i-option-group>
  37 + <i-option value="heilongjiang" disabled>黑龙江市</i-option>
  38 + </i-select>
  39 +
  40 + <i-select :model.sync="focus" style="width:200px" @on-change="change" clearable filterable label-in-value>
  41 + <i-option value="beijing">北京市</i-option>
  42 + <i-option value="shanghai" label="上海市">上海市</i-option>
  43 + <i-option value="shenzhen" disabled>深圳市</i-option>
  44 + <i-option value="guangzhou" label="广州市">广州市2</i-option>
  45 + <i-option value="shijiazhuang" disabled>石家庄市</i-option>
  46 + <i-option value="a">a市</i-option>
  47 + <i-option value="b">b市</i-option>
  48 + <i-option value="c">c市</i-option>
  49 + <i-option value="d">d市</i-option>
  50 + <i-option value="e">e市</i-option>
  51 + </i-select>
  52 +
  53 + <i-select :model.sync="focus2" style="width:300px" @on-change="change" clearable multiple>
  54 + <i-option value="beijing">北京市</i-option>
  55 + <i-option value="shanghai">上海市</i-option>
  56 + <i-option value="shenzhen" disabled>深圳市</i-option>
  57 + <i-option value="guangzhou">广州市</i-option>
  58 + <i-option value="shijiazhuang">石家庄市</i-option>
  59 + <i-option value="a">a市</i-option>
  60 + <i-option value="b">b市</i-option>
  61 + <i-option value="c">c市</i-option>
  62 + <i-option value="d">d市</i-option>
  63 + <i-option value="e">e市</i-option>
  64 + </i-select>
  65 +
  66 + <br><br><br><br><br><br><br><br><br><br><br><br>
  67 + </div>
  68 +</template>
  69 +<script>
  70 + import { iSelect, iOption, iOptionGroup, Button } from 'iview';
  71 +
  72 + export default {
  73 + components: {
  74 + iSelect,
  75 + iOption,
  76 + iOptionGroup,
  77 + Button
  78 + },
  79 + props: {
  80 +
  81 + },
  82 + data () {
  83 + return {
  84 + city: '',
  85 + focus: '',
  86 + focus2: ['beijing', 'guangzhou', 'b']
  87 +// focus2: []
  88 + }
  89 + },
  90 + computed: {
  91 +
  92 + },
  93 + methods: {
  94 + change (data) {
  95 + console.log(data)
  96 + }
  97 + }
  98 + }
  99 +</script>
0 \ No newline at end of file 100 \ No newline at end of file
local/routers/tag.vue
1 <template> 1 <template>
2 - <div>  
3 - <Button @click="start">start</Button>  
4 - <Button @click="destroy">destroy</Button>  
5 - <Button @click="finish">finish</Button>  
6 - <Button @click="error">error</Button>  
7 - <Button @click="update">update</Button>  
8 - <br><br>  
9 - <Page :total="100"></Page>  
10 - <Page :total="100" show-sizer></Page>  
11 - <Page :total="100" show-elevator></Page>  
12 - <Page :total="100" show-total></Page>  
13 - <br><br>  
14 - <Page :current="2" :total="50" simple></Page>  
15 - <br>  
16 - <Page :total="400" size="small"></Page>  
17 - <br>  
18 - <Page :total="400" size="small" show-elevator show-sizer></Page>  
19 - <br>  
20 - <Page :total="400" size="small" show-total></Page>  
21 - </div> 2 + <Collapse active-key="1">
  3 + <Panel key="1">
  4 + 史蒂夫·乔布斯
  5 + <p slot="content">史蒂夫·乔布斯(Steve Jobs),1955年2月24日生于美国加利福尼亚州旧金山,美国发明家、企业家、美国苹果公司联合创办人。</p>
  6 + </Panel>
  7 + <Panel key="2">
  8 + 斯蒂夫·盖瑞·沃兹尼亚克
  9 + <p slot="content">斯蒂夫·盖瑞·沃兹尼亚克(Stephen Gary Wozniak),美国电脑工程师,曾与史蒂夫·乔布斯合伙创立苹果电脑(今之苹果公司)。斯蒂夫·盖瑞·沃兹尼亚克曾就读于美国科罗拉多大学,后转学入美国著名高等学府加州大学伯克利分校(UC Berkeley)并获得电机工程及计算机(EECS)本科学位(1987年)。</p>
  10 + </Panel>
  11 + <Panel key="3">
  12 + 乔纳森·伊夫
  13 + <p slot="content">乔纳森·伊夫是一位工业设计师,现任Apple公司设计师兼资深副总裁,英国爵士。他曾参与设计了iPod,iMac,iPhone,iPad等众多苹果产品。除了乔布斯,他是对苹果那些著名的产品最有影响力的人。</p>
  14 + </Panel>
  15 + </Collapse>
  16 + <Input-number :max="10" :min="1" :value="1" @on-change="change"></Input-number>
  17 + <Input-number :max="10" :min="1" :step="1.2" :value="1"></Input-number>
22 </template> 18 </template>
23 <script> 19 <script>
24 - import { Tag, LoadingBar, Button, Progress, Icon, Timeline, Page } from 'iview';  
25 - const ButtonGroup = Button.Group;  
26 - const TimelineItem = Timeline.Item; 20 + import { Collapse, InputNumber } from 'iview';
  21 + const Panel = Collapse.Panel;
27 export default { 22 export default {
28 - components: {  
29 - Tag,  
30 - Button,  
31 - Progress,  
32 - ButtonGroup,  
33 - Timeline,  
34 - TimelineItem,  
35 - Icon,  
36 - Page  
37 - },  
38 - props: {  
39 -  
40 - },  
41 - data () {  
42 - return {  
43 - percent: 0  
44 - }  
45 - },  
46 - computed: {  
47 -  
48 - }, 23 + components: { Collapse, Panel, InputNumber },
49 methods: { 24 methods: {
50 - start () {  
51 - LoadingBar.start();  
52 - },  
53 - destroy () {  
54 - LoadingBar.destroy();  
55 - },  
56 - finish () {  
57 - LoadingBar.finish();  
58 - },  
59 - error () {  
60 - LoadingBar.error();  
61 - },  
62 - update () {  
63 - LoadingBar.update(50);  
64 - },  
65 - add () {  
66 - if (this.percent >= 100) {  
67 - return false;  
68 - }  
69 - this.percent += 10;  
70 - },  
71 - minus () {  
72 - if (this.percent <= 0) {  
73 - return false;  
74 - }  
75 - this.percent -= 10; 25 + change (data) {
  26 + console.log(data);
76 } 27 }
77 } 28 }
78 } 29 }
79 -</script>  
80 \ No newline at end of file 30 \ No newline at end of file
  31 +</script>
1 { 1 {
2 "name": "iview", 2 "name": "iview",
3 - "version": "0.9.3", 3 + "version": "0.9.4",
4 "title": "iView", 4 "title": "iView",
5 "description": "A high quality UI components Library with Vue.js", 5 "description": "A high quality UI components Library with Vue.js",
6 "homepage": "http://www.iviewui.com", 6 "homepage": "http://www.iviewui.com",
@@ -29,6 +29,7 @@ @@ -29,6 +29,7 @@
29 "url": "https://github.com/iview/iview/issues" 29 "url": "https://github.com/iview/iview/issues"
30 }, 30 },
31 "dependencies": { 31 "dependencies": {
  32 + "popper.js": "^0.6.4",
32 "vue": "^1.0.26" 33 "vue": "^1.0.26"
33 }, 34 },
34 "devDependencies": { 35 "devDependencies": {
styles/animation/index.less
@@ -24,4 +24,5 @@ @@ -24,4 +24,5 @@
24 24
25 @import "fade"; 25 @import "fade";
26 @import "move"; 26 @import "move";
27 -@import "ease";  
28 \ No newline at end of file 27 \ No newline at end of file
  28 +@import "ease";
  29 +@import "slide";
29 \ No newline at end of file 30 \ No newline at end of file
styles/animation/slide.less 0 → 100644
  1 +.slide-motion(@className, @keyframeName) {
  2 + .make-motion(@className, @keyframeName);
  3 + .@{className}-enter, .@{className}-appear {
  4 + opacity: 0;
  5 + animation-timing-function: @ease-in-out;
  6 + }
  7 + .@{className}-leave {
  8 + animation-timing-function: @ease-in-out;
  9 + }
  10 +}
  11 +
  12 +.slide-motion(slide-up, ivuSlideUp);
  13 +.slide-motion(slide-down, ivuSlideDown);
  14 +.slide-motion(slide-left, ivuSlideLeft);
  15 +.slide-motion(slide-right, ivuSlideRight);
  16 +
  17 +@keyframes ivuSlideUpIn {
  18 + 0% {
  19 + opacity: 0;
  20 + transform-origin: 0% 0%;
  21 + transform: scaleY(.8);
  22 + }
  23 + 100% {
  24 + opacity: 1;
  25 + transform-origin: 0% 0%;
  26 + transform: scaleY(1);
  27 + }
  28 +}
  29 +
  30 +@keyframes ivuSlideUpOut {
  31 + 0% {
  32 + opacity: 1;
  33 + transform-origin: 0% 0%;
  34 + transform: scaleY(1);
  35 + }
  36 + 100% {
  37 + opacity: 0;
  38 + transform-origin: 0% 0%;
  39 + transform: scaleY(.8);
  40 + }
  41 +}
  42 +
  43 +@keyframes ivuSlideDownIn {
  44 + 0% {
  45 + opacity: 0;
  46 + transform-origin: 100% 100%;
  47 + transform: scaleY(.8);
  48 + }
  49 + 100% {
  50 + opacity: 1;
  51 + transform-origin: 100% 100%;
  52 + transform: scaleY(1);
  53 + }
  54 +}
  55 +
  56 +@keyframes ivuSlideDownOut {
  57 + 0% {
  58 + opacity: 1;
  59 + transform-origin: 100% 100%;
  60 + transform: scaleY(1);
  61 + }
  62 + 100% {
  63 + opacity: 0;
  64 + transform-origin: 100% 100%;
  65 + transform: scaleY(.8);
  66 + }
  67 +}
  68 +
  69 +@keyframes ivuSlideLeftIn {
  70 + 0% {
  71 + opacity: 0;
  72 + transform-origin: 0% 0%;
  73 + transform: scaleX(.8);
  74 + }
  75 + 100% {
  76 + opacity: 1;
  77 + transform-origin: 0% 0%;
  78 + transform: scaleX(1);
  79 + }
  80 +}
  81 +
  82 +@keyframes ivuSlideLeftOut {
  83 + 0% {
  84 + opacity: 1;
  85 + transform-origin: 0% 0%;
  86 + transform: scaleX(1);
  87 + }
  88 + 100% {
  89 + opacity: 0;
  90 + transform-origin: 0% 0%;
  91 + transform: scaleX(.8);
  92 + }
  93 +}
  94 +
  95 +@keyframes ivuSlideRightIn {
  96 + 0% {
  97 + opacity: 0;
  98 + transform-origin: 100% 0%;
  99 + transform: scaleX(.8);
  100 + }
  101 + 100% {
  102 + opacity: 1;
  103 + transform-origin: 100% 0%;
  104 + transform: scaleX(1);
  105 + }
  106 +}
  107 +
  108 +@keyframes ivuSlideRightOut {
  109 + 0% {
  110 + opacity: 1;
  111 + transform-origin: 100% 0%;
  112 + transform: scaleX(1);
  113 + }
  114 + 100% {
  115 + opacity: 0;
  116 + transform-origin: 100% 0%;
  117 + transform: scaleX(.8);
  118 + }
  119 +}
0 \ No newline at end of file 120 \ No newline at end of file
styles/common/iconfont/_ionicons-font.less
@@ -12,16 +12,20 @@ @@ -12,16 +12,20 @@
12 font-style: normal; 12 font-style: normal;
13 } 13 }
14 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 +
15 .ivu-icon { 29 .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; 30 + .ivu-icon();
27 } 31 }
28 \ No newline at end of file 32 \ No newline at end of file
styles/components/index.less
@@ -19,4 +19,6 @@ @@ -19,4 +19,6 @@
19 @import "timeline"; 19 @import "timeline";
20 @import "page"; 20 @import "page";
21 @import "steps"; 21 @import "steps";
22 -@import "modal";  
23 \ No newline at end of file 22 \ No newline at end of file
  23 +@import "modal";
  24 +@import "select";
  25 +@import "select-dropdown";
24 \ No newline at end of file 26 \ No newline at end of file
styles/components/select-dropdown.less 0 → 100644
  1 +@select-dropdown-prefix-cls: ~"@{css-prefix}select-dropdown";
  2 +
  3 +.@{select-dropdown-prefix-cls} {
  4 + width: 100%;
  5 + max-height: 200px;
  6 + overflow: auto;
  7 + margin: 5px 0;
  8 + padding: 7px 0;
  9 + background-color: #fff;
  10 + box-sizing: border-box;
  11 + border: 1px solid @border-color-base;
  12 + border-radius: @btn-border-radius;
  13 + box-shadow: 0 1px 3px rgba(0,0,0,.2);
  14 + position: absolute;
  15 + z-index: @zindex-select;
  16 +}
0 \ No newline at end of file 17 \ No newline at end of file
styles/components/select.less 0 → 100644
  1 +@select-prefix-cls: ~"@{css-prefix}select";
  2 +@select-item-prefix-cls: ~"@{css-prefix}select-item";
  3 +@select-group-prefix-cls: ~"@{css-prefix}select-group";
  4 +
  5 +.@{select-prefix-cls} {
  6 + display: inline-block;
  7 + width: 100%;
  8 + box-sizing: border-box;
  9 + vertical-align: middle;
  10 + color: @text-color;
  11 + font-size: @font-size-base;
  12 + position: relative;
  13 +
  14 + &-selection {
  15 + display: block;
  16 + box-sizing: border-box;
  17 + outline: none;
  18 + user-select: none;
  19 + cursor: pointer;
  20 +
  21 + background-color: #fff;
  22 + border-radius: @btn-border-radius;
  23 + border: 1px solid @border-color-base;
  24 + .transition(all @transition-time @ease-in-out);
  25 +
  26 + .@{select-prefix-cls}-arrow:nth-of-type(1) {
  27 + display: none;
  28 + cursor: pointer;
  29 + }
  30 +
  31 + &:hover {
  32 + .hover();
  33 + .@{select-prefix-cls}-arrow:nth-of-type(1) {
  34 + display: inline-block;
  35 + }
  36 + }
  37 + }
  38 +
  39 + &-show-clear &-selection:hover .@{select-prefix-cls}-arrow:nth-of-type(2){
  40 + display: none;
  41 + }
  42 +
  43 + &-arrow {
  44 + position: absolute;
  45 + top: 50%;
  46 + right: 8px;
  47 + line-height: 1;
  48 + margin-top: -6px;
  49 + color: @border-color-base;
  50 + .transition(all @transition-time @ease-in-out);
  51 + }
  52 +
  53 + &-visible{
  54 + .@{select-prefix-cls}-selection{
  55 + .active();
  56 + }
  57 +
  58 + .@{select-prefix-cls}-arrow:nth-of-type(2) {
  59 + .transform(rotate(180deg));
  60 + }
  61 + }
  62 +
  63 + &-disabled {
  64 + .@{select-prefix-cls}-selection {
  65 + .disabled();
  66 +
  67 + .@{select-prefix-cls}-arrow:nth-of-type(1) {
  68 + display: none;
  69 + }
  70 +
  71 + &:hover {
  72 + border-color: @border-color-base;
  73 + box-shadow: none;
  74 +
  75 + .@{select-prefix-cls}-arrow:nth-of-type(2) {
  76 + display: inline-block;
  77 + }
  78 + }
  79 + }
  80 + }
  81 +
  82 + &-single &-selection{
  83 + height: 28px;
  84 + position: relative;
  85 +
  86 + .@{select-prefix-cls}-placeholder{
  87 + color: @input-placeholder-color;
  88 + }
  89 +
  90 + .@{select-prefix-cls}-placeholder, .@{select-prefix-cls}-selected-value{
  91 + display: block;
  92 + height: 26px;
  93 + line-height: 26px;
  94 + overflow: hidden;
  95 + text-overflow: ellipsis;
  96 + white-space: nowrap;
  97 + padding-left: 8px;
  98 + padding-right: 24px;
  99 + }
  100 + }
  101 +
  102 + &-large&-single &-selection{
  103 + height: 32px;
  104 +
  105 + .@{select-prefix-cls}-placeholder, .@{select-prefix-cls}-selected-value{
  106 + height: 30px;
  107 + line-height: 30px;
  108 + }
  109 + }
  110 +
  111 + &-small&-single &-selection{
  112 + height: 22px;
  113 + border-radius: @btn-border-radius-small;
  114 +
  115 + .@{select-prefix-cls}-placeholder, .@{select-prefix-cls}-selected-value{
  116 + height: 20px;
  117 + line-height: 20px;
  118 + }
  119 + }
  120 +
  121 + &-multiple &-selection{
  122 + padding: 0 24px 0 2px;
  123 + min-height: 28px;
  124 +
  125 + .@{select-prefix-cls}-placeholder{
  126 + display: block;
  127 + height: 26px;
  128 + line-height: 26px;
  129 + color: @input-placeholder-color;
  130 + overflow: hidden;
  131 + text-overflow: ellipsis;
  132 + white-space: nowrap;
  133 + padding-left: 8px;
  134 + padding-right: 22px;
  135 + }
  136 + }
  137 +
  138 + // input
  139 + &-input{
  140 + display: inline-block;
  141 + height: @input-height-base;
  142 + line-height: @input-height-base;
  143 + padding: 0 24px 0 8px;
  144 + font-size: @font-size-base;
  145 + outline: none;
  146 + border: none;
  147 + box-sizing: border-box;
  148 + color: @input-color;
  149 + background-color: transparent;
  150 + position: relative;
  151 + cursor: pointer;
  152 + .placeholder();
  153 + }
  154 +
  155 + &-single &-input{
  156 + width: 100%;
  157 + }
  158 +
  159 + &-large &-input{
  160 + height: @input-height-large;
  161 + }
  162 +
  163 + &-small &-input{
  164 + height: @input-height-small;
  165 + }
  166 +}
  167 +
  168 +.@{select-item-prefix-cls} {
  169 + margin: 0;
  170 + padding: 7px 15px;
  171 + clear: both;
  172 + color: @text-color;
  173 + //border-radius: @btn-border-radius-small;
  174 + white-space: nowrap;
  175 + cursor: pointer;
  176 + .transition(background @transition-time @ease-in-out);
  177 +
  178 + &:hover{
  179 + background: @background-color-select-hover;
  180 + }
  181 +
  182 + &-focus {
  183 + background: @background-color-select-hover;
  184 + }
  185 +
  186 + &-disabled {
  187 + color: @btn-disable-color;
  188 + cursor: @cursor-disabled;
  189 +
  190 + &:hover {
  191 + color: @btn-disable-color;
  192 + background-color: #fff;
  193 + cursor: @cursor-disabled;
  194 + }
  195 + }
  196 +
  197 + &-selected ,&-selected:hover{
  198 + color: #fff;
  199 + background: @selected-color;
  200 + }
  201 +
  202 + &-selected&-focus {
  203 + background: shade(@selected-color, 10%);
  204 + }
  205 +}
  206 +
  207 +.@{select-prefix-cls}-multiple .@{select-item-prefix-cls} {
  208 + &-selected{
  209 + color: @selected-color;
  210 + background: #fff;
  211 + }
  212 + &-focus,&-selected:hover{
  213 + background: @background-color-select-hover;
  214 + }
  215 +
  216 + &-selected&-focus {
  217 + color: shade(@selected-color, 10%);
  218 + background: #fff;
  219 + }
  220 +
  221 + &-selected:after{
  222 + .ivu-icon();
  223 + float: right;
  224 + font-size: 24px;
  225 + content: '\F3FD';
  226 + color: @selected-color;
  227 + }
  228 +}
  229 +
  230 +.@{select-group-prefix-cls} {
  231 + list-style: none;
  232 + margin: 0;
  233 + padding: 0;
  234 +
  235 + &-title {
  236 + padding-left: 10px;
  237 + font-size: 12px;
  238 + color: @legend-color;
  239 + height: 30px;
  240 + line-height: 30px;
  241 + }
  242 +}
0 \ No newline at end of file 243 \ No newline at end of file
styles/themes/default/custom.less
@@ -10,6 +10,7 @@ @@ -10,6 +10,7 @@
10 @link-color : #0099e5; 10 @link-color : #0099e5;
11 @link-hover-color : tint(@link-color, 20%); 11 @link-hover-color : tint(@link-color, 20%);
12 @link-active-color : shade(@link-color, 5%); 12 @link-active-color : shade(@link-color, 5%);
  13 +@selected-color : fade(@primary-color, 90%);
13 14
14 // Base 15 // Base
15 @body-background : #fff; 16 @body-background : #fff;
@@ -28,7 +29,8 @@ @@ -28,7 +29,8 @@
28 @border-color-split : #e9e9e9; // inside 29 @border-color-split : #e9e9e9; // inside
29 30
30 // Background color 31 // Background color
31 -@background-color-base : #f7f7f7; // base 32 +@background-color-base : #f7f7f7; // base
  33 +@background-color-select-hover: @input-disabled-bg;
32 34
33 // Shadow 35 // Shadow
34 @shadow-color : rgba(100, 100, 100, .2); 36 @shadow-color : rgba(100, 100, 100, .2);
@@ -102,6 +104,7 @@ @@ -102,6 +104,7 @@
102 @zindex-spin : 8; 104 @zindex-spin : 8;
103 @zindex-affix : 10; 105 @zindex-affix : 10;
104 @zindex-back-top : 10; 106 @zindex-back-top : 10;
  107 +@zindex-select : 900;
105 @zindex-modal : 1000; 108 @zindex-modal : 1000;
106 @zindex-message : 1010; 109 @zindex-message : 1010;
107 @zindex-notification : 1010; 110 @zindex-notification : 1010;