Skip to content

Commit 84e9e9f

Browse files
committed
Update.
1 parent 493ac2d commit 84e9e9f

File tree

5 files changed

+397
-60
lines changed

5 files changed

+397
-60
lines changed

resource/static/main.css

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,3 +1041,112 @@ html, body {
10411041
}
10421042

10431043
.ui-alerts{position:fixed;z-index:2060;padding:23px}.ui-alerts.center{top:50%;left:50%;margin-top:-100px;margin-left:-222px}.ui-alerts.top-right{top:20px;right:20px}.ui-alerts.top-center{top:20px;margin-left:-222px;left:50%}.ui-alerts.top-left{top:20px;left:20px}.ui-alerts.bottom-right{bottom:0;right:20px}.ui-alerts.bottom-center{bottom:0;margin-left:-222px;left:50%}.ui-alerts.bottom-left{bottom:0;left:20px}.ui-alerts.ui-alerts>.message>.content>.header{padding-right:13px}@media (min-width:320px){.ui-alerts.top-center{margin-left:-163px}}
1044+
1045+
/* Tippy.js 自定义样式 - 服务器信息弹窗 */
1046+
.tippy-box[data-theme~='custom-server-popup'] {
1047+
background-color: rgba(255, 255, 255, 0.8) !important;
1048+
color: #333 !important;
1049+
border-radius: 8px !important;
1050+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15) !important;
1051+
backdrop-filter: blur(10px) !important;
1052+
border: 1px solid rgba(255, 255, 255, 0.3) !important;
1053+
font-size: 13px !important;
1054+
line-height: 1.5 !important;
1055+
padding: 10px 12px !important;
1056+
width: 250px !important;
1057+
height: auto !important;
1058+
max-width: none !important;
1059+
max-height: none !important;
1060+
overflow: visible !important;
1061+
}
1062+
1063+
.tippy-box[data-theme~='custom-server-popup'] .tippy-content {
1064+
padding: 0 !important;
1065+
color: #333 !important;
1066+
overflow: visible !important;
1067+
max-width: none !important;
1068+
max-height: none !important;
1069+
}
1070+
1071+
/* 修复箭头样式 - 重新实现三角箭头 */
1072+
.tippy-box[data-theme~='custom-server-popup'] .tippy-arrow {
1073+
width: 16px !important;
1074+
height: 16px !important;
1075+
}
1076+
1077+
/* 箭头容器定位 - 偏移到popup外部(修正方向) */
1078+
.tippy-box[data-theme~='custom-server-popup'][data-placement^='top'] .tippy-arrow {
1079+
bottom: -9px !important;
1080+
left: 50% !important;
1081+
transform: translateX(-50%) !important;
1082+
}
1083+
1084+
.tippy-box[data-theme~='custom-server-popup'][data-placement^='bottom'] .tippy-arrow {
1085+
top: -9px !important;
1086+
left: 50% !important;
1087+
transform: translateX(-50%) !important;
1088+
}
1089+
1090+
.tippy-box[data-theme~='custom-server-popup'][data-placement^='left'] .tippy-arrow {
1091+
top: 49% !important;
1092+
right: -9px !important;
1093+
transform: translateY(-50%) !important;
1094+
}
1095+
1096+
.tippy-box[data-theme~='custom-server-popup'][data-placement^='right'] .tippy-arrow {
1097+
top: 49% !important;
1098+
left: -9px !important;
1099+
transform: translateY(-50%) !important;
1100+
}
1101+
1102+
.tippy-box[data-theme~='custom-server-popup'] .tippy-arrow::before {
1103+
content: '' !important;
1104+
position: absolute !important;
1105+
border: 8px solid transparent !important;
1106+
}
1107+
1108+
/* 顶部箭头 - popup在上方,箭头在popup底部指向下方 */
1109+
.tippy-box[data-theme~='custom-server-popup'][data-placement^='top'] .tippy-arrow::before {
1110+
bottom: 0 !important;
1111+
left: 50% !important;
1112+
transform: translateX(-50%) !important;
1113+
border-top-color: rgba(255, 255, 255, 0.8) !important;
1114+
border-bottom: none !important;
1115+
}
1116+
1117+
/* 底部箭头 - popup在下方,箭头在popup顶部指向上方 */
1118+
.tippy-box[data-theme~='custom-server-popup'][data-placement^='bottom'] .tippy-arrow::before {
1119+
top: 0 !important;
1120+
left: 50% !important;
1121+
transform: translateX(-50%) !important;
1122+
border-bottom-color: rgba(255, 255, 255, 0.8) !important;
1123+
border-top: none !important;
1124+
}
1125+
1126+
/* 左侧箭头 - popup在左侧,箭头在popup右侧指向右方 */
1127+
.tippy-box[data-theme~='custom-server-popup'][data-placement^='left'] .tippy-arrow::before {
1128+
top: 47% !important;
1129+
right: 0 !important;
1130+
transform: translateY(-50%) !important;
1131+
border-left-color: rgba(255, 255, 255, 0.8) !important;
1132+
border-right: none !important;
1133+
}
1134+
1135+
/* 右侧箭头 - popup在右侧,箭头在popup左侧指向左方 */
1136+
.tippy-box[data-theme~='custom-server-popup'][data-placement^='right'] .tippy-arrow::before {
1137+
top: 47% !important;
1138+
left: 0 !important;
1139+
transform: translateY(-50%) !important;
1140+
border-right-color: rgba(255, 255, 255, 0.8) !important;
1141+
border-left: none !important;
1142+
}
1143+
1144+
/* 兼容性:同时支持SVG箭头 */
1145+
.tippy-box[data-theme~='custom-server-popup'] .tippy-svg-arrow {
1146+
fill: rgba(255, 255, 255, 0.8) !important;
1147+
}
1148+
1149+
/* 隐藏原始内容 */
1150+
.server-popup-content {
1151+
display: none !important;
1152+
}

resource/static/main.js

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
};

resource/template/common/footer.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
<script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/semantic-ui/2.4.1/semantic.min.js"></script>
1010
<script src="/static/wallpaper.js?v20220423"></script>
1111
<script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.14/vue.min.js"></script>
12-
<script src="/static/main.js?v20250526"></script>
12+
<script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/echarts/5.3.0/echarts.min.js"></script>
13+
<script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/popper.js/2.11.2/umd/popper.min.js"></script>
14+
<script src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/tippy.js/6.3.7/tippy.umd.min.js"></script>
15+
<script src="/static/mixin.js?v20240912"></script>
16+
<script src="/static/main.js?v2025070722"></script>
1317
<script>
1418
var def_bg = document.getElementById('def_background');
1519
if (def_bg) {
@@ -27,4 +31,4 @@
2731
</script>
2832
</body>
2933
</html>
30-
{{end}}
34+
{{end}}

0 commit comments

Comments
 (0)