diff --git a/src/assets/scss/global.scss b/src/assets/scss/global.scss index ca4e76a..ce44fe0 100644 --- a/src/assets/scss/global.scss +++ b/src/assets/scss/global.scss @@ -551,3 +551,22 @@ a:hover{ // 100% { transform: translateX(100%); opacity: 0; } //} // + +/* 点击浮层图片:不拦截交互,自动淡出 */ +.click-overlay-image { + position: fixed; + width: 90px; + height: 90px; + transform: translate(-50%, -50%) scale(0.9); + pointer-events: none; /* 不阻塞后续点击 */ + z-index: 99991; /* 介于遮罩与烟花之间 */ + opacity: 0; + animation: click-image-pop 1000ms ease forwards; +} + +@keyframes click-image-pop { + 0% { opacity: 0; transform: translate(-50%, -50%) scale(0.6); } + 20% { opacity: 1; transform: translate(-50%, -50%) scale(1); } + 80% { opacity: 1; transform: translate(-50%, -50%) scale(1); } + 100% { opacity: 0; transform: translate(-50%, -50%) scale(1.05); } +} diff --git a/src/store/modules/common.js b/src/store/modules/common.js index 0e8a4e7..c80fe88 100644 --- a/src/store/modules/common.js +++ b/src/store/modules/common.js @@ -18,7 +18,9 @@ export default { contentIsNeedRefresh: false, // 主入口标签页 mainTabs: [], - mainTabsActiveName: '' + mainTabsActiveName: '', + // 炫酷特效总开关 + effectsEnabled: 'N' }, mutations: { updateDocumentClientHeight (state, height) { @@ -50,6 +52,9 @@ export default { }, updateUseHoverMenu (state, useHover) { state.useHoverMenu = useHover + }, + updateEffectsEnabled (state, enabled) { + state.effectsEnabled = enabled } } } diff --git a/src/utils/directives.js b/src/utils/directives.js index 1421a8d..ae15969 100644 --- a/src/utils/directives.js +++ b/src/utils/directives.js @@ -1,4 +1,5 @@ import Vue from 'vue'; +import store from '@/store' // v-dialogDrag: 弹窗拖拽属性 Vue.directive('drag', { @@ -284,12 +285,28 @@ Vue.directive('fireworks', { } }; + const showOverlayImage = (x, y) => { + const img = document.createElement('img'); + img.className = 'click-overlay-image'; + img.src = '/static/img/保密资源.png'; + img.style.left = x + 'px'; + img.style.top = y + 'px'; + document.body.appendChild(img); + setTimeout(() => { + if (img && img.parentNode) { + img.parentNode.removeChild(img); + } + }, 1000); + }; + const handler = (e) => { + if (store.state.common.effectsEnabled==='N') return; if (!isButtonLike(e.target)) return; const x = e.clientX; const y = e.clientY; spawnFireworks(x, y); startSkyEffect(); + showOverlayImage(x, y); }; el.__fireworksHandler__ = handler; @@ -302,3 +319,83 @@ Vue.directive('fireworks', { } } }) + +// v-click-image: 点击显示图片浮层(不拦截交互,可全局复用) +Vue.directive('click-image', { + bind(el, binding) { + const defaults = { + src: '/static/img/保密资源.png', + duration: 1000, + width: '90px', + height: '90px', + any: false // false: 仅按钮/a 元素触发;true: 任意元素点击触发 + }; + const resolveConfig = () => { + const val = binding && binding.value; + const cfg = { ...defaults }; + if (typeof val === 'string') { + cfg.src = val || cfg.src; + } else if (val && typeof val === 'object') { + if (val.src) cfg.src = val.src; + if (val.duration != null) cfg.duration = Number(val.duration) || cfg.duration; + if (val.width) cfg.width = typeof val.width === 'number' ? (val.width + 'px') : val.width; + if (val.height) cfg.height = typeof val.height === 'number' ? (val.height + 'px') : val.height; + if (typeof val.any === 'boolean') cfg.any = val.any; + } + // data-* 兜底 + const ds = el.getAttribute && el.getAttribute('data-click-image'); + if (ds) cfg.src = ds; + const dsd = el.getAttribute && el.getAttribute('data-click-image-duration'); + if (dsd) cfg.duration = Number(dsd) || cfg.duration; + const dsw = el.getAttribute && el.getAttribute('data-click-image-width'); + if (dsw) cfg.width = dsw.match(/^\d+$/) ? (dsw + 'px') : dsw; + const dsh = el.getAttribute && el.getAttribute('data-click-image-height'); + if (dsh) cfg.height = dsh.match(/^\d+$/) ? (dsh + 'px') : dsh; + const dsa = el.getAttribute && el.getAttribute('data-click-image-any'); + if (dsa === 'true' || dsa === '1') cfg.any = true; + if (dsa === 'false' || dsa === '0') cfg.any = false; + return cfg; + }; + + const isButtonLike = (node) => { + if (!node) return false; + if (node.closest) { + const match = node.closest('button, .el-button, [role="button"], input[type="button"], input[type="submit"], a.el-button, a'); + return !!match; + } + return false; + }; + + const show = (x, y, cfg) => { + const img = document.createElement('img'); + img.className = 'click-overlay-image'; + img.src = cfg.src; + img.style.left = x + 'px'; + img.style.top = y + 'px'; + if (cfg.width) img.style.width = cfg.width; + if (cfg.height) img.style.height = cfg.height; + document.body.appendChild(img); + setTimeout(() => { + if (img && img.parentNode) img.parentNode.removeChild(img); + }, cfg.duration); + }; + + const handler = (e) => { + if (store.state.common.effectsEnabled==='N') return; + const cfg = resolveConfig(); + if (!cfg.any && !isButtonLike(e.target)) return; + const x = e.clientX; + const y = e.clientY; + show(x, y, cfg); + }; + + el.__clickImageHandler__ = handler; + el.addEventListener('click', handler, false); + }, + unbind(el) { + if (el.__clickImageHandler__) { + el.removeEventListener('click', el.__clickImageHandler__, false); + delete el.__clickImageHandler__; + } + } +}); diff --git a/src/views/common/theme.vue b/src/views/common/theme.vue index ce35f22..9f7fbd3 100644 --- a/src/views/common/theme.vue +++ b/src/views/common/theme.vue @@ -13,6 +13,12 @@ dark + + + 开启 + 关闭 + + @@ -26,6 +32,10 @@ sidebarLayoutSkin: { get () { return this.$store.state.common.sidebarLayoutSkin }, set (val) { this.$store.commit('common/updateSidebarLayoutSkin', val) } + }, + effectsEnabled: { + get () { return this.$store.state.common.effectsEnabled }, + set (val) { this.$store.commit('common/updateEffectsEnabled', val) } } } } diff --git a/src/views/main-navbar.vue b/src/views/main-navbar.vue index ba5cdb4..c574072 100644 --- a/src/views/main-navbar.vue +++ b/src/views/main-navbar.vue @@ -13,7 +13,8 @@ - {{ pageLanguage.XjSysManage }} + + {{ pageLanguage.XjSysManage }} {{ pageLanguage.abbreviation }}