mirror of
https://github.com/hi2shark/nazhua.git
synced 2026-01-20 19:39:36 +08:00
Compare commits
No commits in common. "14c83386e7f8fbfce5a792e39be00affd10f7e19" and "f446221f4531226360967e9986dba6929fb2c2ba" have entirely different histories.
14c83386e7
...
f446221f45
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "nazhua",
|
"name": "nazhua",
|
||||||
"version": "0.5.7",
|
"version": "0.5.6",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@ -44,5 +44,4 @@ window.$$nazhuaConfig = {
|
|||||||
// v1DashboardUrl: '/dashboard', // v1版本控制台地址
|
// v1DashboardUrl: '/dashboard', // v1版本控制台地址
|
||||||
// v1HideNezhaDashboardBtn: true, // v1版本导航栏控制台入口/登录按钮 在nezhaVersion为v1时有效
|
// v1HideNezhaDashboardBtn: true, // v1版本导航栏控制台入口/登录按钮 在nezhaVersion为v1时有效
|
||||||
// routeMode: 'h5', // 路由模式
|
// routeMode: 'h5', // 路由模式
|
||||||
// customFavicon: '', // 自定义favicon, 填写完整的url地址
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,292 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
ref="triggerRef"
|
|
||||||
class="popover-trigger"
|
|
||||||
@mouseenter="handleMouseEnter"
|
|
||||||
@mouseleave="handleMouseLeave"
|
|
||||||
@focusin="handleFocusIn"
|
|
||||||
@focusout="handleFocusOut"
|
|
||||||
@click="handleTriggerClick"
|
|
||||||
>
|
|
||||||
<slot name="trigger" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Teleport to="body">
|
|
||||||
<div
|
|
||||||
v-show="isShow"
|
|
||||||
ref="popoverRef"
|
|
||||||
class="popover"
|
|
||||||
:style="[popoverStyle, { zIndex: currentZIndex }]"
|
|
||||||
>
|
|
||||||
<template v-if="$slots.title || title">
|
|
||||||
<div class="popover-body">
|
|
||||||
{{ title }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<div class="popover-body">
|
|
||||||
<slot name="default" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</Teleport>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
/**
|
|
||||||
组件名称:Popover
|
|
||||||
|
|
||||||
组件说明:
|
|
||||||
该组件在移动端与 PC 端提供不同的交互模式,通过 "hover" 或 "click" 来触发显示或隐藏提示浮层。
|
|
||||||
若设置 unique 属性,则在显示新浮层的同时会隐藏其他已显示的浮层。
|
|
||||||
|
|
||||||
使用示例:
|
|
||||||
<Popover title="示例标题" trigger="click">
|
|
||||||
<template #trigger>
|
|
||||||
<button>点击触发</button>
|
|
||||||
</template>
|
|
||||||
这是 Popover 的内容
|
|
||||||
</Popover>
|
|
||||||
|
|
||||||
Props:
|
|
||||||
- visible (Boolean,默认 false)
|
|
||||||
Popover 的可见状态,可供外部进行手动控制。
|
|
||||||
- title (String,默认 '')
|
|
||||||
Popover 的标题文本,如不传则展示默认内容插槽。
|
|
||||||
- trigger (String,默认 'hover')
|
|
||||||
触发模式,可选值为 "hover" 或 "click"。
|
|
||||||
- unique (Boolean,默认 true)
|
|
||||||
如果为 true,则在显示当前 Popover 时会自动隐藏其他已显示的 Popover。
|
|
||||||
|
|
||||||
方法说明:
|
|
||||||
- handleMouseEnter()
|
|
||||||
当鼠标移入触发元素时,若 trigger 为 hover,会显示 Popover。
|
|
||||||
- handleMouseLeave()
|
|
||||||
当鼠标移出触发元素时,若 trigger 为 hover,会隐藏 Popover。
|
|
||||||
- handleTriggerClick(e)
|
|
||||||
当在移动端或 trigger 为 click 时,点击触发元素会切换 Popover 显示状态,并在移动端下自动延时隐藏。
|
|
||||||
- handleFocusIn()
|
|
||||||
当触发元素获得焦点时,若触发方式为 hover,会显示 Popover。
|
|
||||||
- handleFocusOut()
|
|
||||||
当触发元素失去焦点时,若触发方式为 hover,会隐藏 Popover。
|
|
||||||
|
|
||||||
注意事项:
|
|
||||||
- 在移动端会根据窗口宽度做适配,通过 document 监听点击事件和窗口大小变化来控制显示与关闭。
|
|
||||||
- 当 visible 通过外部控制时,非移动端能手动实现 Popover 的显隐。
|
|
||||||
*/
|
|
||||||
import {
|
|
||||||
ref,
|
|
||||||
computed,
|
|
||||||
onMounted,
|
|
||||||
onUnmounted,
|
|
||||||
watch,
|
|
||||||
} from 'vue';
|
|
||||||
import { getNextZIndex } from '../utils/zIndexManager';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
visible: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
trigger: {
|
|
||||||
type: String,
|
|
||||||
default: 'hover',
|
|
||||||
validator: (value) => ['hover', 'click'].includes(value),
|
|
||||||
},
|
|
||||||
unique: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// 移除全局 Symbol 相关代码
|
|
||||||
// 添加静态 z-index 计数器
|
|
||||||
// const baseZIndex = 1000;
|
|
||||||
// let zIndexCounter = baseZIndex;
|
|
||||||
|
|
||||||
const popoverRef = ref(null);
|
|
||||||
const position = ref({
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
});
|
|
||||||
const isMobile = ref(window.innerWidth < 600);
|
|
||||||
const isShow = ref(false);
|
|
||||||
const triggerRef = ref(null);
|
|
||||||
const currentZIndex = ref(1000);
|
|
||||||
|
|
||||||
// 移除 getCurrentPopover 和 setCurrentPopover 函数
|
|
||||||
|
|
||||||
// 更新移动端位置
|
|
||||||
const updateMobilePosition = () => {
|
|
||||||
if (!triggerRef.value) return;
|
|
||||||
const rect = triggerRef.value.getBoundingClientRect();
|
|
||||||
position.value = {
|
|
||||||
x: rect.left + rect.width / 2,
|
|
||||||
y: rect.top + rect.height,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// 修改显示逻辑
|
|
||||||
const updateShow = (value) => {
|
|
||||||
if (value) {
|
|
||||||
currentZIndex.value = getNextZIndex();
|
|
||||||
}
|
|
||||||
isShow.value = value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMouseEnter = () => {
|
|
||||||
if (!isMobile.value && props.trigger === 'hover') {
|
|
||||||
updateShow(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMouseLeave = () => {
|
|
||||||
if (!isMobile.value && props.trigger === 'hover') {
|
|
||||||
updateShow(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let autoCloseTimer;
|
|
||||||
const handleTriggerClick = (e) => {
|
|
||||||
if (props.trigger === 'click' || isMobile.value) {
|
|
||||||
e.stopPropagation();
|
|
||||||
updateShow(!isShow.value);
|
|
||||||
if (isShow.value && isMobile.value) {
|
|
||||||
if (autoCloseTimer) {
|
|
||||||
clearTimeout(autoCloseTimer);
|
|
||||||
}
|
|
||||||
autoCloseTimer = setTimeout(() => {
|
|
||||||
isShow.value = false;
|
|
||||||
}, 5 * 1000);
|
|
||||||
updateMobilePosition();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFocusIn = () => {
|
|
||||||
if (!isMobile.value && props.trigger === 'hover') {
|
|
||||||
isShow.value = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFocusOut = () => {
|
|
||||||
if (!isMobile.value && props.trigger === 'hover') {
|
|
||||||
isShow.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 修改点击事件处理
|
|
||||||
const handleDocumentClick = (e) => {
|
|
||||||
if (isShow.value && !triggerRef.value?.contains(e.target) && !popoverRef.value?.contains(e.target)) {
|
|
||||||
isShow.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const updatePosition = (e) => {
|
|
||||||
if (isMobile.value || !isShow.value) return;
|
|
||||||
position.value = {
|
|
||||||
x: e.clientX,
|
|
||||||
y: e.clientY,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const popoverStyle = computed(() => {
|
|
||||||
if (isMobile.value) {
|
|
||||||
return {
|
|
||||||
position: 'fixed',
|
|
||||||
bottom: '10vh',
|
|
||||||
left: '50%',
|
|
||||||
transform: 'translateX(-50%)',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const { x, y } = position.value;
|
|
||||||
const rect = popoverRef.value?.getBoundingClientRect();
|
|
||||||
const offset = 15; // 修改为20px偏移量
|
|
||||||
|
|
||||||
let left = x + offset;
|
|
||||||
let top = y + offset;
|
|
||||||
|
|
||||||
if (rect) {
|
|
||||||
// 防止超出右边界
|
|
||||||
if (left + rect.width > window.innerWidth) {
|
|
||||||
left = x - rect.width - offset;
|
|
||||||
}
|
|
||||||
// 防止超出下边界
|
|
||||||
if (top + rect.height > window.innerHeight) {
|
|
||||||
top = y - rect.height - offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
position: 'fixed',
|
|
||||||
left: `${left}px`,
|
|
||||||
top: `${top}px`,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleResize = () => {
|
|
||||||
isMobile.value = window.innerWidth < 600;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 监听visible属性变化
|
|
||||||
watch(() => props.visible, (newVal) => {
|
|
||||||
if (!isMobile.value) {
|
|
||||||
updateShow(newVal);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (isMobile.value || props.trigger === 'click') {
|
|
||||||
document.addEventListener('click', handleDocumentClick);
|
|
||||||
}
|
|
||||||
if (!isMobile.value) {
|
|
||||||
document.addEventListener('mousemove', updatePosition);
|
|
||||||
}
|
|
||||||
window.addEventListener('resize', handleResize);
|
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
if (isMobile.value || props.trigger === 'click') {
|
|
||||||
document.removeEventListener('click', handleDocumentClick);
|
|
||||||
}
|
|
||||||
if (!isMobile.value) {
|
|
||||||
document.removeEventListener('mousemove', updatePosition);
|
|
||||||
}
|
|
||||||
window.removeEventListener('resize', handleResize);
|
|
||||||
// 移除全局 Popover 相关的清理代码
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.popover-trigger {
|
|
||||||
display: inline-block;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover {
|
|
||||||
background: rgba(#000, 0.8);
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
|
|
||||||
// 移除固定的 z-index
|
|
||||||
max-width: 300px;
|
|
||||||
|
|
||||||
@media screen and (max-width: 600px) {
|
|
||||||
max-width: 90%;
|
|
||||||
text-align: center;
|
|
||||||
box-shadow: 0 4px 12px rgba(251, 255, 217, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover-body {
|
|
||||||
line-height: 1.4;
|
|
||||||
font-size: 14px;
|
|
||||||
// 允许换行
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -33,27 +33,10 @@ if (config.nazhua.nezhaVersion) {
|
|||||||
config.init = true;
|
config.init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 替换网站图标
|
|
||||||
*/
|
|
||||||
function replaceFavicon() {
|
|
||||||
if (config.nazhua.customFavicon) {
|
|
||||||
const link = document.querySelector("link[rel*='icon']");
|
|
||||||
link.type = 'image/x-icon';
|
|
||||||
link.rel = 'shortcut icon';
|
|
||||||
link.href = config.nazhua.customFavicon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
replaceFavicon();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 合并自定义配置
|
|
||||||
*/
|
|
||||||
export function mergeNazhuaConfig(customConfig) {
|
export function mergeNazhuaConfig(customConfig) {
|
||||||
Object.keys(customConfig).forEach((key) => {
|
Object.keys(customConfig).forEach((key) => {
|
||||||
config.nazhua[key] = customConfig[key];
|
config.nazhua[key] = customConfig[key];
|
||||||
});
|
});
|
||||||
replaceFavicon();
|
|
||||||
}
|
}
|
||||||
// 暴露合并配置方法
|
// 暴露合并配置方法
|
||||||
window.$mergeNazhuaConfig = mergeNazhuaConfig;
|
window.$mergeNazhuaConfig = mergeNazhuaConfig;
|
||||||
|
|||||||
@ -5,13 +5,11 @@ import store from './store';
|
|||||||
import config from './config';
|
import config from './config';
|
||||||
|
|
||||||
import DotDotBox from './components/dot-dot-box.vue';
|
import DotDotBox from './components/dot-dot-box.vue';
|
||||||
import Popover from './components/popover.vue';
|
|
||||||
|
|
||||||
export default (app) => {
|
export default (app) => {
|
||||||
app.use(router);
|
app.use(router);
|
||||||
app.use(store);
|
app.use(store);
|
||||||
app.component('DotDotBox', DotDotBox);
|
app.component('DotDotBox', DotDotBox);
|
||||||
app.component('Popover', Popover);
|
|
||||||
|
|
||||||
app.config.globalProperties.$hasSarasaTerm = !import.meta.env.VITE_DISABLE_SARASA_TERM_SC;
|
app.config.globalProperties.$hasSarasaTerm = !import.meta.env.VITE_DISABLE_SARASA_TERM_SC;
|
||||||
app.config.globalProperties.$config = config;
|
app.config.globalProperties.$config = config;
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
const BASE_Z_INDEX = 1000;
|
|
||||||
let zIndexCounter = BASE_Z_INDEX;
|
|
||||||
|
|
||||||
export const getNextZIndex = () => {
|
|
||||||
zIndexCounter += 1;
|
|
||||||
return zIndexCounter;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getCurrentZIndex = () => zIndexCounter;
|
|
||||||
|
|
||||||
export const resetZIndex = () => {
|
|
||||||
zIndexCounter = BASE_Z_INDEX;
|
|
||||||
};
|
|
||||||
@ -68,26 +68,23 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="server-info-content">
|
<div class="server-info-content">
|
||||||
<div class="server-info-item-group">
|
<div class="server-info-item-group">
|
||||||
<template
|
<span
|
||||||
v-for="(ttItem, ttIndex) in temperatureData.list"
|
v-for="(ttItem, ttIndex) in temperatureData.list"
|
||||||
:key="`${info.ID}_temperature_${ttIndex}`"
|
:key="`${info.ID}_temperature_${ttIndex}`"
|
||||||
|
class="server-info-item"
|
||||||
|
:class="`temperature--${ttItem.type}`"
|
||||||
|
:title="ttItem?.title || ''"
|
||||||
>
|
>
|
||||||
<popover :title="ttItem?.title || (`${ttItem.label}: ${ttItem.value}`)">
|
<span
|
||||||
<template #trigger>
|
class="server-info-item-label"
|
||||||
<span
|
:title="ttItem.label"
|
||||||
class="server-info-item"
|
>
|
||||||
:class="`temperature--${ttItem.type}`"
|
{{ ttItem.label }}
|
||||||
>
|
</span>
|
||||||
<span class="server-info-item-label">
|
<span class="server-info-item-value">
|
||||||
{{ ttItem.label }}
|
{{ ttItem.value }}
|
||||||
</span>
|
</span>
|
||||||
<span class="server-info-item-value">
|
</span>
|
||||||
{{ ttItem.value }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</popover>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -67,44 +67,38 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="monitor-cate-group">
|
<div class="monitor-cate-group">
|
||||||
<template
|
<div
|
||||||
v-for="cateItem in monitorChartData.cateList"
|
v-for="cateItem in monitorChartData.cateList"
|
||||||
:key="cateItem.id"
|
:key="cateItem.id"
|
||||||
|
class="monitor-cate-item"
|
||||||
|
:class="{
|
||||||
|
disabled: showCates[cateItem.id] === false,
|
||||||
|
}"
|
||||||
|
:style="{
|
||||||
|
'--cate-color': cateItem.color,
|
||||||
|
}"
|
||||||
|
:title="cateItem.title"
|
||||||
|
@click="toggleShowCate(cateItem.id)"
|
||||||
>
|
>
|
||||||
<popover :title="cateItem.title">
|
<span class="cate-legend" />
|
||||||
<template #trigger>
|
<span
|
||||||
<div
|
class="cate-name"
|
||||||
class="monitor-cate-item"
|
>
|
||||||
:class="{
|
{{ cateItem.name }}
|
||||||
disabled: showCates[cateItem.id] === false,
|
</span>
|
||||||
}"
|
<span
|
||||||
:style="{
|
v-if="cateItem.avg !== 0"
|
||||||
'--cate-color': cateItem.color,
|
class="cate-avg-ms"
|
||||||
}"
|
>
|
||||||
@click="toggleShowCate(cateItem.id)"
|
{{ cateItem.avg }}ms
|
||||||
>
|
</span>
|
||||||
<span class="cate-legend" />
|
<span
|
||||||
<span
|
v-else
|
||||||
class="cate-name"
|
class="cate-avg-ms"
|
||||||
>
|
>
|
||||||
{{ cateItem.name }}
|
-ms
|
||||||
</span>
|
</span>
|
||||||
<span
|
</div>
|
||||||
v-if="cateItem.avg !== 0"
|
|
||||||
class="cate-avg-ms"
|
|
||||||
>
|
|
||||||
{{ cateItem.avg }}ms
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-else
|
|
||||||
class="cate-avg-ms"
|
|
||||||
>
|
|
||||||
-ms
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</popover>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<line-chart
|
<line-chart
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user