diff --git a/bin/build-entry.js b/bin/build-entry.js index 4cbd41f8d..77e0b5ee2 100644 --- a/bin/build-entry.js +++ b/bin/build-entry.js @@ -21,6 +21,7 @@ const install = function(Vue) { Vue.prototype.$confirm = MessageBox.confirm; Vue.prototype.$prompt = MessageBox.prompt; Vue.prototype.$notify = Notification; + Vue.prototype.$message = Message; }; // auto install diff --git a/components.json b/components.json index 3f311394d..1aedb8f28 100644 --- a/components.json +++ b/components.json @@ -1,7 +1,4 @@ { - "group": [ - "./packages/group/index.js" - ], "select-dropdown": [ "./packages/select-dropdown/index.js" ], @@ -148,5 +145,8 @@ ], "spinner": [ "./packages/spinner/index.js" + ], + "message": [ + "./packages/message/index.js" ] } diff --git a/examples/docs/checkbox.md b/examples/docs/checkbox.md index 619ec4213..310343c51 100644 --- a/examples/docs/checkbox.md +++ b/examples/docs/checkbox.md @@ -41,22 +41,26 @@ ### 多个勾选框,绑定到同一个数组
- - - - - + + + + + + +

{{ checkList }}

```html + + + +## 基本用法 + +
+ 打开消息提示 +
+ +```html + + + +``` + +## 不同状态 +
+ 成功 + 警告 + 错误 +
+ +```html + + + +``` + +## 可关闭 +
+ 消息 + 成功 + 警告 + 错误 +
+ +```html + + + +``` + +## 全局方法 + +element 为 Vue.prototype 添加了全局方法 $message。因此在 vue instance 中可以采用本页面中的方式调用 `Message`。 + +## 单独引用 + +单独引入 `Message`: + +```javascript +import { Message } from 'element-ui'; +``` + +此时调用方法为 `Message(options)`。 + +## API +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|---------- |-------------- |---------- |-------------------------------- |-------- | +| message | 消息文字 | string | | | +| type | 主题 | string | 'success', 'warning', 'info', 'error' | 'info' | +| duration | 显示时间, 毫秒。设为 0 则不会自动关闭 | number | | 3000 | +| showClose | 是否显示关闭按钮 | boolean | | false | +| onClose | 关闭时的回调函数, 参数为被关闭的 message 实例 | function | | | diff --git a/examples/docs/pagination.md b/examples/docs/pagination.md index 58344f70e..c51d709b9 100644 --- a/examples/docs/pagination.md +++ b/examples/docs/pagination.md @@ -50,8 +50,8 @@ @@ -85,6 +85,10 @@ | current-page | 当前页数 | Number | | 0| | layout | 组件布局,子组件名用逗号分隔。| String | `prev`, `pager`, `next`, `jumper`, `slot`, `->`, `total` | 'prev, pager, next, jumper, slot, ->, total' | | page-sizes | 切换每页显示个数的子组件值 | Number[] | | [10, 20, 30, 40, 50, 100] | -| size-change | pageSize 改变时会触发的事件 | Function | | | -| current-change | currentPage 改变时会触发的事件 | Function | | | + +## 事件 +| 事件名称 | 说明 | 回调函数 | +|---------|--------|---------| +| sizechange | pageSize 改变时会触发 | `size` | +| currentchange | currentPage 改变时会触发 | `currentPage` | diff --git a/examples/docs/radio.md b/examples/docs/radio.md index 598dff6a1..862dfa7e5 100644 --- a/examples/docs/radio.md +++ b/examples/docs/radio.md @@ -16,16 +16,16 @@ ## 基本用法
- - - + + +
```html diff --git a/packages/dialog/package.json b/packages/dialog/package.json index a223e6050..aff2f4a59 100644 --- a/packages/dialog/package.json +++ b/packages/dialog/package.json @@ -12,6 +12,6 @@ "author": "elemefe", "license": "MIT", "devDependencies": { - "vue-popup": "^0.2.1" + "vue-popup": "^0.2.2" } } diff --git a/packages/message-box/package.json b/packages/message-box/package.json index 1d36034cf..64d1610ef 100644 --- a/packages/message-box/package.json +++ b/packages/message-box/package.json @@ -12,6 +12,6 @@ "author": "elemefe", "license": "MIT", "dependencies": { - "vue-popup": "^0.2.1" + "vue-popup": "^0.2.2" } } diff --git a/packages/message/cooking.conf.js b/packages/message/cooking.conf.js new file mode 100644 index 000000000..e9dcae592 --- /dev/null +++ b/packages/message/cooking.conf.js @@ -0,0 +1,31 @@ +var cooking = require('cooking'); +var path = require('path'); + +cooking.set({ + entry: { + index: path.join(__dirname, 'index.js') + }, + dist: path.join(__dirname, 'lib'), + template: false, + format: 'umd', + moduleName: 'ElMessage', + extractCSS: 'style.css', + + extends: ['vue', 'saladcss'] +}); + +cooking.add('resolve.alias', { + 'main': path.join(__dirname, '../../src'), + 'packages': path.join(__dirname, '../../packages') +}); + +cooking.add('externals', { + vue: { + root: 'Vue', + commonjs: 'vue', + commonjs2: 'vue', + amd: 'vue' + } +}); + +module.exports = cooking.resolve(); diff --git a/packages/message/index.js b/packages/message/index.js new file mode 100644 index 000000000..fe700f641 --- /dev/null +++ b/packages/message/index.js @@ -0,0 +1,3 @@ +import Message from './src/main.js'; + +module.exports = Message; diff --git a/packages/message/package.json b/packages/message/package.json new file mode 100644 index 000000000..116095fdf --- /dev/null +++ b/packages/message/package.json @@ -0,0 +1,15 @@ +{ + "name": "el-message", + "version": "0.0.0", + "description": "A message component for Vue.js.", + "keywords": [ + "element", + "vue", + "component" + ], + "main": "./lib/index.js", + "repository": "https://github.com/element-component/element/tree/master/packages/message", + "author": "elemefe", + "license": "MIT", + "dependencies": {} +} diff --git a/packages/message/src/main.js b/packages/message/src/main.js new file mode 100644 index 000000000..9b67a5a78 --- /dev/null +++ b/packages/message/src/main.js @@ -0,0 +1,57 @@ +import Vue from 'vue'; +let MessageConstructor = Vue.extend(require('./main.vue')); + +let instance; +let instances = []; +let seed = 1; + +var Message = function(options) { + options = options || {}; + let userOnClose = options.onClose; + let id = 'message_' + seed++; + + options.onClose = function() { + Message.close(id, userOnClose); + }; + + instance = new MessageConstructor({ + data: options + }); + instance.id = id; + instance.vm = instance.$mount(); + document.body.appendChild(instance.vm.$el); + instance.vm.visible = true; + instance.dom = instance.vm.$el; + + let topDist = 0; + for (let i = 0, len = instances.length; i < len; i++) { + topDist += instances[i].$el.offsetHeight + 20; + } + topDist += 20; + instance.top = topDist; + instances.push(instance); +}; + +Message.close = function(id, userOnClose) { + let index; + let removedHeight; + for (var i = 0, len = instances.length; i < len; i++) { + if (id === instances[i].id) { + if (typeof userOnClose === 'function') { + userOnClose(instances[i]); + } + index = i; + removedHeight = instances[i].dom.offsetHeight; + instances.splice(i, 1); + break; + } + } + + if (len > 1) { + for (i = index; i < len - 1 ; i++) { + instances[i].dom.style.top = parseInt(instances[i].dom.style.top, 10) - removedHeight - 20 + 'px'; + } + } +}; + +export default Message; diff --git a/packages/message/src/main.vue b/packages/message/src/main.vue new file mode 100644 index 000000000..db4787025 --- /dev/null +++ b/packages/message/src/main.vue @@ -0,0 +1,81 @@ + + + \ No newline at end of file diff --git a/packages/pagination/src/pager.vue b/packages/pagination/src/pager.vue index a5a7dd004..879da2ea0 100644 --- a/packages/pagination/src/pager.vue +++ b/packages/pagination/src/pager.vue @@ -71,7 +71,7 @@ } if (newPage !== currentPage) { - this.$emit('currentChange', newPage); + this.$emit('currentchange', newPage); } } }, diff --git a/packages/pagination/src/pagination.js b/packages/pagination/src/pagination.js index 82a7b56d2..a4c0cf6e0 100644 --- a/packages/pagination/src/pagination.js +++ b/packages/pagination/src/pagination.js @@ -49,7 +49,7 @@ export default { const TEMPLATE_MAP = { prev: , jumper: , - pager: , + pager: , next: , sizes: , slot: , @@ -114,6 +114,12 @@ export default { }, Sizes: { + created() { + if (Array.isArray(this.$parent.pageSizes)) { + this.$parent.internalPageSize = this.$parent.pageSizes[0]; + } + }, + render(h) { return ( @@ -144,7 +150,7 @@ export default { handleChange(val) { if (val !== this.$parent.internalPageSize) { this.$parent.internalPageSize = val = parseInt(val, 10); - this.$parent.$emit('size-change', val); + this.$parent.$emit('sizechange', val); } } } @@ -167,7 +173,7 @@ export default { this.$parent.internalCurrentPage = this.$parent.getValidCurrentPage(target.value); if (target.value !== this.oldValue && Number(target.value) === this.$parent.internalCurrentPage) { - this.$parent.$emit('current-change', this.$parent.internalCurrentPage); + this.$parent.$emit('currentchange', this.$parent.internalCurrentPage); } this.oldValue = null; @@ -209,7 +215,7 @@ export default { methods: { handleCurrentChange(val) { this.internalCurrentPage = this.getValidCurrentPage(val); - this.$emit('current-change', this.internalCurrentPage); + this.$emit('currentchange', this.internalCurrentPage); }, prev() { @@ -218,7 +224,7 @@ export default { this.internalCurrentPage = this.getValidCurrentPage(newVal); if (this.internalCurrentPage !== oldPage) { - this.$emit('current-change', this.internalCurrentPage); + this.$emit('currentchange', this.internalCurrentPage); } }, @@ -228,7 +234,7 @@ export default { this.internalCurrentPage = this.getValidCurrentPage(newVal); if (this.internalCurrentPage !== oldPage) { - this.$emit('current-change', this.internalCurrentPage); + this.$emit('currentchange', this.internalCurrentPage); } }, @@ -238,7 +244,7 @@ export default { this.internalCurrentPage = this.getValidCurrentPage(newVal); if (this.internalCurrentPage !== oldPage) { - this.$emit('current-change', this.internalCurrentPage); + this.$emit('currentchange', this.internalCurrentPage); } }, @@ -248,7 +254,7 @@ export default { this.internalCurrentPage = this.getValidCurrentPage(newVal); if (this.internalCurrentPage !== oldPage) { - this.$emit('current-change', this.internalCurrentPage); + this.$emit('currentchange', this.internalCurrentPage); } }, diff --git a/packages/radio/src/radio-button.vue b/packages/radio/src/radio-button.vue index 8c27e1541..1cf0e235e 100644 --- a/packages/radio/src/radio-button.vue +++ b/packages/radio/src/radio-button.vue @@ -17,7 +17,7 @@ return this.$parent.value; }, set(newValue) { - this.$parent.value = newValue; + this.$parent.$emit('input', newValue); } } }, @@ -44,7 +44,7 @@ :disabled="disabled"> - + diff --git a/packages/radio/src/radio-group.vue b/packages/radio/src/radio-group.vue index d232025bb..4850ba4cd 100644 --- a/packages/radio/src/radio-group.vue +++ b/packages/radio/src/radio-group.vue @@ -4,18 +4,17 @@ export default { name: 'ElRadioGroup', + componentName: 'radio-group', + mixins: [emitter], props: { - value: { - default: '', - twoWay: true, - required: true - }, + value: [String, Number], size: String }, watch: { value(value) { + this.$emit('change', value); this.dispatch('form-item', 'el.form.change', [this.value]); } } diff --git a/packages/radio/src/radio.vue b/packages/radio/src/radio.vue index 5906bc097..db5d74eab 100644 --- a/packages/radio/src/radio.vue +++ b/packages/radio/src/radio.vue @@ -19,7 +19,7 @@ - + @@ -28,9 +28,7 @@ name: 'ElRadio', props: { - value: { - twoWay: true - }, + value: [String, Number], label: { type: [String, Number], required: true @@ -43,11 +41,6 @@ focus: false }; }, - watch: { - value(value) { - this.$emit('onchange', value); - } - }, computed: { _value: { get() { @@ -55,9 +48,9 @@ }, set(newValue) { if (this.value !== undefined) { - this.value = newValue; + this.$emit('input', newValue); } else { - this.$parent.value = newValue; + this.$parent.$emit('input', newValue); } } } diff --git a/packages/select/src/select.vue b/packages/select/src/select.vue index 74d7fd22f..bff1ef3e0 100644 --- a/packages/select/src/select.vue +++ b/packages/select/src/select.vue @@ -281,6 +281,7 @@ visible(val) { if (!val) { + this.$refs.reference.$el.querySelector('input').blur(); this.$el.querySelector('.el-input__icon').classList.remove('is-reverse'); this.broadcast('select-dropdown', 'destroyPopper'); if (this.$refs.input) { @@ -423,6 +424,9 @@ }, toggleMenu() { + if (this.filterable && this.query === '' && this.visible) { + return; + } if (!this.disabled) { this.visible = !this.visible; if (this.remote && this.visible) { @@ -472,7 +476,9 @@ }, selectOption() { - this.handleOptionSelect(this.options[this.hoverIndex]); + if (this.options[this.hoverIndex]) { + this.handleOptionSelect(this.options[this.hoverIndex]); + } }, deleteSelected(event) { diff --git a/packages/tabs/src/tab-pane.vue b/packages/tabs/src/tab-pane.vue index 888001f66..7f7cc5afc 100644 --- a/packages/tabs/src/tab-pane.vue +++ b/packages/tabs/src/tab-pane.vue @@ -7,9 +7,7 @@ type: String, required: true }, - key: { - type: String - } + name: String }, data() { @@ -18,7 +16,8 @@ transition: '', paneStyle: { position: 'relative' - } + }, + key: '' }; }, @@ -28,17 +27,20 @@ } }, - events: { - }, - computed: { show() { - return this.$parent.activeKey === this.key; + return this.$parent.currentName === this.key; } }, watch: { - '$parent.activeKey'(newValue, oldValue) { + name: { + immediate: true, + handler(val) { + this.key = val; + } + }, + '$parent.currentName'(newValue, oldValue) { if (this.key === newValue) { this.transition = newValue > oldValue ? 'slideInRight' : 'slideInLeft'; } diff --git a/packages/tabs/src/tab.vue b/packages/tabs/src/tab.vue index e1a66f45b..ea35ec505 100644 --- a/packages/tabs/src/tab.vue +++ b/packages/tabs/src/tab.vue @@ -8,20 +8,23 @@ required: true }, closable: Boolean - }, - data() { - return { - showClose: false - }; } }; diff --git a/packages/tabs/src/tabs.vue b/packages/tabs/src/tabs.vue index ab371f641..1e8b70588 100644 --- a/packages/tabs/src/tabs.vue +++ b/packages/tabs/src/tabs.vue @@ -11,12 +11,8 @@ props: { type: String, tabPosition: String, - defaultActiveKey: { - type: String - }, - activeKey: { - type: String - }, + defaultActiveName: String, + activeName: String, closable: false, tabWidth: 0 }, @@ -25,34 +21,22 @@ return { tabs: [], children: null, - activeTab: null + activeTab: null, + currentName: 0, + barStyle: '' }; }, - computed: { - barStyle: { - cache: false, - get() { - if (this.type) return {}; - var style = {}; - var offset = 0; - var tabWidth = 0; - - this.tabs.every((tab, index) => { - let $el = this.$refs.tabs[index].$el; - if (tab.key !== this.activeKey) { - offset += $el.clientWidth; - return true; - } else { - tabWidth = $el.clientWidth; - return false; - } - }); - - style.width = tabWidth + 'px'; - style.transform = `translateX(${offset}px)`; - return style; + watch: { + activeName: { + immediate: true, + handler(val) { + this.currentName = val || 0; } + }, + + 'currentName'() { + this.calcBarStyle(); } }, @@ -67,27 +51,49 @@ this.tabs.splice(index, 1); } - if (tab.key === this.activeKey) { + if (tab.key === this.currentName) { let deleteIndex = this.$children.indexOf(tab); let nextChild = this.$children[deleteIndex + 1]; let prevChild = this.$children[deleteIndex - 1]; - this.activeKey = nextChild ? nextChild.key : prevChild ? prevChild.key : '-1'; + this.currentName = nextChild ? nextChild.key : prevChild ? prevChild.key : '-1'; } this.$emit('tab.remove', tab); }, handleTabClick(tab) { - this.activeKey = tab.key; + this.currentName = tab.key; this.$emit('tab.click', tab); + }, + calcBarStyle() { + if (this.type) return {}; + var style = {}; + var offset = 0; + var tabWidth = 0; + + this.tabs.every((tab, index) => { + let $el = this.$refs.tabs[index].$el; + if (tab.key !== this.currentName) { + offset += $el.clientWidth; + return true; + } else { + tabWidth = $el.clientWidth; + return false; + } + }); + + style.width = tabWidth + 'px'; + style.transform = `translateX(${offset}px)`; + + this.barStyle = style; } }, - ready() { - if (!this.activeKey) { - this.activeKey = this.defaultActiveKey || this.$children[0].key; + + mounted() { + if (!this.currentName) { + this.currentName = this.defaultActiveName || this.$children[0].key; } - this.$children.forEach(tab => { - this.tabs.push(tab); - }); + this.$children.forEach(tab => this.tabs.push(tab)); + this.$nextTick(() => this.calcBarStyle()); } }; @@ -97,13 +103,17 @@
-
+ @click.native="handleTabClick(tab)"> + +
+
diff --git a/packages/theme-default/src/index.css b/packages/theme-default/src/index.css index f0900aa88..806a04761 100644 --- a/packages/theme-default/src/index.css +++ b/packages/theme-default/src/index.css @@ -15,6 +15,7 @@ @import "./popover.css"; @import "./tooltip.css"; @import "./autocomplete.css"; +@import "./message.css"; @import "./message-box.css"; @import "./date-picker.css"; @import "./time-picker.css"; diff --git a/packages/theme-default/src/message.css b/packages/theme-default/src/message.css new file mode 100644 index 000000000..b7647011f --- /dev/null +++ b/packages/theme-default/src/message.css @@ -0,0 +1,72 @@ +@charset "UTF-8"; +@import "./common/var.css"; + +@component-namespace el { + + @b message { + box-shadow:0 2px 4px 0 rgba(0, 0, 0, .12), 0 0 6px 0 rgba(0, 0, 0, .04); + width: 300px; + padding: 10px 12px; + box-sizing: border-box; + border-radius: 2px; + position: fixed; + left: 50%; + transform: translateX(-50%); + background-color: #fff; + transition: opacity 0.3s, top 0.4s; + overflow: hidden; + z-index: 1000; + + @e group { + margin-left: 28px; + position: relative; + + & p { + font-size: 14px; + line-height: 20px; + margin: 0 20px 0 0; + color: #8492a6; + text-align: justify; + } + } + + @e icon { + size: 20px; + font-size: 20px; + float: left; + position: relative; + } + + @e closeBtn { + position: absolute 3px 0 * *; + cursor: pointer; + color: #C0CCDA; + font-size: 14px; + + &:hover { + color: #99A9BF; + } + } + + & .el-icon-circle-check { + color: #13ce66; + } + + & .el-icon-circle-cross { + color: #ff4949; + } + + & .el-icon-information { + color: #50bfff; + } + + & .el-icon-warning { + color: #f7ba2a; + } + } + + .el-message-fade-enter, + .el-message-fade-leave-active { + opacity: 0; + } +} diff --git a/packages/tree/src/tree-node.vue b/packages/tree/src/tree-node.vue index 4041002f2..7e11f8cab 100644 --- a/packages/tree/src/tree-node.vue +++ b/packages/tree/src/tree-node.vue @@ -6,7 +6,7 @@ - + {{ node.label }}
diff --git a/src/index.js b/src/index.js index 878bd4d9f..fb956bc4f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,3 @@ -import Group from '../packages/group/index.js'; import SelectDropdown from '../packages/select-dropdown/index.js'; import Pagination from '../packages/pagination/index.js'; import Dialog from '../packages/dialog/index.js'; @@ -48,11 +47,11 @@ import Col from '../packages/col/index.js'; import Upload from '../packages/upload/index.js'; import Progress from '../packages/progress/index.js'; import Spinner from '../packages/spinner/index.js'; +import Message from '../packages/message/index.js'; const install = function(Vue) { if (install.installed) return; - Vue.component(Group.name, Group); Vue.component(SelectDropdown.name, SelectDropdown); Vue.component(Pagination.name, Pagination); Vue.component(Dialog.name, Dialog); @@ -99,6 +98,7 @@ const install = function(Vue) { Vue.component(Upload.name, Upload); Vue.component(Progress.name, Progress); Vue.component(Spinner.name, Spinner); + Vue.component(Message.name, Message); Vue.use(Loading); @@ -107,6 +107,7 @@ const install = function(Vue) { Vue.prototype.$confirm = MessageBox.confirm; Vue.prototype.$prompt = MessageBox.prompt; Vue.prototype.$notify = Notification; + Vue.prototype.$message = Message; }; // auto install @@ -116,7 +117,6 @@ if (typeof window !== 'undefined' && window.Vue) { module.exports = { install, - Group, SelectDropdown, Pagination, Dialog, @@ -165,5 +165,6 @@ module.exports = { Col, Upload, Progress, - Spinner + Spinner, + Message };