feat: status-server主题增加套餐信息展示 (#464)

* feat: status-server主题增加套餐信息展示
1. 首页通过在后台配置PublicNote字段,实现agent套餐信息展示
2. 一些其他小优化

* 1.未获取agent国家时,默认彩虹旗修改为联合国旗
This commit is contained in:
nap0o 2024-11-01 09:28:14 -04:00 committed by GitHub
parent 45b0f1edfc
commit 96c3fd433f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 187 additions and 47 deletions

View File

@ -750,3 +750,6 @@ other = "d"
[CustomNameservers]
other = "Custom Public DNS Nameservers for DDNS (separate with comma)"
[Plan]
other = "Plan"

View File

@ -750,3 +750,6 @@ other = "d"
[CustomNameservers]
other = "Servidores DNS públicos personalizados para DDNS (separar con coma)"
[Plan]
other = "Plan"

View File

@ -750,3 +750,6 @@ other = "天"
[CustomNameservers]
other = "自定义DDNS使用的公共DNS服务器逗号分隔"
[Plan]
other = "套餐"

View File

@ -750,3 +750,6 @@ other = "天"
[CustomNameservers]
other = "自訂DDNS使用的公共DNS伺服器逗號分隔"
[Plan]
other = "套餐"

View File

@ -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 {

View File

@ -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;
}

View File

@ -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 {

View File

@ -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;
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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));

View File

@ -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);