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 @@
+
+
+
+
{{badge}}
+
+
+
+ {{ message }}
+
+
+
+
+
+
+
+
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,