mirror of
https://github.com/nezhahq/nezha.git
synced 2025-01-22 12:48:14 -05:00
feat: status-server主题增加套餐信息展示 (#464)
* feat: status-server主题增加套餐信息展示 1. 首页通过在后台配置PublicNote字段,实现agent套餐信息展示 2. 一些其他小优化 * 1.未获取agent国家时,默认彩虹旗修改为联合国旗
This commit is contained in:
parent
45b0f1edfc
commit
96c3fd433f
3
resource/l10n/en-US.toml
vendored
3
resource/l10n/en-US.toml
vendored
@ -750,3 +750,6 @@ other = "d"
|
||||
|
||||
[CustomNameservers]
|
||||
other = "Custom Public DNS Nameservers for DDNS (separate with comma)"
|
||||
|
||||
[Plan]
|
||||
other = "Plan"
|
||||
|
3
resource/l10n/es-ES.toml
vendored
3
resource/l10n/es-ES.toml
vendored
@ -750,3 +750,6 @@ other = "d"
|
||||
|
||||
[CustomNameservers]
|
||||
other = "Servidores DNS públicos personalizados para DDNS (separar con coma)"
|
||||
|
||||
[Plan]
|
||||
other = "Plan"
|
||||
|
3
resource/l10n/zh-CN.toml
vendored
3
resource/l10n/zh-CN.toml
vendored
@ -750,3 +750,6 @@ other = "天"
|
||||
|
||||
[CustomNameservers]
|
||||
other = "自定义DDNS使用的公共DNS服务器(逗号分隔)"
|
||||
|
||||
[Plan]
|
||||
other = "套餐"
|
||||
|
3
resource/l10n/zh-TW.toml
vendored
3
resource/l10n/zh-TW.toml
vendored
@ -750,3 +750,6 @@ other = "天"
|
||||
|
||||
[CustomNameservers]
|
||||
other = "自訂DDNS使用的公共DNS伺服器(逗號分隔)"
|
||||
|
||||
[Plan]
|
||||
other = "套餐"
|
||||
|
@ -90,6 +90,11 @@ body[theme="dark"] .table > tbody > tr.expandRow.odd > td:before {
|
||||
body[theme="dark"] .table > tbody > tr.expandRow.even > td:before {
|
||||
background-color: rgba(28, 29, 38, 1);
|
||||
}
|
||||
|
||||
body[theme="dark"] .plan {
|
||||
background-image: none;
|
||||
background-color: rgba(255, 255, 255, 0.075);
|
||||
}
|
||||
/* expandRow展开部分样式结束 */
|
||||
|
||||
body[theme="dark"] .progress {
|
||||
|
@ -116,6 +116,13 @@ body[theme="light"] tr.odd.expandRow > :hover {
|
||||
background: #ffffff !important;
|
||||
}
|
||||
|
||||
body[theme="light"] .plan {
|
||||
color: #000000;
|
||||
background-color: #f5f5f5;
|
||||
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1);
|
||||
}
|
||||
|
||||
body[theme="light"] .progress-bar {
|
||||
color: #000000;
|
||||
}
|
||||
|
@ -104,6 +104,11 @@ body[theme="light"] .table > tbody > tr.expandRow.odd > td:before {
|
||||
body[theme="light"] .table > tbody > tr.expandRow.even > td:before {
|
||||
background-color: unset;
|
||||
}
|
||||
|
||||
body[theme="light"] .plan {
|
||||
background-image: none;
|
||||
background-color: rgba(0, 0, 0, 0.015);
|
||||
}
|
||||
/* expandRow展开部分样式结束 */
|
||||
|
||||
body[theme="light"] .progress {
|
||||
|
19
resource/static/theme-server-status/css/main.css
vendored
19
resource/static/theme-server-status/css/main.css
vendored
@ -268,6 +268,22 @@ tr.accordion-toggle{
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
.plan {
|
||||
display: inline-block;
|
||||
font-size: 85%;
|
||||
margin-right: 2px;
|
||||
padding: 2px 5px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.network-route, .extra {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.last {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.temp-detail {
|
||||
cursor: pointer;
|
||||
}
|
||||
@ -557,6 +573,9 @@ footer p {
|
||||
min-width: 75px;
|
||||
max-width: 75px;
|
||||
}
|
||||
.plan {
|
||||
display: inline;
|
||||
}
|
||||
.accordian-body {
|
||||
margin: 5px 0px 5px 10px;
|
||||
}
|
||||
|
@ -26,9 +26,9 @@
|
||||
<script src="https://unpkg.com/bootstrap@3.4.1/dist/js/bootstrap.min.js"></script>
|
||||
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
|
||||
<script src="https://unpkg.com/echarts@5.5.0/dist/echarts.min.js"></script>
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/main.css?v20241008">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/dark.css?v20241008">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/light.css?v20241008">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/main.css?v20241029">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/dark.css?v20241029">
|
||||
<link rel="stylesheet" href="/static/theme-server-status/css/light.css?v20241029">
|
||||
<script src="/static/theme-server-status/js/mixin.js?v20240915"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -35,8 +35,8 @@
|
||||
<span class="node-cell-os-text">@#getPlatformName(node.os) === '' && node.stateuptime > 0 ? 'linux' : getPlatformName(node.os)#@</span>
|
||||
</td>
|
||||
<td class="node-cell location center">
|
||||
<i :class="'fi fi-' + (node.stateuptime > 0 ? (node.location || 'rb') : '')"></i>
|
||||
<span class="node-cell-location-text text-uppercase">@#node.stateuptime > 0 ? (node.location || 'RB') : ''#@</span>
|
||||
<i :class="'fi fi-' + (node.stateuptime > 0 ? (node.location || 'un') : '')"></i>
|
||||
<span class="node-cell-location-text text-uppercase">@#node.stateuptime > 0 ? (node.location || 'UN') : ''#@</span>
|
||||
</td>
|
||||
<td v-if="nodesNoTag.some(item => item.additional && item.additional.price && Object.keys(item.additional.price).length > 0)" class="node-cell price center">
|
||||
<template v-if="node.additional && node.additional.price">
|
||||
@ -88,6 +88,45 @@
|
||||
<td colspan="16">
|
||||
<div class="accordian-body collapse" :id="'rt'+node.ID">
|
||||
<div style="display: flex;left-items: center;justify-content: center;flex-direction: column; max-width: 89vw">
|
||||
<span v-if="node.additional && Object.keys(node.additional.plan).length > 0" class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "Plan"}}:</span>
|
||||
<span v-if="node.additional && Object.keys(node.additional.price).length > 0" class="plan price">
|
||||
<span v-if="node.additional.price.amount == 0">FREE</span>
|
||||
<span v-else-if="node.additional.price.amount == -1">PAYG</span>
|
||||
<span v-else><i class="bi bi-cash-stack"></i> @#node.additional.price.amount#@@#(node.additional.price.cycle ? '/' + node.additional.price.cycle : '')#@</span>
|
||||
</span>
|
||||
<span v-if="node.additional && node.additional.remaining.endDate" class="plan enddate">
|
||||
<i class="bi bi-clock-history"></i>
|
||||
<span v-if="node.additional.remaining.days == 'lifetime'">{{tr "Lifetime"}}</span>
|
||||
<span v-else-if="node.additional.remaining.days < 0">{{tr "Expired"}}</span>
|
||||
<span v-else>@#node.additional.remaining.endDate.toISOString().split('T')[0]#@</span>
|
||||
</span>
|
||||
<span v-if="node.additional && node.additional.plan.bandwidth" class="plan bandwidth">
|
||||
<i class="bi bi-speedometer2"></i>
|
||||
<span>@#node.additional.plan.bandwidth#@</span>
|
||||
</span>
|
||||
<span v-if="node.additional && node.additional.plan.trafficVol" class="plan traffic">
|
||||
<i v-if="node.additional && node.additional.plan.trafficType == 1" class="bi bi-arrow-up"></i>
|
||||
<i v-else class="bi bi-arrow-down-up"></i>
|
||||
<span>@#node.additional.plan.trafficVol#@</span>
|
||||
</span>
|
||||
<span v-if="node.additional && node.additional.plan.ipv4" class="plan ipv4">
|
||||
<span>IPv4</span>
|
||||
</span>
|
||||
<span v-if="node.additional && node.additional.plan.ipv6" class="plan ipv6">
|
||||
<span>IPv6</span>
|
||||
</span>
|
||||
<template v-if="node.additional && node.additional.plan.networkRoute.length>0" v-for="(item, index) in node.additional.plan.networkRoute" :key="index">
|
||||
<span class="plan network-route" :class="{ last: index === node.additional.plan.networkRoute.length - 1 }">
|
||||
<span>@#item#@</span>
|
||||
</span>
|
||||
</template>
|
||||
<template v-if="node.additional && node.additional.plan.extra.length>0" v-for="(item, index) in node.additional.plan.extra" :key="index">
|
||||
<span class="plan extra" :class="{ last: index === node.additional.plan.extra.length - 1 }">
|
||||
<span>@#item#@</span>
|
||||
</span>
|
||||
</template>
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "Platform"}}:</span>
|
||||
<span v-if="node.host.Platform">@#node.host.Platform#@@#node.host.PlatformVersion ? '-' + node.host.PlatformVersion : ''#@</span>
|
||||
|
@ -38,8 +38,8 @@
|
||||
<span class="node-cell-os-text">@#getPlatformName(node.os) === '' && node.stateuptime > 0 ? 'linux' : getPlatformName(node.os)#@</span>
|
||||
</td>
|
||||
<td class="node-cell location center">
|
||||
<i :class="'fi fi-' + (node.stateuptime > 0 ? (node.location || 'rb') : '')"></i>
|
||||
<span class="node-cell-location-text text-uppercase">@#node.stateuptime > 0 ? (node.location || 'RB') : ''#@</span>
|
||||
<i :class="'fi fi-' + (node.stateuptime > 0 ? (node.location || 'un') : '')"></i>
|
||||
<span class="node-cell-location-text text-uppercase">@#node.stateuptime > 0 ? (node.location || 'UN') : ''#@</span>
|
||||
</td>
|
||||
<td v-if="group.data.some(item => item.additional && item.additional.price && Object.keys(item.additional.price).length > 0)" class="node-cell price center">
|
||||
<template v-if="node.additional && node.additional.price">
|
||||
@ -91,6 +91,45 @@
|
||||
<td colspan="16">
|
||||
<div class="accordian-body collapse" :id="'rt'+node.ID">
|
||||
<div style="display: flex;left-items: center;justify-content: center;flex-direction: column; max-width: 89vw">
|
||||
<span v-if="node.additional && Object.keys(node.additional.plan).length > 0" class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "Plan"}}:</span>
|
||||
<span v-if="node.additional && Object.keys(node.additional.price).length > 0" class="plan price">
|
||||
<span v-if="node.additional.price.amount == 0">FREE</span>
|
||||
<span v-else-if="node.additional.price.amount == -1">PAYG</span>
|
||||
<span v-else><i class="bi bi-cash-stack"></i> @#node.additional.price.amount#@@#(node.additional.price.cycle ? '/' + node.additional.price.cycle : '')#@</span>
|
||||
</span>
|
||||
<span v-if="node.additional && node.additional.remaining.endDate" class="plan enddate">
|
||||
<i class="bi bi-clock-history"></i>
|
||||
<span v-if="node.additional.remaining.days == 'lifetime'">{{tr "Lifetime"}}</span>
|
||||
<span v-else-if="node.additional.remaining.days < 0">{{tr "Expired"}}</span>
|
||||
<span v-else>@#node.additional.remaining.endDate.toISOString().split('T')[0]#@</span>
|
||||
</span>
|
||||
<span v-if="node.additional && node.additional.plan.bandwidth" class="plan bandwidth">
|
||||
<i class="bi bi-speedometer2"></i>
|
||||
<span>@#node.additional.plan.bandwidth#@</span>
|
||||
</span>
|
||||
<span v-if="node.additional && node.additional.plan.trafficVol" class="plan traffic">
|
||||
<i v-if="node.additional && node.additional.plan.trafficType == 1" class="bi bi-arrow-up"></i>
|
||||
<i v-else class="bi bi-arrow-down-up"></i>
|
||||
<span>@#node.additional.plan.trafficVol#@</span>
|
||||
</span>
|
||||
<span v-if="node.additional && node.additional.plan.ipv4" class="plan ipv4">
|
||||
<span>IPv4</span>
|
||||
</span>
|
||||
<span v-if="node.additional && node.additional.plan.ipv6" class="plan ipv6">
|
||||
<span>IPv6</span>
|
||||
</span>
|
||||
<template v-if="node.additional && node.additional.plan.networkRoute.length>0" v-for="(item, index) in node.additional.plan.networkRoute" :key="index">
|
||||
<span class="plan network-route" :class="{ last: index === node.additional.plan.networkRoute.length - 1 }">
|
||||
<span>@#item#@</span>
|
||||
</span>
|
||||
</template>
|
||||
<template v-if="node.additional && node.additional.plan.extra.length>0" v-for="(item, index) in node.additional.plan.extra" :key="index">
|
||||
<span class="plan extra" :class="{ last: index === node.additional.plan.extra.length - 1 }">
|
||||
<span>@#item#@</span>
|
||||
</span>
|
||||
</template>
|
||||
</span>
|
||||
<span class="node-cell-expand">
|
||||
<span class="node-cell-expand-label">{{tr "Platform"}}:</span>
|
||||
<span v-if="node.host.Platform">@#node.host.Platform#@@#node.host.PlatformVersion ? '-' + node.host.PlatformVersion : ''#@</span>
|
||||
|
90
resource/template/theme-server-status/home.html
vendored
90
resource/template/theme-server-status/home.html
vendored
@ -117,43 +117,54 @@
|
||||
initAdditional(servers) {
|
||||
let nodes = {};
|
||||
servers?.forEach(server => {
|
||||
if (server.PublicNote) {
|
||||
const remainingFormat = this.getRemainingFormat(server.live, server.PublicNote);
|
||||
const remainingDays = this.getRemainingDays(this.getNoteElementValue(server.PublicNote, "billingDataMod", "endDate"), server.PublicNote);
|
||||
const remainingPercent = this.getRemainingPercent(
|
||||
this.getNoteElementValue(server.PublicNote, "billingDataMod", "startDate"),
|
||||
this.getNoteElementValue(server.PublicNote, "billingDataMod", "endDate"),
|
||||
server.PublicNote
|
||||
);
|
||||
const priceAmount = this.getNoteElementValue(server.PublicNote, "billingDataMod", "amount");
|
||||
const priceCycle = this.getNoteElementValue(server.PublicNote, "billingDataMod", "cycle");
|
||||
//处理异常
|
||||
if (!server.PublicNote) return;
|
||||
|
||||
// 初始化节点
|
||||
nodes[server.ID] = {
|
||||
"remaining": {},
|
||||
"price": {}
|
||||
};
|
||||
// 初始化节点
|
||||
nodes[server.ID] = {
|
||||
"remaining": {},
|
||||
"price": {},
|
||||
"plan": {}
|
||||
};
|
||||
|
||||
if (remainingFormat) {
|
||||
nodes[server.ID].remaining.format = remainingFormat;
|
||||
}
|
||||
// 处理 billingDataMod 的 remaining 配置
|
||||
const remainingEndDate = this.getRemainingDays(this.getNoteElementValue(server.PublicNote, "billingDataMod", "endDate"), server.PublicNote, 1);
|
||||
const remainingFormat = this.getRemainingFormat(server.live, server.PublicNote);
|
||||
const remainingDays = this.getRemainingDays(this.getNoteElementValue(server.PublicNote, "billingDataMod", "endDate"), server.PublicNote);
|
||||
const remainingPercent = this.getRemainingPercent(
|
||||
this.getNoteElementValue(server.PublicNote, "billingDataMod", "startDate"),
|
||||
this.getNoteElementValue(server.PublicNote, "billingDataMod", "endDate"),
|
||||
server.PublicNote
|
||||
);
|
||||
// 设置 remaining 属性
|
||||
if (remainingEndDate) nodes[server.ID].remaining.endDate = remainingEndDate;
|
||||
if (remainingFormat) nodes[server.ID].remaining.format = remainingFormat;
|
||||
if (remainingDays) nodes[server.ID].remaining.days = remainingDays;
|
||||
if (remainingPercent) nodes[server.ID].remaining.percent = this.toFixed2(100 - remainingPercent);
|
||||
|
||||
if (remainingDays) {
|
||||
nodes[server.ID].remaining.days = remainingDays;
|
||||
}
|
||||
|
||||
if (remainingPercent) {
|
||||
nodes[server.ID].remaining.percent = this.toFixed2(100 - remainingPercent);
|
||||
}
|
||||
|
||||
if (priceAmount) {
|
||||
nodes[server.ID].price.amount = priceAmount;
|
||||
}
|
||||
|
||||
if (priceCycle && priceAmount) {
|
||||
nodes[server.ID].price.cycle = priceCycle;
|
||||
}
|
||||
}
|
||||
// 处理 billingDataMod 的 price 配置
|
||||
const priceAmount = this.getNoteElementValue(server.PublicNote, "billingDataMod", "amount");
|
||||
const priceCycle = this.getNoteElementValue(server.PublicNote, "billingDataMod", "cycle");
|
||||
// 设置 price 属性
|
||||
if (priceAmount) nodes[server.ID].price.amount = priceAmount;
|
||||
if (priceCycle && priceAmount) nodes[server.ID].price.cycle = priceCycle;
|
||||
|
||||
// 处理 planDataMod 配置
|
||||
const planBandwidth = this.getNoteElementValue(server.PublicNote, "planDataMod", "bandwidth");
|
||||
const planTrafficVol = this.getNoteElementValue(server.PublicNote, "planDataMod", "trafficVol");
|
||||
const planTrafficType = this.getNoteElementValue(server.PublicNote, "planDataMod", "trafficType");
|
||||
const planIPv4 = this.getNoteElementValue(server.PublicNote, "planDataMod", "IPv4");
|
||||
const planIPv6 = this.getNoteElementValue(server.PublicNote, "planDataMod", "IPv6");
|
||||
const planNetworkRoute = this.getNoteElementValue(server.PublicNote, "planDataMod", "networkRoute");
|
||||
const planExtra = this.getNoteElementValue(server.PublicNote, "planDataMod", "extra");
|
||||
// 设置 plan 属性
|
||||
if (planBandwidth) nodes[server.ID].plan.bandwidth = planBandwidth;
|
||||
if (planTrafficVol) nodes[server.ID].plan.trafficVol = planTrafficVol;
|
||||
if (planTrafficType) nodes[server.ID].plan.trafficType = planTrafficType;
|
||||
if (planIPv4) nodes[server.ID].plan.ipv4 = planIPv4 >= 1;
|
||||
if (planIPv6) nodes[server.ID].plan.ipv6 = planIPv6 >= 1;
|
||||
if (planNetworkRoute) nodes[server.ID].plan.networkRoute = planNetworkRoute.split(',');
|
||||
if (planExtra) nodes[server.ID].plan.extra = planExtra.split(',');
|
||||
});
|
||||
return nodes;
|
||||
},
|
||||
@ -859,7 +870,7 @@
|
||||
const expiration = new Date(endDate);
|
||||
const current = this.getAdjustTimezone(new Date(endDate), new Date());
|
||||
|
||||
// 如果 expiration 无效,返回 null 并记录日志
|
||||
// 如果 expiration 无效,记录日志
|
||||
if (isNaN(expiration.getTime())) {
|
||||
console.log("getAutoRenewalEndDate: Invalid expiration format");
|
||||
}
|
||||
@ -1055,7 +1066,7 @@
|
||||
return this.formatPercents(online, this.toFixed2(percent));
|
||||
|
||||
},
|
||||
getRemainingDays(endDate, note) {
|
||||
getRemainingDays(endDate, note, type) {
|
||||
// 检查 endDate 是否有效
|
||||
if (!endDate || typeof endDate !== 'string') {
|
||||
return null;
|
||||
@ -1066,9 +1077,9 @@
|
||||
return "lifetime";
|
||||
}
|
||||
|
||||
// 检查 startDate 和 endDate 是否为合法的Date
|
||||
// 检查 endDate 是否为合法的Date
|
||||
if (isNaN(new Date(endDate).getTime())) {
|
||||
return "NaN";
|
||||
return type === 1 ? null : "NaN";
|
||||
}
|
||||
|
||||
// 获取当前时间,并调整时区
|
||||
@ -1087,6 +1098,9 @@
|
||||
// 确定到期时间
|
||||
const end = autoRenewal ? autoEndDate.date : new Date(endDate);
|
||||
|
||||
// 直接返回处理后的到期时间
|
||||
if (type === 1) return end;
|
||||
|
||||
// 计算剩余天数
|
||||
const timeDiff = end - currentTime;
|
||||
const daysDiff = Math.ceil(timeDiff / (1000 * 60 * 60 * 24));
|
||||
|
@ -11,7 +11,7 @@
|
||||
<input type="text" id="dropdown-search" class="form-control" placeholder="Search...">
|
||||
</li>
|
||||
<li class="dropdown-item" v-for="server in servers" @click="showCharts(server.ID)">
|
||||
<a><i :class="'fi fi-' + (server.Host.CountryCode || 'rb')"></i> @#server.Name#@ <i v-if="server.ID == currentServerId" class="check icon"></i></a>
|
||||
<a><i :class="'fi fi-' + (server.Host.CountryCode || 'un')"></i> @#server.Name#@ <i v-if="server.ID == currentServerId" class="check icon"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -332,7 +332,7 @@
|
||||
},
|
||||
getServerCountryCode(id){
|
||||
const result = this.servers.find(item => item.ID == id);
|
||||
return result.Host.CountryCode ? result.Host.CountryCode : 'rb';
|
||||
return result.Host.CountryCode ? result.Host.CountryCode : 'un';
|
||||
},
|
||||
getNextServerId(id) {
|
||||
const currentIndex = this.servers.findIndex(item => item.ID === id);
|
||||
|
Loading…
Reference in New Issue
Block a user