From 5408210238dc01522537e3809c72f371e01ba108 Mon Sep 17 00:00:00 2001 From: toesbieya <1647775459@qq.com> Date: Sat, 16 May 2020 22:23:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A8=A1=E4=BB=BFquasar=EF=BC=8C=E7=BB=99messa?= =?UTF-8?q?ge=E6=B7=BB=E5=8A=A0=E4=BA=86group?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vue/src/assets/styles/element-ui.scss | 33 +++++++++ vue/src/components/Message/index.js | 102 ++++++++++++++++++++++++++ vue/src/components/Message/main.vue | 94 ++++++++++++++++++++++++ vue/src/main.js | 6 +- vue/src/utils/message.js | 3 +- vue/vue.config.js | 1 + 6 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 vue/src/components/Message/index.js create mode 100644 vue/src/components/Message/main.vue diff --git a/vue/src/assets/styles/element-ui.scss b/vue/src/assets/styles/element-ui.scss index f468ab6..9a2aa25 100644 --- a/vue/src/assets/styles/element-ui.scss +++ b/vue/src/assets/styles/element-ui.scss @@ -68,6 +68,21 @@ .el-message { @include deep-shadow; border-width: 0; + overflow: visible; + + &__badge { + animation: el-message-badge .42s; + padding: 4px 8px; + position: absolute; + box-shadow: 0 1px 3px rgba(#000, .2), 0 1px 1px rgba(#000, .14), 0 2px 1px -1px rgba(#000, .12); + background-color: $--color-danger; + color: #fff; + border-radius: 4px; + top: -10px; + left: -10px; + font-size: 12px; + line-height: 12px; + } .el-message__icon, .el-message__content { color: #fff !important; @@ -189,6 +204,24 @@ width: 100% !important; } +@keyframes el-message-badge { + 15% { + transform: translate3d(-25%, 0, 0) rotate3d(0, 0, 1, -5deg) + } + 30% { + transform: translate3d(20%, 0, 0) rotate3d(0, 0, 1, 3deg) + } + 45% { + transform: translate3d(-15%, 0, 0) rotate3d(0, 0, 1, -3deg) + } + 60% { + transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 2deg) + } + 75% { + transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -1deg) + } +} + diff --git a/vue/src/components/Message/index.js b/vue/src/components/Message/index.js new file mode 100644 index 0000000..e01e4b1 --- /dev/null +++ b/vue/src/components/Message/index.js @@ -0,0 +1,102 @@ +import Vue from 'vue' +import Main from './main.vue' +import {PopupManager} from 'element-ui/src/utils/popup' +import {isVNode} from 'element-ui/src/utils/vdom' +import {isEmpty} from "@/utils" + +let MessageConstructor = Vue.extend(Main) + +let instance +let instances = [] +let seed = 1 + +//键:options.type+'|'+options.message,值:message实例 +let groups = {} + +const Message = function (options) { + options = options || {} + if (typeof options === 'string') { + options = {message: options} + } + if (isEmpty(options.type)) { + options.type = 'info' + } + const group = options.type + '|' + options.message + const originalMessage = groups[group] + //若不是新的message + if (originalMessage) { + //旧实例的badge+1,并重新计时(如果有的话) + originalMessage.badge++ + originalMessage.clearTimer() + originalMessage.startTimer() + return originalMessage + } + + let userOnClose = options.onClose + let id = 'message_' + seed++ + + options.onClose = function () { + Message.close(id, userOnClose) + } + instance = new MessageConstructor({data: options}) + instance.id = id + instance.group = group + if (isVNode(instance.message)) { + instance.$slots.default = [instance.message] + instance.message = null + } + instance.$mount() + document.body.appendChild(instance.$el) + let verticalOffset = options.offset || 20 + instances.forEach(item => { + verticalOffset += item.$el.offsetHeight + 16 + }) + instance.verticalOffset = verticalOffset + instance.visible = true + instance.$el.style.zIndex = PopupManager.nextZIndex() + instances.push(instance) + groups[group] = instance + return instance +}; + +['success', 'warning', 'info', 'error'].forEach(type => { + Message[type] = options => { + if (typeof options === 'string') { + options = {message: options} + } + options.type = type + return Message(options) + } +}) + +Message.close = function (id, userOnClose) { + let len = instances.length + let index = -1 + let removedHeight + for (let i = 0; i < len; i++) { + if (id === instances[i].id) { + removedHeight = instances[i].$el.offsetHeight + index = i + if (typeof userOnClose === 'function') { + userOnClose(instances[i]) + } + delete groups[instances[i].group] + instances.splice(i, 1) + break + } + } + if (len <= 1 || index === -1 || index > instances.length - 1) return + for (let i = index; i < len - 1; i++) { + let dom = instances[i].$el + dom.style.top = + parseInt(dom.style.top, 10) - removedHeight - 16 + 'px' + } +} + +Message.closeAll = function () { + for (let i = instances.length - 1; i >= 0; i--) { + instances[i].close() + } +} + +export default Message diff --git a/vue/src/components/Message/main.vue b/vue/src/components/Message/main.vue new file mode 100644 index 0000000..b006359 --- /dev/null +++ b/vue/src/components/Message/main.vue @@ -0,0 +1,94 @@ + + + diff --git a/vue/src/main.js b/vue/src/main.js index 6f5a919..b59da2f 100644 --- a/vue/src/main.js +++ b/vue/src/main.js @@ -12,6 +12,9 @@ import * as filters from './filter' import BottomTip from '@/components/BottomTip' import Guide from '@/components/Guide' import ImageViewer from '@/components/ImageViewer' +import Message from '@/components/Message' + +Vue.use(Element) //注册过滤器 Object.keys(filters).forEach(key => { @@ -25,8 +28,7 @@ store.dispatch('socket/init', store.state.user).then() Vue.prototype.$bottomTip = BottomTip Vue.prototype.$guide = Guide Vue.prototype.$image = ImageViewer - -Vue.use(Element) +Vue.prototype.$message = Message Vue.config.productionTip = false diff --git a/vue/src/utils/message.js b/vue/src/utils/message.js index dfca801..25f8208 100644 --- a/vue/src/utils/message.js +++ b/vue/src/utils/message.js @@ -1,4 +1,5 @@ -import {Message, MessageBox} from 'element-ui' +import Message from '@/components/Message' +import {MessageBox} from 'element-ui' import {isEmpty} from "@/utils" export function elError(msg = '操作失败') { diff --git a/vue/vue.config.js b/vue/vue.config.js index 4358e9c..ceee59f 100644 --- a/vue/vue.config.js +++ b/vue/vue.config.js @@ -15,6 +15,7 @@ module.exports = { publicPath: './', outputDir: 'dist', assetsDir: 'static', + runtimeCompiler: true, lintOnSave: false, productionSourceMap: false, parallel: true,