diff --git a/src/main/resources/static/assets/js/message.js b/src/main/resources/static/assets/js/message.js new file mode 100644 index 0000000..d06b825 --- /dev/null +++ b/src/main/resources/static/assets/js/message.js @@ -0,0 +1,145 @@ +if (!window.Vue) { + const vueScript = document.createElement('script'); + vueScript.src = 'https://cdn.jsdelivr.net/npm/vue@3.3.4/dist/vue.global.prod.js'; + document.head.appendChild(vueScript); +} + +if (!document.querySelector('link[href*="bootstrap-icons"]')) { + const iconLink = document.createElement('link'); + iconLink.rel = 'stylesheet'; + iconLink.href = 'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css'; + document.head.appendChild(iconLink); +} + +const style = document.createElement('style'); +style.textContent = ` + .h5-message-container { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 9999; + pointer-events: none; + } + + .h5-message { + display: flex; + align-items: center; + padding: 14px 24px; + border-radius: 8px; + color: #fff; + font-size: 16px; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25); + animation: h5MsgShow 0.4s ease-out forwards; + } + .h5-message.success { background-color: rgba(40, 167, 69, 0.95); } + .h5-message.error { background-color: rgba(220, 53, 69, 0.95); } + .h5-message.warning { background-color: rgba(255, 193, 7, 0.95); color: #333; } + .h5-message.info { background-color: rgba(13, 110, 253, 0.95); } + .h5-message-icon { + margin-right: 10px; + font-size: 20px; + } + @keyframes h5MsgShow { + from { opacity: 0; transform: translate(-50%, -50%) scale(0.9); } + to { opacity: 1; transform: translate(-50%, -50%) scale(1); } + } + .h5-message.hide { animation: h5MsgHide 0.4s ease-in forwards; } + @keyframes h5MsgHide { + from { opacity: 1; transform: translate(-50%, -50%) scale(1); } + to { opacity: 0; transform: translate(-50%, -50%) scale(0.9); } + } +`; +document.head.appendChild(style); + +const messageTemplate = ` +
+ + {{ message }} +
+`; +const messageInstances = []; +function showMessage(options) { + if (typeof options === 'string') { + options = { message: options }; + } + + const config = { + message: '', + type: 'info', + duration: 3000, + ...options, + duration: Math.max(1000, options.duration || 3000) + }; + + const checkVueLoaded = setInterval(() => { + if (window.Vue) { + clearInterval(checkVueLoaded); + + const container = document.createElement('div'); + container.className = 'h5-message-container'; + document.body.appendChild(container); + const app = Vue.createApp({ + template: messageTemplate, + data() { + return { + message: config.message, + type: config.type, + visible: true + }; + }, + computed: { + iconClass() { + const icons = { + success: 'bi bi-check-circle', + error: 'bi bi-exclamation-circle', + warning: 'bi bi-exclamation-triangle', + info: 'bi bi-info-circle' + }; + return icons[this.type] || icons.info; + } + }, + mounted() { + const showTimer = setTimeout(() => { + this.visible = false; + const hideTimer = setTimeout(() => { + + const index = messageInstances.indexOf(app); + if (index > -1) { + messageInstances.splice(index, 1); + } + app.unmount(container); + document.body.removeChild(container); + clearTimeout(hideTimer); + }, 400); + + clearTimeout(showTimer); + }, config.duration); + } + }); + const instance = app.mount(container); + messageInstances.push(instance); + } + }, 50); + + setTimeout(() => { + clearInterval(checkVueLoaded); + if (!window.Vue) { + alert(config.message); + } + }, 1000); +} + + +window.$message = showMessage; + +['success', 'error', 'warning', 'info'].forEach(type => { + window.$message[type] = (message, duration) => { + window.$message({ + message, + type, + // 确保时长有效(用户未传则用默认3秒) + duration: duration ? Math.max(1000, duration) : 500 + }); + }; +}); \ No newline at end of file