diff --git a/resource/l10n/en-US.toml b/resource/l10n/en-US.toml index 3884ab0..38b2634 100644 --- a/resource/l10n/en-US.toml +++ b/resource/l10n/en-US.toml @@ -690,3 +690,18 @@ other = "Cancel" [FMError] other = "Agent returned an error, please view the console for details. To open a new connection, reopen the FM again." + +[Remaining] +other = "Remaining" + +[Lifetime] +other = "Lifetime" + +[Price] +other = "Price" + +[Expired] +other = "Expired" + +[Days] +other = "d" diff --git a/resource/l10n/es-ES.toml b/resource/l10n/es-ES.toml index 1d77d67..e32bef6 100644 --- a/resource/l10n/es-ES.toml +++ b/resource/l10n/es-ES.toml @@ -690,3 +690,18 @@ other = "Cancelar" [FMError] other = "Agent devolvió un error, consulte la consola para obtener más detalles. Para abrir una nueva conexión, vuelva a abrir el FM." + +[Remaining] +other = "Remaining" + +[Lifetime] +other = "Lifetime" + +[Price] +other = "Price" + +[Expired] +other = "Expired" + +[Days] +other = "d" diff --git a/resource/l10n/zh-CN.toml b/resource/l10n/zh-CN.toml index 2208494..26478db 100644 --- a/resource/l10n/zh-CN.toml +++ b/resource/l10n/zh-CN.toml @@ -690,3 +690,18 @@ other = "取消" [FMError] other = "Agent 返回了错误,请查看控制台获取详细信息。要建立新连接,请重新打开 FM。" + +[Remaining] +other = "剩余" + +[Lifetime] +other = "永续" + +[Price] +other = "价格" + +[Expired] +other = "已到期" + +[Days] +other = "天" diff --git a/resource/l10n/zh-TW.toml b/resource/l10n/zh-TW.toml index cde3d23..dc07750 100644 --- a/resource/l10n/zh-TW.toml +++ b/resource/l10n/zh-TW.toml @@ -690,3 +690,18 @@ other = "取消" [FMError] other = "Agent 回傳了錯誤,請查看主控台獲取詳細資訊。要建立新連線,請重新開啟 FM。" + +[Remaining] +other = "剩餘" + +[Lifetime] +other = "永續" + +[Price] +other = "價格" + +[Expired] +other = "已到期" + +[Days] +other = "天" diff --git a/resource/static/theme-server-status/css/dark.css b/resource/static/theme-server-status/css/dark.css index ea37d77..011e4ca 100755 --- a/resource/static/theme-server-status/css/dark.css +++ b/resource/static/theme-server-status/css/dark.css @@ -8,6 +8,11 @@ body[theme="dark"] .navbar .navbar-brand { color: #ffffff; } +body[theme="dark"] .navbar .navbar-nav li.pc-active a, +body[theme="dark"] .navbar .navbar-nav li.m-active a{ + color: rgba(73, 146, 255, 1); +} + body[theme="dark"] .navbar .dropdown-menu { background-color: rgba(23, 26, 30, 1); border-color: rgba(49, 54, 59, 1); @@ -88,8 +93,8 @@ body[theme="dark"] .table > tbody > tr.expandRow.even > td:before{ /* expandRow展开部分样式结束 */ body[theme="dark"] .progress { - background-image: linear-gradient(#2c2c2c 0,rgba(28, 29, 38, 1) 100%); - background-color: rgba(28, 29, 38, 1); + background-image: none; + background-color: rgba(255, 255, 255, 0.075); } body[theme="dark"] .progress-bar { @@ -174,5 +179,5 @@ body[theme="dark"] .toolbox i{ } body[theme="dark"] .network-box .network-box-header{ - border-bottom: 1px solid rgba(110, 112, 121, 0.25); + border-bottom-color: rgba(110, 112, 121, 0.25); } \ No newline at end of file diff --git a/resource/static/theme-server-status/css/light.css b/resource/static/theme-server-status/css/light.css index 93b3f10..8cc7b08 100755 --- a/resource/static/theme-server-status/css/light.css +++ b/resource/static/theme-server-status/css/light.css @@ -16,10 +16,15 @@ body[theme="light"] .navbar .navbar-brand { color: rgba(0, 0, 0, 0.87); } -body[theme="light"] .navbar .navbar-nav > li > a{ +body[theme="light"] .navbar .navbar-nav > li > a { color: rgba(0, 0, 0, 0.87); } +body[theme="light"] .navbar .navbar-nav li.pc-active a, +body[theme="light"] .navbar .navbar-nav li.m-active a { + color: rgba(73, 146, 255, 1); +} + body[theme="light"] .navbar .navbar-nav > .open > a:focus, body[theme="light"] .navbar .navbar-nav > .open > a:hover, body[theme="light"] .navbar .navbar-nav > .active > a, @@ -198,7 +203,7 @@ body[theme="light"] .modal-header i.xclose{ } body[theme="light"] .network-box .network-box-header{ - border-bottom: 1px solid rgba(224, 230, 241, 0.6); + border-bottom-color: rgba(224, 230, 241, 0.6); } @media only screen and (max-width: 767px) { diff --git a/resource/static/theme-server-status/css/main.css b/resource/static/theme-server-status/css/main.css index e77100b..86b7735 100755 --- a/resource/static/theme-server-status/css/main.css +++ b/resource/static/theme-server-status/css/main.css @@ -237,8 +237,8 @@ tr.accordion-toggle{ } .node-cell.network { - min-width: 100px; - max-width: 100px; + min-width: 110px; + max-width: 110px; } .node-cell.traffic { @@ -250,6 +250,24 @@ tr.accordion-toggle{ max-width: 50px; } +.node-cell.remaining { + min-width: 65px; + max-width: 65px; +} + +.node-cell.remaining .additional { + position: absolute; + left: 0; + font-size: 12px; + text-align: center; + width: 100%; +} + +.node-cell.remaining .additional small{ + white-space: nowrap; + font-size: 80%; +} + .temp-detail { cursor: pointer; } @@ -331,6 +349,8 @@ td.ping-network-quality { padding: 5px 0px 15px 5px; cursor: pointer; width: 100%; + border-bottom-width: 1px; + border-bottom-style: solid; } .network-box .network-box-header .dropdown-menu { @@ -529,6 +549,10 @@ footer p{ display: none; visibility: hidden; } + .node-cell.remaining { + min-width: 75px; + max-width: 75px; + } .accordian-body{ margin: 5px 0px 5px 10px; } @@ -572,6 +596,7 @@ footer p{ .network-box .network-box-header { margin: 8px 0px 0px 8px; font-size: 16px; + border-bottom: none; } .network-box .chartTitle { font-size: 16px; diff --git a/resource/static/theme-server-status/js/mixin.js b/resource/static/theme-server-status/js/mixin.js index d12728e..05fb65d 100644 --- a/resource/static/theme-server-status/js/mixin.js +++ b/resource/static/theme-server-status/js/mixin.js @@ -155,6 +155,11 @@ const mixinsVue = { checkIsMobile() { // 检测设备类型,页面宽度小于768px认为是移动设备 return window.innerWidth <= 768; }, + isMenuActive(page){ + if(page == this.$root.page) { + return this.isMobile ? 'm-active' : 'pc-active'; + } + }, setBenchmarkHeight() { let vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh', `${vh}px`); diff --git a/resource/template/dashboard-default/server.html b/resource/template/dashboard-default/server.html index 24c3456..2cea237 100644 --- a/resource/template/dashboard-default/server.html +++ b/resource/template/dashboard-default/server.html @@ -30,9 +30,6 @@ {{tr "VersionNumber"}} {{tr "HideForGuest"}} {{tr "EnableDDNS"}} - {{tr "EnableIPv4"}} - {{tr "EnableIpv6"}} - {{tr "DDNSDomain"}} {{tr "Secret"}} {{tr "OneKeyInstall"}} {{tr "Note"}} @@ -51,9 +48,6 @@ {{$server.Host.Version}} {{$server.HideForGuest}} {{$server.EnableDDNS}} - {{$server.EnableIPv4}} - {{$server.EnableIpv6}} - {{$server.DDNSDomain}} - {{$server.Note}} - {{$server.PublicNote}} + + {{if $server.Note}} + + {{end}} + + + {{if $server.PublicNote}} + + {{end}} +
+ +
+ +
+ diff --git a/resource/template/theme-server-status/home-group-true.html b/resource/template/theme-server-status/home-group-true.html index f2b3e53..8fd51b9 100644 --- a/resource/template/theme-server-status/home-group-true.html +++ b/resource/template/theme-server-status/home-group-true.html @@ -9,6 +9,7 @@ {{tr "Name"}} {{tr "Platform"}} {{tr "Location"}} + {{tr "Price"}} {{tr "Uptime"}} {{tr "Load"}} {{tr "NetSpeed"}}↓|↑ @@ -16,6 +17,7 @@ {{tr "CpuUsed"}} {{tr "MemUsed"}} {{tr "DiskUsed"}} + {{tr "Remaining"}} @@ -39,6 +41,12 @@  @#node.location#@ + + + @#node.uptime#@ @#node.load#@ @#node.network#@ @@ -62,6 +70,21 @@ + +
+ +
+ diff --git a/resource/template/theme-server-status/home.html b/resource/template/theme-server-status/home.html index 38b2832..d930594 100644 --- a/resource/template/theme-server-status/home.html +++ b/resource/template/theme-server-status/home.html @@ -42,6 +42,7 @@ servers: [], nodesTag: [], nodesNoTag: [], + additional: {}, chartDataList: [], ws: null, language: {{.Conf.Language}}, @@ -81,7 +82,8 @@ this.countryNameMap = this.initCountryNameMap(); this.countryServer = this.initCountryServer(); this.countryMapChartData = this.initCountryMapChartData(); - }) + }); + this.additional = this.initAdditional(this.servers); }, mounted() { // 初始化时建立WebSocket连接 @@ -98,6 +100,25 @@ }); }, methods: { + initAdditional(servers) { + let nodes = {}; + servers?.forEach(server => { + if(server.PublicNote) { + nodes[server.ID] = { + "remaining": { + format: this.getRemainingFormat(server.live, server.PublicNote), + days: this.getRemainingDays(this.getNoteElementValue(server.PublicNote, "billingDataMod", "endDate"),server.PublicNote), + percent: this.toFixed2(100 - this.getRemainingPercent(this.getNoteElementValue(server.PublicNote, "billingDataMod", "startDate"), this.getNoteElementValue(server.PublicNote, "billingDataMod", "endDate"), server.PublicNote)) + }, + "price": { + amount: this.getNoteElementValue(server.PublicNote, "billingDataMod", "amount"), + cycle: this.getNoteElementValue(server.PublicNote, "billingDataMod", "cycle") + } + }; + } + }); + return nodes; + }, initCountryMap() { return fetch(this.staticUrl + '/maps/nezha.countrymap.json') .then(response => response.json()) @@ -722,6 +743,331 @@ this.renderCharts(id,true); } }); + }, + getNoteElementValue(string, elementName, childElementName) { + let obj; + + try { + obj = JSON.parse(string); + } catch (e) { + return null; + } + + if (!elementName) return null; + + if (childElementName) { + return obj[elementName] && childElementName in obj[elementName] ? obj[elementName][childElementName] : null; + } else { + return elementName in obj ? obj[elementName] : null; + } + }, + getBillingCycle(billingCycle) { + // 统一转换为小写进行比较 + const cycle = billingCycle.toLowerCase(); + + switch (cycle) { + case '月': + case 'month': + case 'monthly': + case 'm': + return "M"; + case '季': + case 'quarterly': + case 'q': + return "Q"; + case '半': + case '半年': + case 'half': + case 'semi-annually': + case 'h': + return "H"; + case '年': + case 'year': + case 'annually': + case 'y': + return "Y"; + default: + return null; + } + }, + getAdjustTimezone(reference, target) { + // 获取时区 + const referenceTimezoneOffset = reference.getTimezoneOffset(); + const targetTimezoneOffset = target.getTimezoneOffset(); + + // 计算时区差异 + const timezoneDifference = (referenceTimezoneOffset - targetTimezoneOffset) * 60 * 1000; + + // 将 target 日期调整到 reference 时区 + return new Date(target.getTime() + timezoneDifference); + }, + getAutoRenewalEndDate(endDate, billingCycle) { + const expiration = new Date(endDate); + const current = this.getAdjustTimezone(new Date(endDate), new Date()); + + // 如果 expiration 无效,返回 null 并记录日志 + if (isNaN(expiration.getTime())) { + console.error("getAutoRenewalEndDate: Invalid expiration format"); + } + + const result = { + flag: 1, // 1表示需要更新 + check: 0, // 判断逻辑标记 + count: 0 // 周期累计计数 + }; + + // 如果当前时间还没到到期时间,直接返回到期时间 + if (current < expiration) { + result.flag = 0; + result.date = expiration; + return result; + } + + let nextExpiration = new Date(expiration); // 初始化为原到期时间 + let newExpirationMonth = expiration.getMonth(); // 获取expiration初始月份 + + switch (billingCycle.toUpperCase()) { + case 'M': // 月度 + nextExpiration.setFullYear(current.getFullYear()); + + const monthCheck = current < new Date(current.getFullYear(), current.getMonth(), expiration.getDate()); + + // 检查当前月是否在有效期内 + if (monthCheck) { + nextExpiration.setMonth(current.getMonth()); + result.check = 1; + } else { + nextExpiration.setMonth(current.getMonth() + 1); + result.check = 2; + } + break; + + case 'Q': // 季度 + nextExpiration.setFullYear(current.getFullYear()); + + // 每次增加 3 个月,直到新的月份大于当前月份 + while (newExpirationMonth < current.getMonth()) { + newExpirationMonth += 3; + result.count += 1; + } + + // nextExpiration设置获取到的新月份 + nextExpiration.setMonth(newExpirationMonth); + + const quarterlyCheck = current < nextExpiration; + + // 检查新月份是否在有效期内 + if (quarterlyCheck) { + result.check = 3; + } else { + nextExpiration.setMonth(current.getMonth() + 3); + result.check = 4; + result.count += 1; + } + break; + + case 'H': // 半年 + nextExpiration.setFullYear(current.getFullYear()); + + // 每次增加 6 个月,直到新的月份大于当前月份 + while (newExpirationMonth < current.getMonth()) { + newExpirationMonth += 6; + result.count += 1; + } + + // nextExpiration设置获取到的新月份 + nextExpiration.setMonth(newExpirationMonth); + + const halfCheck = current < nextExpiration; + + // 检查新月份是否在有效期内 + if (halfCheck) { + result.check = 5; + } else { + nextExpiration.setMonth(current.getMonth() + 6); + result.check = 6; + result.count += 1; + } + break; + + case 'Y': // 年度 + const yearCheck = current < new Date(current.getFullYear(), expiration.getMonth(), expiration.getDate()); + + // 如果当前时间比这一年有效期早,则到期为本年 + if (yearCheck) { + nextExpiration.setFullYear(current.getFullYear()); + result.check = 7; + } else { + // 否则推到下一年 + nextExpiration.setFullYear(current.getFullYear() + 1); + result.check = 8; + } + break; + + default: + throw new Error("Invalid billing cycle"); + } + + // 保持原到期时间的时分秒 + nextExpiration.setHours(expiration.getHours()); + nextExpiration.setMinutes(expiration.getMinutes()); + nextExpiration.setSeconds(expiration.getSeconds()); + + result.date = nextExpiration; + + return result; + }, + getAutoRenewalStartDate(flag, startDate, check, count) { + //1.判断什么时候改变 2.如何改变 + const start = new Date(startDate); + const current = this.getAdjustTimezone(start, new Date()); + + // 检查 startDate 格式是否有效,若无效返回 null + if (isNaN(start.getTime())) { + console.error("getAutoRenewalStartDate: Invalid startDate format"); + } + + // 如果 flag 为 0,直接返回开始日期 + if (flag === 0) { + return start; + } + + // 初始化新的开始日期 + const newStart = new Date(start); + + switch (check) { + case 1: // 处理月份:设置为上个月 + newStart.setFullYear(current.getFullYear()); + newStart.setMonth(current.getMonth() - 1); + break; + + case 2: // 处理月份:设置为当前月 + newStart.setFullYear(current.getFullYear()); + newStart.setMonth(current.getMonth()); + break; + + case 3: + case 4: // 处理季度 + newStart.setFullYear(current.getFullYear()); + newStart.setMonth(start.getMonth() + 3 * count); + break; + + case 5: + case 6: // 处理半年 + newStart.setFullYear(current.getFullYear()); + newStart.setMonth(start.getMonth() + 6 * count); + break; + + case 7: // 处理年份:设置为上一年 + newStart.setFullYear(current.getFullYear() - 1); + break; + + case 8: // 处理年份:保持当前年份 + newStart.setFullYear(current.getFullYear()); + break; + + default: // 默认处理:直接返回 + return; + } + + return newStart; + }, + getRemainingFormat(online, note) { + if (!note) return null; + const startDate = this.getNoteElementValue(note, "billingDataMod", "startDate"); + const endDate = this.getNoteElementValue(note, "billingDataMod", "endDate"); + + // 检查 startDate 和 endDate 是否有效 + if (!startDate || !endDate || typeof startDate !== 'string' || typeof endDate !== 'string') { + console.error("getRemainingFormat: Invalid startDate or endDate in note"); + return null; // 如果无效,返回 null 或其他错误处理逻辑 + } + + //处理特殊时间格式 + if (startDate.includes('0000-00-00') || endDate.includes("0000-00-00")) { + return this.formatPercents(online, this.toFixed2(100)); + } else { + const percent = this.getRemainingPercent(startDate, endDate, note); + return this.formatPercents(online, this.toFixed2(percent)); + } + }, + getRemainingDays(endDate, note) { + // 检查 endDate 是否有效 + if (!endDate || typeof endDate !== 'string') { + console.error("getRemainingDays: Invalid endDate format"); + return null; + } + + // 处理特殊时间格式 + if (endDate.includes("0000-00-00")) return "lifetime"; + + // 获取当前时间,并调整时区 + const currentTime = this.getAdjustTimezone(new Date(endDate), new Date()); + + // 获取计费周期和自动续订日期 + const billingCycle = this.getNoteElementValue(note, "billingDataMod", "cycle") || "月"; + const autoEndDate = this.getAutoRenewalEndDate( + this.getNoteElementValue(note, "billingDataMod", "endDate"), + this.getBillingCycle(billingCycle) + ); + + // 检查 autoRenewal 状态 + const autoRenewal = this.getNoteElementValue(note, "billingDataMod", "autoRenewal") == 1; + + // 确定到期时间 + const end = autoRenewal ? autoEndDate.date : new Date(endDate); + + // 计算剩余天数 + const timeDiff = end - currentTime; + const daysDiff = Math.ceil(timeDiff / (1000 * 60 * 60 * 24)); + + return daysDiff; + }, + getRemainingPercent(startDate, endDate, note) { + // 检查 startDate 和 endDate 是否为有效字符串并处理特殊格式 + if (typeof startDate !== 'string' || typeof endDate !== 'string') { + console.error("getRemainingPercent: Invalid startDate or endDate format"); + return null; + } + + if (startDate.includes("0000-00-00") || endDate.includes("0000-00-00")) return 100; + + // 获取当前时间并调整时区 + const now = this.getAdjustTimezone(new Date(endDate), new Date()); + + // 获取计费周期 + const billingCycle = this.getNoteElementValue(note, "billingDataMod", "cycle") || "月"; + + // 自动获取结束日期 + const autoEndDate = this.getAutoRenewalEndDate( + this.getNoteElementValue(note, "billingDataMod", "endDate"), + this.getBillingCycle(billingCycle) + ); + + // 自动获取开始日期 + const autoStartDate = autoEndDate.flag == 1 + ? this.getAutoRenewalStartDate(autoEndDate.flag, this.getNoteElementValue(note, "billingDataMod", "startDate"), autoEndDate.check, autoEndDate.count) + : new Date(startDate); + + // 计算开始和结束时间 + const autoRenewal = this.getNoteElementValue(note, "billingDataMod", "autoRenewal") == 1; + const start = autoRenewal ? autoStartDate : new Date(startDate); + const end = autoRenewal ? autoEndDate.date : new Date(endDate); + + // 计算剩余百分比 + if (now < start) { + return 0; + } + if (now >= end) { + return 100; + } + + const totalDuration = end - start; + const elapsedDuration = now - start; + const percent = (elapsedDuration / totalDuration) * 100; + + // 确保百分比在 0-100 之间 + return Math.min(Math.max(percent, 0), 100); } } }) diff --git a/resource/template/theme-server-status/menu.html b/resource/template/theme-server-status/menu.html index 940c0da..033ff0a 100644 --- a/resource/template/theme-server-status/menu.html +++ b/resource/template/theme-server-status/menu.html @@ -15,20 +15,9 @@