@@ -2088,3 +2088,254 @@ Date.prototype.format = function(format) {
20882088} ;
20892089
20902090$ . suiAlert = function ( i ) { function t ( ) { l = setTimeout ( function ( ) { c . transition ( { animation :e , duration :"2s" , onComplete :function ( ) { c . remove ( ) } } ) } , 1e3 * o . time ) } var o = $ . extend ( { title :"Semantic UI Alerts" , description :"semantic ui alerts library" , type :"error" , time :5 , position :"top-right" , icon :! 1 } , i ) ; o . icon === ! 1 && ( "info" == o . type ?o . icon = "announcement" :"success" == o . type ?o . icon = "checkmark" :"error" == o . type ?o . icon = "remove" :"warning" == o . type && ( o . icon = "warning circle" ) ) ; var e = "drop" ; "top-right" == o . position ?e = "fly left" :"top-center" == o . position ?e = "fly down" :"top-left" == o . position ?e = "fly right" :"bottom-right" == o . position ?e = "fly left" :"bottom-center" == o . position ?e = "fly up" :"bottom-left" == o . position && ( e = "fly right" ) ; var n = "" , r = $ ( window ) . width ( ) ; r < 425 && ( n = "mini" ) ; var s = "ui-alerts." + o . position ; $ ( "body > ." + s ) . length || $ ( "body" ) . append ( '<div class="ui-alerts ' + o . position + '"></div>' ) ; var c = $ ( '<div class="ui icon floating ' + n + " message " + o . type + '" id="alert"> <i class="' + o . icon + ' icon"></i> <i class="close icon" id="alertclose"></i> <div class="content"> <div class="header">' + o . title + "</div> <p>" + o . description + "</p> </div> </div>" ) ; $ ( "." + s ) . prepend ( c ) , c . transition ( "pulse" ) , $ ( "#alertclose" ) . on ( "click" , function ( ) { $ ( this ) . closest ( "#alert" ) . transition ( { animation :e , onComplete :function ( ) { c . remove ( ) } } ) } ) ; var l = 0 ; $ ( c ) . mouseenter ( function ( ) { clearTimeout ( l ) } ) . mouseleave ( function ( ) { t ( ) } ) , t ( ) } ;
2091+
2092+ // ==================== Tippy.js 服务器信息弹窗相关功能 ====================
2093+
2094+ // 更新当前显示的popup内容
2095+ function updateVisibleTippyContent ( ) {
2096+ if ( window . tippyInstances ) {
2097+ window . tippyInstances . forEach ( instance => {
2098+ if ( instance . state . isVisible ) {
2099+ const contentDiv = instance . reference . nextElementSibling ;
2100+ if ( contentDiv && contentDiv . classList . contains ( 'server-popup-content' ) ) {
2101+ const dynamicContent = contentDiv . innerHTML ;
2102+ instance . setContent ( dynamicContent ) ;
2103+ }
2104+ }
2105+ } ) ;
2106+ }
2107+ }
2108+
2109+ // 全局 Tippy.js 初始化(完全与 Vue 响应式系统解耦)
2110+ function initGlobalTippyPopups ( ) {
2111+ // 检查Tippy.js是否已加载
2112+ if ( typeof tippy === 'undefined' ) {
2113+ console . warn ( 'Tippy.js not loaded, skipping popup initialization' ) ;
2114+ return ;
2115+ }
2116+
2117+ // 完全销毁现有的 tippy 实例,避免重复初始化
2118+ if ( window . tippyInstances ) {
2119+ window . tippyInstances . forEach ( instance => {
2120+ try {
2121+ instance . destroy ( ) ;
2122+ } catch ( e ) {
2123+ console . warn ( 'Failed to destroy tippy instance:' , e ) ;
2124+ }
2125+ } ) ;
2126+ }
2127+ window . tippyInstances = [ ] ;
2128+
2129+ // 获取所有触发元素(支持动态添加)
2130+ const triggers = document . querySelectorAll ( '[data-server-popup]' ) ;
2131+
2132+ triggers . forEach ( trigger => {
2133+ // 如果已经初始化过,跳过
2134+ if ( trigger . _tippyInitialized ) {
2135+ return ;
2136+ }
2137+
2138+ const contentDiv = trigger . nextElementSibling ;
2139+ if ( contentDiv && contentDiv . classList . contains ( 'server-popup-content' ) ) {
2140+ // 使用 Tippy.js 创建 tooltip,关键是设置为手动模式,不自动隐藏
2141+ const instance = tippy ( trigger , {
2142+ content : 'Loading...' , // 初始内容,将动态更新
2143+ allowHTML : true ,
2144+ interactive : true , // 允许与popup内容交互
2145+ trigger : 'manual' , // 完全手动控制
2146+ hideOnClick : false ,
2147+ delay : 0 , // 无延迟
2148+ duration : 0 , // 无动画
2149+ placement : 'auto' ,
2150+ theme : 'custom-server-popup' ,
2151+ maxWidth : 'none' ,
2152+ arrow : true ,
2153+ appendTo : document . body ,
2154+ boundary : 'viewport' ,
2155+ flip : true ,
2156+ fallbackPlacements : [ 'top' , 'bottom' , 'left' , 'right' ] ,
2157+ sticky : false ,
2158+ interactiveBorder : 0 ,
2159+ onShow ( instance ) {
2160+ // 隐藏其他所有 popup
2161+ window . tippyInstances . forEach ( other => {
2162+ if ( other !== instance && other . state . isVisible ) {
2163+ other . hide ( ) ;
2164+ }
2165+ } ) ;
2166+
2167+ // 动态更新内容 - 每次显示时都获取最新内容
2168+ const contentDiv = instance . reference . nextElementSibling ;
2169+ if ( contentDiv && contentDiv . classList . contains ( 'server-popup-content' ) ) {
2170+ // 强制Vue渲染最新内容
2171+ if ( window . statusCards && window . statusCards . $forceUpdate ) {
2172+ window . statusCards . $forceUpdate ( ) ;
2173+ }
2174+
2175+ // 短暂延迟确保Vue渲染完成
2176+ setTimeout ( ( ) => {
2177+ const dynamicContent = contentDiv . innerHTML ;
2178+ instance . setContent ( dynamicContent ) ;
2179+ } , 10 ) ;
2180+ }
2181+ }
2182+ } ) ;
2183+
2184+ // 手动绑定鼠标事件 - 确保不会自动隐藏
2185+ let isHovering = false ;
2186+ let mouseoverTimeout = null ;
2187+
2188+ const showPopup = function ( ) {
2189+ if ( mouseoverTimeout ) {
2190+ clearTimeout ( mouseoverTimeout ) ;
2191+ mouseoverTimeout = null ;
2192+ }
2193+ isHovering = true ;
2194+ instance . show ( ) ;
2195+ } ;
2196+
2197+ const hidePopup = function ( ) {
2198+ isHovering = false ;
2199+ // 延迟200ms隐藏,给足够时间让鼠标移动到popup上
2200+ mouseoverTimeout = setTimeout ( ( ) => {
2201+ if ( ! isHovering && instance . state . isVisible ) {
2202+ instance . hide ( ) ;
2203+ }
2204+ } , 200 ) ;
2205+ } ;
2206+
2207+ // 为trigger绑定事件
2208+ trigger . addEventListener ( 'mouseenter' , showPopup ) ;
2209+ trigger . addEventListener ( 'mouseleave' , hidePopup ) ;
2210+
2211+ // 监听popup显示/隐藏事件,为popup元素绑定鼠标事件
2212+ instance . setProps ( {
2213+ onShown ( instance ) {
2214+ const popupElement = instance . popper ;
2215+ if ( popupElement ) {
2216+ const popupMouseEnter = function ( ) {
2217+ if ( mouseoverTimeout ) {
2218+ clearTimeout ( mouseoverTimeout ) ;
2219+ mouseoverTimeout = null ;
2220+ }
2221+ isHovering = true ;
2222+ } ;
2223+
2224+ const popupMouseLeave = function ( ) {
2225+ isHovering = false ;
2226+ mouseoverTimeout = setTimeout ( ( ) => {
2227+ if ( ! isHovering ) {
2228+ instance . hide ( ) ;
2229+ }
2230+ } , 200 ) ;
2231+ } ;
2232+
2233+ popupElement . addEventListener ( 'mouseenter' , popupMouseEnter ) ;
2234+ popupElement . addEventListener ( 'mouseleave' , popupMouseLeave ) ;
2235+
2236+ // 保存引用用于清理
2237+ popupElement . _popupMouseEnter = popupMouseEnter ;
2238+ popupElement . _popupMouseLeave = popupMouseLeave ;
2239+ }
2240+ } ,
2241+ onHidden ( instance ) {
2242+ // 清理popup的事件监听器
2243+ const popupElement = instance . popper ;
2244+ if ( popupElement ) {
2245+ if ( popupElement . _popupMouseEnter ) {
2246+ popupElement . removeEventListener ( 'mouseenter' , popupElement . _popupMouseEnter ) ;
2247+ delete popupElement . _popupMouseEnter ;
2248+ }
2249+ if ( popupElement . _popupMouseLeave ) {
2250+ popupElement . removeEventListener ( 'mouseleave' , popupElement . _popupMouseLeave ) ;
2251+ delete popupElement . _popupMouseLeave ;
2252+ }
2253+ }
2254+ }
2255+ } ) ;
2256+
2257+ // 标记为已初始化
2258+ trigger . _tippyInitialized = true ;
2259+ trigger . _tippyInstance = instance ;
2260+
2261+ window . tippyInstances . push ( instance ) ;
2262+ }
2263+ } ) ;
2264+
2265+ console . log ( `Initialized ${ window . tippyInstances . length } Tippy.js popups` ) ;
2266+ }
2267+
2268+ // 使用MutationObserver监听DOM变化,确保新的server card能够绑定Tippy
2269+ function setupTippyMutationObserver ( ) {
2270+ if ( window . tippyMutationObserver ) {
2271+ window . tippyMutationObserver . disconnect ( ) ;
2272+ }
2273+
2274+ window . tippyMutationObserver = new MutationObserver ( function ( mutations ) {
2275+ let needsReinit = false ;
2276+
2277+ mutations . forEach ( function ( mutation ) {
2278+ // 检查是否有新的server card添加或修改
2279+ if ( mutation . type === 'childList' ) {
2280+ mutation . addedNodes . forEach ( function ( node ) {
2281+ if ( node . nodeType === 1 && // Element node
2282+ ( node . querySelector && node . querySelector ( '[data-server-popup]' ) ) ) {
2283+ needsReinit = true ;
2284+ }
2285+ } ) ;
2286+ }
2287+
2288+ // 检查现有元素的属性变化(Vue响应式更新)
2289+ if ( mutation . type === 'attributes' &&
2290+ mutation . target . hasAttribute &&
2291+ mutation . target . hasAttribute ( 'data-server-popup' ) ) {
2292+ needsReinit = true ;
2293+ }
2294+ } ) ;
2295+
2296+ if ( needsReinit ) {
2297+ // 防抖,避免频繁重新初始化
2298+ if ( window . tippyReinitTimeout ) {
2299+ clearTimeout ( window . tippyReinitTimeout ) ;
2300+ }
2301+ window . tippyReinitTimeout = setTimeout ( ( ) => {
2302+ console . log ( 'Reinitializing Tippy instances due to DOM changes' ) ;
2303+ initGlobalTippyPopups ( ) ;
2304+ } , 300 ) ;
2305+ }
2306+ } ) ;
2307+
2308+ // 观察server cards容器和整个文档
2309+ const targetNode = document . querySelector ( '.ui.four.stackable.status.cards' ) || document . body ;
2310+ window . tippyMutationObserver . observe ( targetNode , {
2311+ childList : true ,
2312+ subtree : true ,
2313+ attributes : true ,
2314+ attributeFilter : [ 'data-server-popup' ]
2315+ } ) ;
2316+ }
2317+
2318+ // Tippy.js 初始化入口函数
2319+ function initTippyPopups ( ) {
2320+ // 等待Tippy.js完全加载后初始化弹窗
2321+ function waitForTippy ( ) {
2322+ if ( typeof tippy !== 'undefined' ) {
2323+ initGlobalTippyPopups ( ) ;
2324+ setupTippyMutationObserver ( ) ;
2325+ } else {
2326+ // 如果Tippy.js还未加载完成,延迟100ms后重试
2327+ setTimeout ( waitForTippy , 100 ) ;
2328+ }
2329+ }
2330+
2331+ // 开始等待Tippy.js加载
2332+ waitForTippy ( ) ;
2333+ }
2334+
2335+ // 将函数挂载到全局,供其他页面使用
2336+ window . TippyPopups = {
2337+ init : initTippyPopups ,
2338+ initGlobal : initGlobalTippyPopups ,
2339+ setupObserver : setupTippyMutationObserver ,
2340+ updateContent : updateVisibleTippyContent
2341+ } ;
0 commit comments