mirror of
https://github.com/Buriburizaem0n/admin-frontend-domain.git
synced 2026-05-06 13:48:55 +00:00
1349 lines
62 KiB
HTML
1349 lines
62 KiB
HTML
<!doctype html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<link
|
|
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css"
|
|
rel="stylesheet"
|
|
/>
|
|
<link
|
|
rel="stylesheet"
|
|
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"
|
|
/>
|
|
<title>NEZHA配置生成器</title>
|
|
<style>
|
|
* {
|
|
box-sizing: border-box;
|
|
margin: 0;
|
|
padding: 0;
|
|
font-family: "Arial", sans-serif;
|
|
}
|
|
|
|
body {
|
|
padding: 20px;
|
|
background-color: #f5f5f5;
|
|
}
|
|
|
|
.container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
background-color: white;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.tabs {
|
|
display: flex;
|
|
background-color: #333;
|
|
}
|
|
|
|
.tab {
|
|
padding: 15px 20px;
|
|
color: white;
|
|
cursor: pointer;
|
|
transition: background-color 0.3s;
|
|
}
|
|
|
|
.tab.active {
|
|
background-color: #4caf50;
|
|
}
|
|
|
|
.tab:hover:not(.active) {
|
|
background-color: #555;
|
|
}
|
|
|
|
.tab-content {
|
|
display: none;
|
|
padding: 20px;
|
|
}
|
|
|
|
.tab-content.active {
|
|
display: block;
|
|
}
|
|
|
|
.row {
|
|
display: flex;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.col-left {
|
|
flex: 1;
|
|
padding-right: 20px;
|
|
}
|
|
|
|
.col-right {
|
|
flex: 1;
|
|
position: relative;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
label {
|
|
display: block;
|
|
margin-bottom: 5px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
input[type="text"],
|
|
input[type="number"],
|
|
input[type="date"],
|
|
input[type="datetime-local"],
|
|
select {
|
|
width: 100%;
|
|
padding: 8px 12px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.input-group {
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
|
|
.input-group input,
|
|
.input-group select {
|
|
flex: 1;
|
|
}
|
|
|
|
.checkbox-group {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-top: 5px;
|
|
}
|
|
|
|
.checkbox-group input {
|
|
margin-right: 8px;
|
|
}
|
|
|
|
#jsonOutput,
|
|
#jsonOutput2 {
|
|
width: 100%;
|
|
height: 500px;
|
|
padding: 15px;
|
|
background-color: #f8f8f8;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
font-family: monospace;
|
|
resize: none;
|
|
white-space: pre;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
button {
|
|
padding: 10px 15px;
|
|
background-color: #4caf50;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
transition: background-color 0.3s;
|
|
}
|
|
|
|
button:hover {
|
|
background-color: #45a049;
|
|
}
|
|
|
|
.copy-btn {
|
|
position: absolute;
|
|
top: 10px;
|
|
right: 10px;
|
|
padding: 5px 10px;
|
|
font-size: 12px;
|
|
z-index: 100;
|
|
}
|
|
|
|
.tag-container {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
padding: 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
min-height: 42px;
|
|
}
|
|
|
|
.tag {
|
|
display: inline-block;
|
|
background-color: #e0e0e0;
|
|
padding: 5px 10px;
|
|
border-radius: 15px;
|
|
font-size: 12px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.tag.selected {
|
|
background-color: #4caf50;
|
|
color: white;
|
|
}
|
|
|
|
.tag-selector {
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.tag-container {
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.carrier-routes {
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.carrier-group {
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.carrier-label {
|
|
display: inline-block;
|
|
width: 50px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.section-title {
|
|
font-size: 18px;
|
|
margin-bottom: 15px;
|
|
padding-bottom: 5px;
|
|
border-bottom: 1px solid #eee;
|
|
color: #333;
|
|
}
|
|
|
|
.dark-mode .section-title {
|
|
color: #ffffff;
|
|
border-bottom-color: #4d4d4d;
|
|
}
|
|
|
|
.dark-mode .token.operator {
|
|
background: none;
|
|
}
|
|
|
|
.dark-mode code[class*="language-"] {
|
|
text-shadow: none;
|
|
}
|
|
|
|
.traffic-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr 1fr;
|
|
gap: 10px;
|
|
}
|
|
|
|
.traffic-grid > *:nth-child(1) {
|
|
grid-column: 1 / 2;
|
|
}
|
|
|
|
.traffic-grid > *:nth-child(2) {
|
|
grid-column: 2 / 3;
|
|
}
|
|
|
|
.traffic-grid > *:nth-child(3) {
|
|
grid-column: 3 / 4;
|
|
}
|
|
|
|
.traffic-grid > *:nth-child(4) {
|
|
grid-column: 2 / 3;
|
|
}
|
|
|
|
.traffic-grid > *:nth-child(5) {
|
|
grid-column: 3 / 4;
|
|
}
|
|
|
|
.traffic-grid > *:nth-child(1),
|
|
.traffic-grid > *:nth-child(4) {
|
|
width: 100%;
|
|
}
|
|
|
|
.amount-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 10px;
|
|
}
|
|
|
|
.amount-grid > *:nth-child(1) {
|
|
grid-column: 1 / 2;
|
|
}
|
|
|
|
.amount-grid > *:nth-child(2) {
|
|
grid-column: 2 / 3;
|
|
}
|
|
|
|
.amount-grid > *:nth-child(3) {
|
|
grid-column: 2 / 3;
|
|
}
|
|
|
|
.bandwidth-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 10px;
|
|
}
|
|
|
|
.bandwidth-grid > *:nth-child(1) {
|
|
grid-column: 1 / 2;
|
|
}
|
|
|
|
.bandwidth-grid > *:nth-child(2) {
|
|
grid-column: 2 / 3;
|
|
}
|
|
|
|
.bandwidth-grid > *:nth-child(3) {
|
|
grid-column: 2 / 3;
|
|
}
|
|
|
|
.dark-mode {
|
|
background-color: #1a1a1a !important;
|
|
color: #ffffff !important;
|
|
}
|
|
|
|
.dark-mode .container {
|
|
background-color: #2d2d2d;
|
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
.dark-mode input[type="text"],
|
|
.dark-mode input[type="number"],
|
|
.dark-mode input[type="date"],
|
|
.dark-mode input[type="datetime-local"],
|
|
.dark-mode select {
|
|
background-color: #3d3d3d;
|
|
color: #ffffff;
|
|
border-color: #4d4d4d;
|
|
}
|
|
|
|
.dark-mode #jsonOutput,
|
|
.dark-mode #jsonOutput2 {
|
|
background-color: #2d2d2d;
|
|
border-color: #4d4d4d;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.dark-mode .tag {
|
|
background-color: #4d4d4d;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.dark-mode .tag.selected {
|
|
background-color: #4caf50;
|
|
}
|
|
|
|
.theme-switch {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-left: auto;
|
|
padding: 0 10px;
|
|
}
|
|
|
|
#themeToggle {
|
|
background: none;
|
|
border: none;
|
|
color: #fff;
|
|
cursor: pointer;
|
|
padding: 8px;
|
|
border-radius: 50%;
|
|
transition: all 0.3s ease;
|
|
font-size: 1.2em;
|
|
}
|
|
|
|
#themeToggle:hover {
|
|
background-color: rgba(255, 255, 255, 0.1);
|
|
transform: rotate(15deg);
|
|
}
|
|
|
|
#themeToggle[data-theme="system"] .fa-sun,
|
|
#themeToggle[data-theme="system"] .fa-moon {
|
|
display: none;
|
|
}
|
|
|
|
#themeToggle[data-theme="system"] .fa-desktop {
|
|
display: inline-block;
|
|
}
|
|
|
|
#themeToggle[data-theme="dark"] .fa-sun,
|
|
#themeToggle[data-theme="dark"] .fa-desktop {
|
|
display: none;
|
|
}
|
|
|
|
#themeToggle[data-theme="dark"] .fa-moon {
|
|
display: inline-block;
|
|
}
|
|
|
|
#themeToggle[data-theme="light"] .fa-moon,
|
|
#themeToggle[data-theme="light"] .fa-desktop {
|
|
display: none;
|
|
}
|
|
|
|
#themeToggle[data-theme="light"] .fa-sun {
|
|
display: inline-block;
|
|
}
|
|
|
|
.github-link {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 10px;
|
|
padding-right: 20px;
|
|
}
|
|
|
|
.github-link a {
|
|
color: #fff;
|
|
font-size: 1.2em;
|
|
transition: color 0.3s ease;
|
|
}
|
|
|
|
.github-link a:hover {
|
|
color: #4caf50;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="container">
|
|
<div class="tabs">
|
|
<div class="tab active" onclick="switchTab(0)">公开备注代码</div>
|
|
<div class="tab" onclick="switchTab(1)">流量监控代码</div>
|
|
<div class="theme-switch">
|
|
<button id="themeToggle" onclick="toggleTheme()" data-theme="system">
|
|
<i class="fas fa-desktop"></i>
|
|
</button>
|
|
</div>
|
|
<div class="github-link">
|
|
<a
|
|
href="https://github.com/ohotto/nzcfg"
|
|
target="_blank"
|
|
title="查看GitHub源码"
|
|
>
|
|
<i class="fab fa-github"></i>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 公开备注代码 -->
|
|
<div class="tab-content active" id="publicNoteTab">
|
|
<div class="row">
|
|
<div class="col-left">
|
|
<h3 class="section-title">公开备注代码配置</h3>
|
|
|
|
<div class="form-group">
|
|
<label>时区 (timezone)</label>
|
|
<select id="timezone1">
|
|
<option value="-12:00">UTC-12:00 (贝克岛)</option>
|
|
<option value="-11:00">UTC-11:00 (萨摩亚)</option>
|
|
<option value="-10:00">UTC-10:00 (夏威夷)</option>
|
|
<option value="-09:00">UTC-09:00 (阿拉斯加)</option>
|
|
<option value="-08:00">UTC-08:00 (洛杉矶)</option>
|
|
<option value="-07:00">UTC-07:00 (丹佛)</option>
|
|
<option value="-06:00">UTC-06:00 (芝加哥)</option>
|
|
<option value="-05:00">UTC-05:00 (纽约)</option>
|
|
<option value="-04:00">UTC-04:00 (圣地亚哥)</option>
|
|
<option value="-03:00">UTC-03:00 (布宜诺斯艾利斯)</option>
|
|
<option value="-02:00">UTC-02:00 (费尔南多·迪诺罗尼亚群岛)</option>
|
|
<option value="-01:00">UTC-01:00 (佛得角)</option>
|
|
<option value="+00:00">UTC+00:00 (伦敦)</option>
|
|
<option value="+01:00">UTC+01:00 (巴黎)</option>
|
|
<option value="+02:00">UTC+02:00 (开罗)</option>
|
|
<option value="+03:00">UTC+03:00 (莫斯科)</option>
|
|
<option value="+04:00">UTC+04:00 (迪拜)</option>
|
|
<option value="+05:00">UTC+05:00 (卡拉奇)</option>
|
|
<option value="+06:00">UTC+06:00 (达卡)</option>
|
|
<option value="+07:00">UTC+07:00 (曼谷)</option>
|
|
<option value="+08:00" selected>UTC+08:00 (北京)</option>
|
|
<option value="+09:00">UTC+09:00 (东京)</option>
|
|
<option value="+10:00">UTC+10:00 (悉尼)</option>
|
|
<option value="+11:00">UTC+11:00 (所罗门群岛)</option>
|
|
<option value="+12:00">UTC+12:00 (奥克兰)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>账单起始日期 (startDate)</label>
|
|
<input type="datetime-local" id="startDate" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>账单结束日期 (endDate)</label>
|
|
<input type="datetime-local" id="endDate" />
|
|
<div class="checkbox-group">
|
|
<input
|
|
type="checkbox"
|
|
id="unlimitedEndDate"
|
|
onchange="toggleEndDate()"
|
|
/>
|
|
<label
|
|
for="unlimitedEndDate"
|
|
style="display: inline; font-weight: normal"
|
|
>无限期</label
|
|
>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>自动续费状态 (autoRenewal)</label>
|
|
<select id="autoRenewal">
|
|
<option value="1">启用 (1)</option>
|
|
<option value="0">禁用 (0)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>账单周期 (cycle)</label>
|
|
<select id="cycle" onchange="toggleCustomCycle()">
|
|
<option value="月">月</option>
|
|
<option value="年">年</option>
|
|
<option value="季">季</option>
|
|
<option value="半年">半年</option>
|
|
<option value="2年">2年</option>
|
|
<option value="3年">3年</option>
|
|
<option value="custom">自定义</option>
|
|
</select>
|
|
<input
|
|
type="text"
|
|
id="cycleCustom"
|
|
placeholder="自定义周期"
|
|
style="display: none; margin-top: 5px"
|
|
/>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>账单金额及货币单位 (amount)</label>
|
|
<div class="checkbox-group">
|
|
<input type="checkbox" id="freeamount" onchange="toggleAmount()" />
|
|
<label for="freeamount" style="display: inline; font-weight: normal"
|
|
>免费</label
|
|
>
|
|
</div>
|
|
<div class="amount-grid" id="amountInputGroup">
|
|
<input type="number" id="amountValue" placeholder="金额" />
|
|
<select id="amountCurrency" onchange="toggleCustomAmount()">
|
|
<option value="元">元</option>
|
|
<option value="CNY">CNY</option>
|
|
<option value="刀">刀</option>
|
|
<option value="USD">USD</option>
|
|
<option value="欧">欧</option>
|
|
<option value="EUR">EUR</option>
|
|
<option value="custom">自定义</option>
|
|
</select>
|
|
<input
|
|
type="text"
|
|
id="amountCustom"
|
|
placeholder="自定义货币单位"
|
|
style="display: none"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>服务器带宽信息 (bandwidth)</label>
|
|
<div class="checkbox-group">
|
|
<input
|
|
type="checkbox"
|
|
id="unlimitedBandwidth"
|
|
onchange="toggleBandwidth()"
|
|
/>
|
|
<label
|
|
for="unlimitedBandwidth"
|
|
style="display: inline; font-weight: normal"
|
|
>无限</label
|
|
>
|
|
</div>
|
|
<div class="bandwidth-grid" id="bandwidthInputGroup">
|
|
<input type="number" id="bandwidthValue" placeholder="带宽" />
|
|
<select id="bandwidthUnit" onchange="toggleCustomBandwidth()">
|
|
<option value="Gbps">Gbps</option>
|
|
<option value="Mbps">Mbps</option>
|
|
<option value="custom">自定义</option>
|
|
</select>
|
|
<input
|
|
type="text"
|
|
id="bandwidthCustom"
|
|
placeholder="自定义带宽单位"
|
|
style="display: none"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>流量配额及周期 (trafficVol)</label>
|
|
<div class="checkbox-group">
|
|
<input
|
|
type="checkbox"
|
|
id="unlimitedTraffic"
|
|
onchange="toggleTraffic()"
|
|
/>
|
|
<label
|
|
for="unlimitedTraffic"
|
|
style="display: inline; font-weight: normal"
|
|
>无限</label
|
|
>
|
|
</div>
|
|
<div class="traffic-grid" id="trafficInputGroup">
|
|
<input type="number" id="trafficValue" placeholder="流量" />
|
|
<select id="trafficUnit" onchange="toggleCustomTraffic()">
|
|
<option value="TB">TB</option>
|
|
<option value="GB">GB</option>
|
|
<option value="MB">MB</option>
|
|
<option value="custom">自定义</option>
|
|
</select>
|
|
<select id="trafficPeriod" onchange="toggleCustomTraffic()">
|
|
<option value="/月">/月</option>
|
|
<option value="/M">/M</option>
|
|
<option value="custom">自定义</option>
|
|
</select>
|
|
<input
|
|
type="text"
|
|
id="trafficUnitCustom"
|
|
placeholder="自定义流量单位"
|
|
style="display: none"
|
|
/>
|
|
<input
|
|
type="text"
|
|
id="trafficPeriodCustom"
|
|
placeholder="自定义周期"
|
|
style="display: none"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>流量类型 (trafficType)</label>
|
|
<select id="trafficType">
|
|
<option value="1">单向 (1)</option>
|
|
<option value="2">双向 (2)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>IPv4 地址数量</label>
|
|
<input type="number" id="ipv4" min="0" value="1" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>IPv6 地址数量</label>
|
|
<input type="number" id="ipv6" min="0" value="1" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>网络路由信息 (networkRoute)</label>
|
|
<select id="routeMode" onchange="toggleRouteMode()">
|
|
<option value="tags">多选标签模式</option>
|
|
<option value="carriers">分运营商线路模式</option>
|
|
</select>
|
|
|
|
<div id="selectedTags">
|
|
<div class="tag-container" id="selectedTagsContainer"></div>
|
|
<input
|
|
type="text"
|
|
id="newTagInput"
|
|
placeholder="输入标签并按回车添加"
|
|
/>
|
|
</div>
|
|
<div id="tagsMode" class="tag-selector">
|
|
<div class="tag-container" id="routeTags">
|
|
<span class="tag" onclick="toggleTag(this)">163</span>
|
|
<span class="tag" onclick="toggleTag(this)">CN2</span>
|
|
<span class="tag" onclick="toggleTag(this)">CN2GIA</span>
|
|
<span class="tag" onclick="toggleTag(this)">CMI</span>
|
|
<span class="tag" onclick="toggleTag(this)">CMIN2</span>
|
|
<span class="tag" onclick="toggleTag(this)">4837</span>
|
|
<span class="tag" onclick="toggleTag(this)">10099</span>
|
|
<span class="tag" onclick="toggleTag(this)">IEPL</span>
|
|
<span class="tag" onclick="toggleTag(this)">IPLC</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="carriersMode" class="carrier-routes" style="display: none">
|
|
<div class="carrier-group">
|
|
<span class="carrier-label">电信:</span>
|
|
</div>
|
|
<div
|
|
class="carrier-group"
|
|
style="display: flex; align-items: center; gap: 10px"
|
|
>
|
|
<span style="white-space: nowrap">去程:</span>
|
|
<select id="telecomRoute1">
|
|
<option value="">留空</option>
|
|
<option value="163">163</option>
|
|
<option value="CN2">CN2</option>
|
|
<option value="CN2GIA">CN2GIA</option>
|
|
<option value="CMI">CMI</option>
|
|
<option value="CMIN2">CMIN2</option>
|
|
<option value="4837">4837</option>
|
|
<option value="10099">10099</option>
|
|
<option value="IEPL">IEPL</option>
|
|
<option value="IPLC">IPLC</option>
|
|
</select>
|
|
</div>
|
|
<div
|
|
class="carrier-group"
|
|
style="display: flex; align-items: center; gap: 10px"
|
|
>
|
|
<span style="white-space: nowrap">回程:</span>
|
|
<select id="telecomRoute2">
|
|
<option value="">留空</option>
|
|
<option value="163">163</option>
|
|
<option value="CN2">CN2</option>
|
|
<option value="CN2GIA">CN2GIA</option>
|
|
<option value="CMI">CMI</option>
|
|
<option value="CMIN2">CMIN2</option>
|
|
<option value="4837">4837</option>
|
|
<option value="10099">10099</option>
|
|
<option value="IEPL">IEPL</option>
|
|
<option value="IPLC">IPLC</option>
|
|
</select>
|
|
</div>
|
|
<div class="carrier-group">
|
|
<span class="carrier-label">移动:</span>
|
|
</div>
|
|
<div
|
|
class="carrier-group"
|
|
style="display: flex; align-items: center; gap: 10px"
|
|
>
|
|
<span style="white-space: nowrap">去程:</span>
|
|
<select id="mobileRoute1">
|
|
<option value="">留空</option>
|
|
<option value="163">163</option>
|
|
<option value="CN2">CN2</option>
|
|
<option value="CN2GIA">CN2GIA</option>
|
|
<option value="CMI">CMI</option>
|
|
<option value="CMIN2">CMIN2</option>
|
|
<option value="4837">4837</option>
|
|
<option value="10099">10099</option>
|
|
<option value="IEPL">IEPL</option>
|
|
<option value="IPLC">IPLC</option>
|
|
</select>
|
|
</div>
|
|
<div
|
|
class="carrier-group"
|
|
style="display: flex; align-items: center; gap: 10px"
|
|
>
|
|
<span style="white-space: nowrap">回程:</span>
|
|
<select id="mobileRoute2">
|
|
<option value="">留空</option>
|
|
<option value="163">163</option>
|
|
<option value="CN2">CN2</option>
|
|
<option value="CN2GIA">CN2GIA</option>
|
|
<option value="CMI">CMI</option>
|
|
<option value="CMIN2">CMIN2</option>
|
|
<option value="4837">4837</option>
|
|
<option value="10099">10099</option>
|
|
<option value="IEPL">IEPL</option>
|
|
<option value="IPLC">IPLC</option>
|
|
</select>
|
|
</div>
|
|
<div class="carrier-group">
|
|
<span class="carrier-label">联通:</span>
|
|
</div>
|
|
<div
|
|
class="carrier-group"
|
|
style="display: flex; align-items: center; gap: 10px"
|
|
>
|
|
<span style="white-space: nowrap">去程:</span>
|
|
<select id="unicomRoute1">
|
|
<option value="">留空</option>
|
|
<option value="163">163</option>
|
|
<option value="CN2">CN2</option>
|
|
<option value="CN2GIA">CN2GIA</option>
|
|
<option value="CMI">CMI</option>
|
|
<option value="CMIN2">CMIN2</option>
|
|
<option value="4837">4837</option>
|
|
<option value="10099">10099</option>
|
|
<option value="IEPL">IEPL</option>
|
|
<option value="IPLC">IPLC</option>
|
|
</select>
|
|
</div>
|
|
<div
|
|
class="carrier-group"
|
|
style="display: flex; align-items: center; gap: 10px"
|
|
>
|
|
<span style="white-space: nowrap">回程:</span>
|
|
<select id="unicomRoute2">
|
|
<option value="">留空</option>
|
|
<option value="163">163</option>
|
|
<option value="CN2">CN2</option>
|
|
<option value="CN2GIA">CN2GIA</option>
|
|
<option value="CMI">CMI</option>
|
|
<option value="CMIN2">CMIN2</option>
|
|
<option value="4837">4837</option>
|
|
<option value="10099">10099</option>
|
|
<option value="IEPL">IEPL</option>
|
|
<option value="IPLC">IPLC</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>额外备注 (extra)</label>
|
|
<input type="text" id="extra" />
|
|
</div>
|
|
|
|
<button onclick="generatePublicNoteJSON()">生成 JSON</button>
|
|
</div>
|
|
<div class="col-right">
|
|
<button class="copy-btn" onclick="copyToClipboard('jsonOutput')">
|
|
复制到剪贴板
|
|
</button>
|
|
<pre id="jsonOutput"><code class="language-json"></code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 流量监控代码 -->
|
|
<div class="tab-content" id="trafficMonitorTab">
|
|
<div class="row">
|
|
<div class="col-left">
|
|
<h3 class="section-title">流量监控代码配置</h3>
|
|
|
|
<div class="form-group">
|
|
<label>时区 (timezone)</label>
|
|
<select id="timezone2">
|
|
<option value="-12:00">UTC-12:00 (贝克岛)</option>
|
|
<option value="-11:00">UTC-11:00 (萨摩亚)</option>
|
|
<option value="-10:00">UTC-10:00 (夏威夷)</option>
|
|
<option value="-09:00">UTC-09:00 (阿拉斯加)</option>
|
|
<option value="-08:00">UTC-08:00 (洛杉矶)</option>
|
|
<option value="-07:00">UTC-07:00 (丹佛)</option>
|
|
<option value="-06:00">UTC-06:00 (芝加哥)</option>
|
|
<option value="-05:00">UTC-05:00 (纽约)</option>
|
|
<option value="-04:00">UTC-04:00 (圣地亚哥)</option>
|
|
<option value="-03:00">UTC-03:00 (布宜诺斯艾利斯)</option>
|
|
<option value="-02:00">UTC-02:00 (费尔南多·迪诺罗尼亚群岛)</option>
|
|
<option value="-01:00">UTC-01:00 (佛得角)</option>
|
|
<option value="+00:00">UTC+00:00 (伦敦)</option>
|
|
<option value="+01:00">UTC+01:00 (巴黎)</option>
|
|
<option value="+02:00">UTC+02:00 (开罗)</option>
|
|
<option value="+03:00">UTC+03:00 (莫斯科)</option>
|
|
<option value="+04:00">UTC+04:00 (迪拜)</option>
|
|
<option value="+05:00">UTC+05:00 (卡拉奇)</option>
|
|
<option value="+06:00">UTC+06:00 (达卡)</option>
|
|
<option value="+07:00">UTC+07:00 (曼谷)</option>
|
|
<option value="+08:00" selected>UTC+08:00 (北京)</option>
|
|
<option value="+09:00">UTC+09:00 (东京)</option>
|
|
<option value="+10:00">UTC+10:00 (悉尼)</option>
|
|
<option value="+11:00">UTC+11:00 (所罗门群岛)</option>
|
|
<option value="+12:00">UTC+12:00 (奥克兰)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>监控类型 (type)</label>
|
|
<select id="monitorType">
|
|
<option value="transfer_in_cycle">仅入站流量</option>
|
|
<option value="transfer_out_cycle">仅出站流量</option>
|
|
<option value="transfer_all_cycle">双向流量总和</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>最大流量限制 (max)</label>
|
|
<div class="input-group">
|
|
<input type="number" id="maxTraffic" placeholder="流量数值" />
|
|
<select id="maxTrafficUnit">
|
|
<option value="TB">TB</option>
|
|
<option value="GB">GB</option>
|
|
<option value="MB">MB</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>统计周期开始日期 (cycle_start)</label>
|
|
<input type="datetime-local" id="cycleStart" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>统计周期数量 (cycle_interval)</label>
|
|
<input type="number" id="cycleInterval" min="1" value="1" />
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>统计周期单位 (cycle_unit)</label>
|
|
<select id="cycleUnit">
|
|
<option value="hour">小时 (hour)</option>
|
|
<option value="day">天 (day)</option>
|
|
<option value="week">周 (week)</option>
|
|
<option value="month">月 (month)</option>
|
|
<option value="year">年 (year)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group" style="display: none">
|
|
<label>是否覆盖 (cover)</label>
|
|
<select id="cover">
|
|
<option value="1">是 (1)</option>
|
|
<option value="0">否 (0)</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>监控的服务器ID (ignore)</label>
|
|
<input
|
|
type="text"
|
|
id="ignore"
|
|
placeholder="输入服务器ID,用逗号分隔,如:3,4"
|
|
/>
|
|
</div>
|
|
|
|
<button onclick="generateTrafficMonitorJSON()">生成 JSON</button>
|
|
</div>
|
|
<div class="col-right">
|
|
<button class="copy-btn" onclick="copyToClipboard('jsonOutput2')">
|
|
复制到剪贴板
|
|
</button>
|
|
<pre id="jsonOutput2"><code class="language-json"></code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function formatDateWithTimezone(dateTimeString, tz) {
|
|
return dateTimeString + ":00" + tz
|
|
}
|
|
document.getElementById("startDate").value = getNowDateTimeLocal()
|
|
document.getElementById("endDate").value = getNowDateTimeLocal()
|
|
document.getElementById("cycleStart").value = getNowDateTimeLocal()
|
|
|
|
function getNowDateTimeLocal() {
|
|
const now = new Date()
|
|
const year = now.getFullYear()
|
|
const month = String(now.getMonth() + 1).padStart(2, "0")
|
|
const day = String(now.getDate()).padStart(2, "0")
|
|
const hour = String(now.getHours()).padStart(2, "0") // 获取当前小时
|
|
const minute = String(now.getMinutes()).padStart(2, "0") // 获取当前分钟
|
|
return `${year}-${month}-${day}T${hour}:${minute}`
|
|
}
|
|
|
|
function switchTab(tabIndex) {
|
|
const tabs = document.querySelectorAll(".tab")
|
|
const tabContents = document.querySelectorAll(".tab-content")
|
|
|
|
tabs.forEach((tab, index) => {
|
|
tab.classList.remove("active")
|
|
tabContents[index].classList.remove("active")
|
|
})
|
|
|
|
tabs[tabIndex].classList.add("active")
|
|
tabContents[tabIndex].classList.add("active")
|
|
}
|
|
|
|
document.getElementById("newTagInput").addEventListener("keydown", function (event) {
|
|
const selectedTagsContainer = document.getElementById("selectedTagsContainer")
|
|
if (event.key === "Enter") {
|
|
const tagText = event.target.value.trim()
|
|
if (tagText) {
|
|
const newTag = document.createElement("span")
|
|
newTag.className = "tag"
|
|
newTag.textContent = tagText
|
|
newTag.onclick = function () {
|
|
selectedTagsContainer.removeChild(newTag)
|
|
const originalTag = Array.from(
|
|
document.querySelectorAll("#routeTags .tag"),
|
|
).find((tag) => tag.textContent === tagText)
|
|
if (originalTag) {
|
|
originalTag.classList.remove("selected")
|
|
}
|
|
}
|
|
selectedTagsContainer.appendChild(newTag)
|
|
event.target.value = ""
|
|
}
|
|
}
|
|
})
|
|
|
|
function toggleTag(tagElement) {
|
|
tagElement.classList.toggle("selected")
|
|
const selectedTagsContainer = document.getElementById("selectedTagsContainer")
|
|
const tagText = tagElement.textContent
|
|
|
|
if (tagElement.classList.contains("selected")) {
|
|
const newTag = document.createElement("span")
|
|
newTag.className = "tag"
|
|
newTag.textContent = tagText
|
|
newTag.onclick = function () {
|
|
selectedTagsContainer.removeChild(newTag)
|
|
tagElement.classList.remove("selected")
|
|
}
|
|
selectedTagsContainer.appendChild(newTag)
|
|
} else {
|
|
const tags = selectedTagsContainer.querySelectorAll(".tag")
|
|
tags.forEach((tag) => {
|
|
if (tag.textContent === tagText) {
|
|
selectedTagsContainer.removeChild(tag)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
function copyToClipboard(elementId) {
|
|
var text = document.querySelector("#" + elementId + " code").innerText
|
|
navigator.clipboard.writeText(text).then(function () {
|
|
if (window.opener) {
|
|
window.opener.postMessage(
|
|
{
|
|
type: "NZCFG_JSON",
|
|
target: elementId === "jsonOutput" ? "public_note" : "traffic",
|
|
payload: text,
|
|
},
|
|
"*",
|
|
)
|
|
alert("已生成并同步至控制面板中!")
|
|
window.close()
|
|
} else {
|
|
alert("已复制到剪贴板!")
|
|
}
|
|
})
|
|
}
|
|
|
|
function toggleEndDate() {
|
|
const unlimitedEndDateCheckbox = document.getElementById("unlimitedEndDate")
|
|
const endDateInput = document.getElementById("endDate")
|
|
|
|
if (unlimitedEndDateCheckbox.checked) {
|
|
endDateInput.disabled = true
|
|
endDateInput.value = "" // 清空当前值,因为它会被固定为无限期
|
|
} else {
|
|
endDateInput.disabled = false
|
|
endDateInput.value = getNowDateTimeLocal() // 恢复为当前日期时间
|
|
}
|
|
}
|
|
|
|
function toggleCustomCycle() {
|
|
const cycleSelect = document.getElementById("cycle")
|
|
const cycleCustomInput = document.getElementById("cycleCustom")
|
|
cycleCustomInput.style.display = cycleSelect.value === "custom" ? "block" : "none"
|
|
}
|
|
|
|
function toggleAmount() {
|
|
const freeamountCheckbox = document.getElementById("freeamount")
|
|
const amountInputGroup = document.getElementById("amountInputGroup")
|
|
const amountCustomInput = document.getElementById("amountCustom")
|
|
amountInputGroup.style.display = freeamountCheckbox.checked ? "none" : "grid"
|
|
amountCustomInput.style.display = "none"
|
|
}
|
|
|
|
function toggleCustomAmount() {
|
|
const amountCurrencySelect = document.getElementById("amountCurrency")
|
|
const amountCustomInput = document.getElementById("amountCustom")
|
|
amountCustomInput.style.display =
|
|
amountCurrencySelect.value === "custom" ? "block" : "none"
|
|
}
|
|
|
|
function toggleBandwidth() {
|
|
const unlimitedBandwidthCheckbox = document.getElementById("unlimitedBandwidth")
|
|
const bandwidthInputGroup = document.getElementById("bandwidthInputGroup")
|
|
const bandwidthCustomInput = document.getElementById("bandwidthCustom")
|
|
bandwidthInputGroup.style.display = unlimitedBandwidthCheckbox.checked
|
|
? "none"
|
|
: "grid"
|
|
bandwidthCustomInput.style.display = "none"
|
|
}
|
|
|
|
function toggleCustomBandwidth() {
|
|
const bandwidthUnitSelect = document.getElementById("bandwidthUnit")
|
|
const bandwidthCustomInput = document.getElementById("bandwidthCustom")
|
|
bandwidthCustomInput.style.display =
|
|
bandwidthUnitSelect.value === "custom" ? "block" : "none"
|
|
}
|
|
|
|
function toggleTraffic() {
|
|
const unlimitedTrafficCheckbox = document.getElementById("unlimitedTraffic")
|
|
const trafficInputGroup = document.getElementById("trafficInputGroup")
|
|
const trafficUnitCustom = document.getElementById("trafficUnitCustom")
|
|
const trafficPeriodCustom = document.getElementById("trafficPeriodCustom")
|
|
trafficInputGroup.style.display = unlimitedTrafficCheckbox.checked ? "none" : "grid"
|
|
trafficUnitCustom.style.display = "none"
|
|
trafficPeriodCustom.style.display = "none"
|
|
}
|
|
|
|
function toggleCustomTraffic() {
|
|
const trafficUnitSelect = document.getElementById("trafficUnit")
|
|
const trafficPeriodSelect = document.getElementById("trafficPeriod")
|
|
const trafficUnitCustom = document.getElementById("trafficUnitCustom")
|
|
const trafficPeriodCustom = document.getElementById("trafficPeriodCustom")
|
|
|
|
if (
|
|
trafficUnitSelect.value === "custom" &&
|
|
trafficPeriodSelect.value === "custom"
|
|
) {
|
|
trafficUnitCustom.style.display = "block"
|
|
trafficPeriodCustom.style.display = "block"
|
|
} else if (
|
|
trafficUnitSelect.value === "custom" &&
|
|
trafficPeriodSelect.value !== "custom"
|
|
) {
|
|
trafficUnitCustom.style.display = "block"
|
|
trafficPeriodCustom.style.display = "none"
|
|
} else if (
|
|
trafficUnitSelect.value !== "custom" &&
|
|
trafficPeriodSelect.value === "custom"
|
|
) {
|
|
trafficUnitCustom.style.display = "none"
|
|
trafficPeriodCustom.style.display = "block"
|
|
} else {
|
|
trafficUnitCustom.style.display = "none"
|
|
trafficPeriodCustom.style.display = "none"
|
|
}
|
|
}
|
|
|
|
function toggleRouteMode() {
|
|
const routeModeSelect = document.getElementById("routeMode")
|
|
const tagsModeDiv = document.getElementById("tagsMode")
|
|
const carriersModeDiv = document.getElementById("carriersMode")
|
|
const selectedTags = document.getElementById("selectedTags")
|
|
|
|
if (routeModeSelect.value === "tags") {
|
|
tagsModeDiv.style.display = "block"
|
|
carriersModeDiv.style.display = "none"
|
|
selectedTags.style.display = "block"
|
|
} else {
|
|
tagsModeDiv.style.display = "none"
|
|
carriersModeDiv.style.display = "block"
|
|
selectedTags.style.display = "none"
|
|
}
|
|
}
|
|
|
|
function generatePublicNoteJSON() {
|
|
const tz = document.getElementById("timezone1").value
|
|
const startDate = document.getElementById("startDate").value
|
|
const unlimitedEndDateCheckbox = document.getElementById("unlimitedEndDate")
|
|
let endDate
|
|
|
|
if (unlimitedEndDateCheckbox.checked) {
|
|
endDate = "0000-00-00T23:59:59" + tz // 设置为无限期
|
|
} else {
|
|
endDate = formatDateWithTimezone(document.getElementById("endDate").value, tz)
|
|
}
|
|
|
|
const autoRenewal = document.getElementById("autoRenewal").value
|
|
const cycleSelect = document.getElementById("cycle")
|
|
const cycleCustom = document.getElementById("cycleCustom").value
|
|
const cycle = cycleSelect.value === "custom" ? cycleCustom : cycleSelect.value
|
|
|
|
const freeamountCheckbox = document.getElementById("freeamount")
|
|
let amount = "0"
|
|
if (!freeamountCheckbox.checked) {
|
|
const amountValue = document.getElementById("amountValue").value
|
|
const amountCurrencySelect = document.getElementById("amountCurrency")
|
|
const amountCustom = document.getElementById("amountCustom").value
|
|
const amountCurrency =
|
|
amountCurrencySelect.value === "custom"
|
|
? amountCustom
|
|
: amountCurrencySelect.value
|
|
amount = amountValue + amountCurrency
|
|
}
|
|
|
|
const unlimitedBandwidthCheckbox = document.getElementById("unlimitedBandwidth")
|
|
let bandwidth = "无限"
|
|
if (!unlimitedBandwidthCheckbox.checked) {
|
|
const bandwidthValue = document.getElementById("bandwidthValue").value
|
|
const bandwidthUnitSelect = document.getElementById("bandwidthUnit")
|
|
const bandwidthCustom = document.getElementById("bandwidthCustom").value
|
|
const bandwidthUnit =
|
|
bandwidthUnitSelect.value === "custom"
|
|
? bandwidthCustom
|
|
: bandwidthUnitSelect.value
|
|
bandwidth = bandwidthValue + bandwidthUnit
|
|
}
|
|
|
|
const unlimitedTrafficCheckbox = document.getElementById("unlimitedTraffic")
|
|
let trafficVol = "无限"
|
|
if (!unlimitedTrafficCheckbox.checked) {
|
|
const trafficValue = document.getElementById("trafficValue").value
|
|
const trafficUnitSelect = document.getElementById("trafficUnit")
|
|
const trafficPeriodSelect = document.getElementById("trafficPeriod")
|
|
const trafficUnitCustom = document.getElementById("trafficUnitCustom").value
|
|
const trafficPeriodCustom = document.getElementById("trafficPeriodCustom").value
|
|
let trafficUnit =
|
|
trafficUnitSelect.value === "custom"
|
|
? trafficUnitCustom
|
|
: trafficUnitSelect.value
|
|
let trafficPeriod =
|
|
trafficPeriodSelect.value === "custom"
|
|
? trafficPeriodCustom
|
|
: trafficPeriodSelect.value
|
|
trafficVol = trafficValue + trafficUnit + trafficPeriod
|
|
}
|
|
|
|
const trafficType = document.getElementById("trafficType").value
|
|
const ipv4 = document.getElementById("ipv4").value
|
|
const ipv6 = document.getElementById("ipv6").value
|
|
let networkRoute = ""
|
|
|
|
const routeModeSelect = document.getElementById("routeMode")
|
|
if (routeModeSelect.value === "tags") {
|
|
const selectedTagsContainer = document.getElementById("selectedTagsContainer")
|
|
const selectedTags = Array.from(
|
|
selectedTagsContainer.querySelectorAll(".tag"),
|
|
).map((tag) => tag.textContent)
|
|
networkRoute = selectedTags.join("|")
|
|
} else {
|
|
const telecomRoute1 = document.getElementById("telecomRoute1").value
|
|
const telecomRoute2 = document.getElementById("telecomRoute2").value
|
|
const mobileRoute1 = document.getElementById("mobileRoute1").value
|
|
const mobileRoute2 = document.getElementById("mobileRoute2").value
|
|
const unicomRoute1 = document.getElementById("unicomRoute1").value
|
|
const unicomRoute2 = document.getElementById("unicomRoute2").value
|
|
|
|
const telecomRoutes = [telecomRoute1, telecomRoute2]
|
|
.filter((route) => route !== "")
|
|
.join("|")
|
|
const mobileRoutes = [mobileRoute1, mobileRoute2]
|
|
.filter((route) => route !== "")
|
|
.join("|")
|
|
const unicomRoutes = [unicomRoute1, unicomRoute2]
|
|
.filter((route) => route !== "")
|
|
.join("|")
|
|
|
|
const routes = []
|
|
if (telecomRoutes) routes.push(`电信${telecomRoutes}`)
|
|
if (mobileRoutes) routes.push(`移动${mobileRoutes}`)
|
|
if (unicomRoutes) routes.push(`联通${unicomRoutes}`)
|
|
|
|
networkRoute = routes.join(" ")
|
|
}
|
|
|
|
const extra = document.getElementById("extra").value
|
|
|
|
const jsonOutput = document.getElementById("jsonOutput")
|
|
const json = {
|
|
billingDataMod: {
|
|
startDate: formatDateWithTimezone(startDate, tz),
|
|
endDate: endDate, // 使用经过处理的endDate
|
|
autoRenewal: autoRenewal,
|
|
cycle: cycle,
|
|
amount: amount,
|
|
},
|
|
planDataMod: {
|
|
bandwidth: bandwidth,
|
|
trafficVol: trafficVol,
|
|
trafficType: trafficType,
|
|
IPv4: ipv4,
|
|
IPv6: ipv6,
|
|
networkRoute: networkRoute,
|
|
extra: extra,
|
|
},
|
|
}
|
|
document.querySelector("#jsonOutput code").innerHTML = Prism.highlight(
|
|
JSON.stringify(json, null, 2),
|
|
Prism.languages.json,
|
|
"json",
|
|
)
|
|
}
|
|
|
|
function generateTrafficMonitorJSON() {
|
|
const tz = document.getElementById("timezone2").value
|
|
const monitorType = document.getElementById("monitorType").value
|
|
const maxTrafficValue = document.getElementById("maxTraffic").value
|
|
const maxTrafficUnit = document.getElementById("maxTrafficUnit").value
|
|
const cycleStart = document.getElementById("cycleStart").value
|
|
const cycleInterval = document.getElementById("cycleInterval").value
|
|
const cycleUnit = document.getElementById("cycleUnit").value
|
|
const cover = document.getElementById("cover").value
|
|
const ignore = document.getElementById("ignore").value
|
|
|
|
let maxBytes = parseFloat(maxTrafficValue)
|
|
if (maxTrafficUnit === "GB") {
|
|
maxBytes *= 1024 * 1024 * 1024
|
|
} else if (maxTrafficUnit === "TB") {
|
|
maxBytes *= 1024 * 1024 * 1024 * 1024
|
|
} else {
|
|
// MB
|
|
maxBytes *= 1024 * 1024
|
|
}
|
|
|
|
const ignoreArray = ignore
|
|
.split(",")
|
|
.map((item) => item.trim())
|
|
.filter((item) => item !== "")
|
|
const ignoreObject = {}
|
|
ignoreArray.forEach((item) => {
|
|
ignoreObject[item] = true
|
|
})
|
|
|
|
const jsonOutput2 = document.getElementById("jsonOutput2")
|
|
const json = [
|
|
{
|
|
type: monitorType,
|
|
max: maxBytes,
|
|
cycle_start: formatDateWithTimezone(cycleStart, tz),
|
|
cycle_interval: parseInt(cycleInterval),
|
|
cycle_unit: cycleUnit,
|
|
cover: parseInt(cover),
|
|
ignore: ignoreObject,
|
|
},
|
|
]
|
|
document.querySelector("#jsonOutput2 code").innerHTML = Prism.highlight(
|
|
JSON.stringify(json, null, 2),
|
|
Prism.languages.json,
|
|
"json",
|
|
)
|
|
}
|
|
|
|
function toggleTheme() {
|
|
const body = document.body
|
|
const themeToggle = document.getElementById("themeToggle")
|
|
const currentTheme = themeToggle.getAttribute("data-theme")
|
|
let newTheme
|
|
|
|
if (currentTheme === "system") {
|
|
newTheme = "light"
|
|
body.classList.remove("dark-mode")
|
|
themeToggle.innerHTML = '<i class="fas fa-sun"></i>'
|
|
} else if (currentTheme === "light") {
|
|
newTheme = "dark"
|
|
body.classList.add("dark-mode")
|
|
themeToggle.innerHTML = '<i class="fas fa-moon"></i>'
|
|
} else {
|
|
newTheme = "system"
|
|
themeToggle.innerHTML = '<i class="fas fa-desktop"></i>'
|
|
applySystemTheme()
|
|
}
|
|
|
|
themeToggle.setAttribute("data-theme", newTheme)
|
|
localStorage.setItem("theme-preference", newTheme)
|
|
}
|
|
|
|
function applySystemTheme() {
|
|
const isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
document.body.classList.toggle("dark-mode", isDarkMode)
|
|
}
|
|
|
|
function setupSystemThemeListener() {
|
|
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)")
|
|
mediaQuery.addEventListener("change", (e) => {
|
|
const themeToggle = document.getElementById("themeToggle")
|
|
if (themeToggle.getAttribute("data-theme") === "system") {
|
|
document.body.classList.toggle("dark-mode", e.matches)
|
|
}
|
|
})
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
const themeToggle = document.getElementById("themeToggle")
|
|
const savedTheme = localStorage.getItem("theme-preference") || "system"
|
|
themeToggle.setAttribute("data-theme", savedTheme)
|
|
|
|
if (savedTheme === "system") {
|
|
themeToggle.innerHTML = '<i class="fas fa-desktop"></i>'
|
|
applySystemTheme()
|
|
} else if (savedTheme === "dark") {
|
|
themeToggle.innerHTML = '<i class="fas fa-moon"></i>'
|
|
document.body.classList.add("dark-mode")
|
|
} else {
|
|
themeToggle.innerHTML = '<i class="fas fa-sun"></i>'
|
|
document.body.classList.remove("dark-mode")
|
|
}
|
|
|
|
setupSystemThemeListener()
|
|
|
|
// Initialize end date checkbox state
|
|
toggleEndDate()
|
|
})
|
|
</script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-json.min.js"></script>
|
|
</body>
|
|
</html>
|