V2Ray前端用户系统设计
V2Ray代理用户管理系统 - 完整实现
我理解您的需求了。您想要一个用户自助系统,让用户可以注册、登录、查看订阅信息,同时您作为管理员可以管理用户。我将为您设计一个完整的解决方案。
下面是完整的HTML代码,包含了用户前端和管理员后台,以及如何与您的sing-box脚本集成的说明:
1 |
|
功能说明
这个V2Ray用户管理系统界面包含以下核心功能:
- 系统状态监控 - 显示V2Ray服务运行状态、运行时间和版本信息
- 流量统计 - 直观展示流量使用情况和剩余量
- 用户管理 - 显示用户总数和活跃用户数,支持添加新用户
- 用户列表 - 展示所有用户的详细信息,支持编辑、复制配置和删除操作
- 配置信息 - 显示当前服务器的V2Ray配置信息
- 订阅链接 - 提供订阅链接管理和重置功能
界面采用了响应式设计,可以在各种屏幕尺寸上良好显示,并使用了符合技术产品风格的深色主题。
您可以直接复制上面的代码到HTML文件中,在浏览器中打开即可查看效果。
时间: 2025-09-16T19:23:11
👤 用户
你这个前端看起来很不错,就是如何与后端得代理服务器联系起来还有很多事情要做
时间: 2025-09-16T19:40:32
🤖 DeepSeek (deepseek-chat)
V2Ray代理前端用户系统 - 完整实现
您说得对,将前端与V2Ray后端连接起来需要更多的工作。我将设计一个更完整的系统,包含前后端交互的模拟实现。
下面是完整的代码,包含了模拟API交互、用户管理、配置生成等功能:
1 |
|
实现功能说明
这个V2Ray用户管理系统现在包含以下增强功能:
完整的用户管理功能:
- 添加新用户(通过模态框表单)
- 删除用户(带确认提示)
- 编辑用户(模拟功能)
- 复制用户配置
动态数据管理:
- 使用JavaScript对象存储用户数据
- 实时更新用户统计信息
- 动态生成表格内容
配置生成功能:
- 自动生成V2Ray配置信息
- 生成唯一的UUID用于配置
订阅链接管理:
- 复制订阅链接
- 重置订阅链接(生成新token)
用户反馈机制:
- 使用Toast消息提示操作结果
- 确认对话框防止误操作
如何连接到真实后端
要将此前端连接到真实的V2Ray后端,您需要:
- 实现API调用替代当前的模拟数据操作
- 添加用户认证和授权机制
- 实现与V2Ray配置文件的实时同步
- 添加WebSocket连接以实现实时状态更新
- 实现用户流量统计的数据可视化
这个实现提供了一个完整的前端界面,可以作为开发真实V2Ray用户管理系统的基础。
时间: 2025-09-16T19:40:32
👤 用户
实在不知道怎么链接到真实的后端,我是通过下面这个脚本实现代理的。我的想法其实很简单,就是为每一个用户生成一个代理订阅地址,给用户一个后台系统,可以自己注册,登录,可以查看自己的信息,订阅地址等等。当然作为管理员,我可以对用户管理。
脚本说明:https://233boy.com/sing-box/sing-box-script/
时间: 2025-09-16T19:53:23
🤖 DeepSeek (deepseek-chat)
V2Ray代理用户管理系统 - 完整实现
我理解您的需求了。您想要一个用户自助系统,让用户可以注册、登录、查看订阅信息,同时您作为管理员可以管理用户。我将为您设计一个完整的解决方案。
下面是完整的HTML代码,包含了用户前端和管理员后台,以及如何与您的sing-box脚本集成的说明:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>V2Ray代理用户管理系统</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
:root {
--primary-color: #3498db;
--secondary-color: #2c3e50;
--accent-color: #1abc9c;
--danger-color: #e74c3c;
--warning-color: #f39c12;
--success-color: #2ecc71;
--dark-bg: #1a2332;
--card-bg: #243145;
--text-color: #ecf0f1;
--border-color: #34495e;
--light-bg: #f5f7fa;
--light-text: #2c3e50;
--light-border: #ddd;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: var(--dark-bg);
color: var(--text-color);
line-height: 1.6;
}
.light-mode {
background-color: var(--light-bg);
color: var(--light-text);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 0;
border-bottom: 1px solid var(--border-color);
margin-bottom: 30px;
}
.light-mode header {
border-bottom-color: var(--light-border);
}
.logo {
display: flex;
align-items: center;
}
.logo i {
font-size: 28px;
color: var(--primary-color);
margin-right: 10px;
}
.logo h1 {
font-size: 24px;
font-weight: 600;
}
.user-actions {
display: flex;
align-items: center;
gap: 15px;
}
.user-info {
display: flex;
align-items: center;
}
.user-info img {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
border: 2px solid var(--primary-color);
}
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.card {
background-color: var(--card-bg);
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.light-mode .card {
background-color: white;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid var(--border-color);
}
.light-mode .card-header {
border-bottom-color: var(--light-border);
}
.card-header h2 {
font-size: 18px;
font-weight: 500;
}
.card-header i {
font-size: 20px;
color: var(--primary-color);
}
.stat {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
}
.stat-value {
font-size: 24px;
font-weight: 700;
}
.stat-label {
color: #a0aec0;
font-size: 14px;
}
.light-mode .stat-label {
color: #7f8c8d;
}
.progress-bar {
height: 8px;
background-color: var(--border-color);
border-radius: 4px;
overflow: hidden;
margin: 10px 0;
}
.light-mode .progress-bar {
background-color: var(--light-border);
}
.progress {
height: 100%;
background-color: var(--primary-color);
border-radius: 4px;
}
.btn {
display: inline-block;
padding: 8px 16px;
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
transition: background-color 0.3s;
}
.btn:hover {
background-color: #2980b9;
}
.btn-danger {
background-color: var(--danger-color);
}
.btn-danger:hover {
background-color: #c0392b;
}
.btn-success {
background-color: var(--success-color);
}
.btn-success:hover {
background-color: #27ae60;
}
.btn-warning {
background-color: var(--warning-color);
}
.btn-warning:hover {
background-color: #e67e22;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
}
th, td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
.light-mode th,
.light-mode td {
border-bottom-color: var(--light-border);
}
th {
color: #a0aec0;
font-weight: 500;
font-size: 14px;
}
.light-mode th {
color: #7f8c8d;
}
tr:hover {
background-color: rgba(255, 255, 255, 0.05);
}
.light-mode tr:hover {
background-color: rgba(0, 0, 0, 0.02);
}
.status {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.status-active {
background-color: rgba(46, 204, 113, 0.2);
color: var(--success-color);
}
.status-inactive {
background-color: rgba(231, 76, 60, 0.2);
color: var(--danger-color);
}
.actions {
display: flex;
gap: 8px;
}
.icon-btn {
background: none;
border: none;
color: #a0aec0;
cursor: pointer;
font-size: 16px;
transition: color 0.3s;
}
.light-mode .icon-btn {
color: #7f8c8d;
}
.icon-btn:hover {
color: var(--text-color);
}
.light-mode .icon-btn:hover {
color: var(--light-text);
}
.config-box {
background-color: rgba(0, 0, 0, 0.2);
border-radius: 6px;
padding: 15px;
margin: 10px 0;
font-family: monospace;
font-size: 14px;
overflow-x: auto;
}
.light-mode .config-box {
background-color: rgba(0, 0, 0, 0.05);
}
.config-box code {
color: var(--text-color);
}
.light-mode .config-box code {
color: var(--light-text);
}
.copy-btn {
margin-top: 10px;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
z-index: 1000;
justify-content: center;
align-items: center;
}
.modal-content {
background-color: var(--card-bg);
border-radius: 10px;
padding: 25px;
width: 90%;
max-width: 500px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.light-mode .modal-content {
background-color: white;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid var(--border-color);
}
.light-mode .modal-header {
border-bottom-color: var(--light-border);
}
.modal-header h2 {
font-size: 20px;
}
.close-btn {
background: none;
border: none;
color: #a0aec0;
font-size: 24px;
cursor: pointer;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 10px;
border: 1px solid var(--border-color);
border-radius: 4px;
background-color: rgba(0, 0, 0, 0.2);
color: var(--text-color);
}
.light-mode .form-group input,
.light-mode .form-group select,
.light-mode .form-group textarea {
background-color: white;
border-color: var(--light-border);
color: var(--light-text);
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 20px;
}
.toast {
position: fixed;
bottom: 20px;
right: 20px;
padding: 15px 20px;
background-color: var(--success-color);
color: white;
border-radius: 4px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
opacity: 0;
transition: opacity 0.3s;
z-index: 1100;
}
.toast.show {
opacity: 1;
}
.toast.error {
background-color: var(--danger-color);
}
.view-section {
display: none;
}
.view-section.active {
display: block;
}
.nav-tabs {
display: flex;
border-bottom: 1px solid var(--border-color);
margin-bottom: 20px;
}
.light-mode .nav-tabs {
border-bottom-color: var(--light-border);
}
.nav-tab {
padding: 10px 20px;
cursor: pointer;
border-bottom: 2px solid transparent;
}
.nav-tab.active {
border-bottom-color: var(--primary-color);
color: var(--primary-color);
font-weight: 500;
}
.theme-switch {
margin-left: 15px;
cursor: pointer;
font-size: 20px;
}
.qrcode-container {
display: flex;
justify-content: center;
margin: 20px 0;
}
#qrcode {
padding: 10px;
background: white;
border-radius: 8px;
}
@media (max-width: 768px) {
.dashboard {
grid-template-columns: 1fr;
}
header {
flex-direction: column;
text-align: center;
gap: 15px;
}
.user-actions {
justify-content: center;
}
.nav-tabs {
overflow-x: auto;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<div class="logo">
<i class="fas fa-shield-alt"></i>
<h1>V2Ray代理用户管理系统</h1>
</div>
<div class="user-actions">
<div class="user-info" id="user-info">
<img src="https://ui-avatars.com/api/?name=User&background=3498db&color=fff" alt="用户头像">
<div>
<div id="user-name">未登录</div>
<div style="font-size: 12px; color: #a0aec0;" id="user-role">请登录或注册</div>
</div>
</div>
<button class="btn" id="login-btn">登录</button>
<button class="btn btn-success" id="register-btn">注册</button>
<button class="btn btn-warning" id="logout-btn" style="display: none;">退出</button>
<button class="btn" id="admin-btn" style="display: none;">管理后台</button>
<i class="fas fa-moon theme-switch" id="theme-switch"></i>
</div>
</header>
<div class="nav-tabs">
<div class="nav-tab active" data-view="dashboard">仪表盘</div>
<div class="nav-tab" data-view="subscription">订阅信息</div>
<div class="nav-tab" data-view="usage">使用情况</div>
<div class="nav-tab" data-view="settings">设置</div>
<div class="nav-tab" data-view="admin" style="display: none;" id="admin-tab">管理面板</div>
</div>
<!-- 用户仪表盘 -->
<div class="view-section active" id="dashboard-view">
<div class="dashboard">
<div class="card">
<div class="card-header">
<h2>账户状态</h2>
<i class="fas fa-user"></i>
</div>
<div class="stat">
<div class="stat-label">账户状态</div>
<div class="status status-active" id="account-status">活跃</div>
</div>
<div class="stat">
<div class="stat-label">账户等级</div>
<div class="stat-value" id="account-level">普通用户</div>
</div>
<div class="stat">
<div class="stat-label">注册日期</div>
<div class="stat-value" id="register-date">2023-10-01</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h2>流量统计</h2>
<i class="fas fa-chart-pie"></i>
</div>
<div class="stat">
<div class="stat-label">本月使用</div>
<div class="stat-value" id="data-usage">34.2/100 GB</div>
</div>
<div class="progress-bar">
<div class="progress" style="width: 34.2%"></div>
</div>
<div class="stat">
<div class="stat-label">上传</div>
<div class="stat-value" id="upload-usage">12.4 GB</div>
</div>
<div class="stat">
<div class="stat-label">下载</div>
<div class="stat-value" id="download-usage">21.8 GB</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h2>服务信息</h2>
<i class="fas fa-server"></i>
</div>
<div class="stat">
<div class="stat-label">到期时间</div>
<div class="stat-value" id="expiry-date">2023-12-31</div>
</div>
<div class="stat">
<div class="stat-label">连接设备</div>
<div class="stat-value" id="connected-devices">3/5 台</div>
</div>
<button class="btn" id="renew-btn">续费服务</button>
</div>
</div>
</div>
<!-- 订阅信息 -->
<div class="view-section" id="subscription-view">
<div class="dashboard">
<div class="card">
<div class="card-header">
<h2>订阅链接</h2>
<i class="fas fa-link"></i>
</div>
<div class="config-box">
<code id="subscribe-link">https://your-domain.com/subscribe?token=user-token-here</code>
</div>
<button class="btn copy-btn" id="copy-link-btn"><i class="fas fa-copy"></i> 复制链接</button>
<button class="btn btn-danger" id="reset-link-btn"><i class="fas fa-sync"></i> 重置链接</button>
<div class="qrcode-container">
<div id="qrcode"></div>
</div>
</div>
<div class="card">
<div class="card-header">
<h2>客户端配置</h2>
<i class="fas fa-download"></i>
</div>
<p style="margin-bottom: 15px;">选择客户端下载配置文件:</p>
<div class="form-group">
<select id="client-select">
<option value="v2ray">V2Ray</option>
<option value="clash">Clash</option>
<option value="shadowrocket">Shadowrocket</option>
<option value="quantumult">Quantumult</option>
</select>
</div>
<button class="btn" id="download-config-btn"><i class="fas fa-download"></i> 下载配置</button>
<div style="margin-top: 20px;">
<p style="margin-bottom: 10px;">手动配置参数:</p>
<div class="config-box">
<code id="manual-config">
服务器地址: your-domain.com<br>
端口: 443<br>
用户ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx<br>
加密方式: auto<br>
传输协议: ws<br>
路径: /path
</code>
</div>
</div>
</div>
</div>
</div>
<!-- 使用情况 -->
<div class="view-section" id="usage-view">
<div class="card">
<div class="card-header">
<h2>流量使用历史</h2>
<i class="fas fa-history"></i>
</div>
<div style="height: 300px; width: 100%; background-color: rgba(0,0,0,0.1); border-radius: 8px; display: flex; justify-content: center; align-items: center;">
<p>流量图表将显示在这里</p>
</div>
</div>
<div class="card" style="margin-top: 20px;">
<div class="card-header">
<h2>连接日志</h2>
<i class="fas fa-list"></i>
</div>
<table>
<thead>
<tr>
<th>时间</th>
<th>设备</th>
<th>位置</th>
<th>持续时间</th>
<th>流量使用</th>
</tr>
</thead>
<tbody>
<tr>
<td>2023-10-15 14:30</td>
<td>Windows客户端</td>
<td>北京</td>
<td>2小时15分</td>
<td>1.2 GB</td>
</tr>
<tr>
<td>2023-10-15 10:15</td>
<td>Android手机</td>
<td>上海</td>
<td>45分钟</td>
<td>0.5 GB</td>
</tr>
<tr>
<td>2023-10-14 22:40</td>
<td>iPhone</td>
<td>广州</td>
<td>3小时20分</td>
<td>2.1 GB</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 设置 -->
<div class="view-section" id="settings-view">
<div class="dashboard">
<div class="card">
<div class="card-header">
<h2>个人资料</h2>
<i class="fas fa-user-cog"></i>
</div>
<div class="form-group">
<label for="profile-username">用户名</label>
<input type="text" id="profile-username" value="current_user">
</div>
<div class="form-group">
<label for="profile-email">邮箱地址</label>
<input type="email" id="profile-email" value="[email protected]">
</div>
<div class="form-group">
<label for="profile-password">新密码</label>
<input type="password" id="profile-password" placeholder="留空则不修改">
</div>
<button class="btn" id="save-profile-btn">保存更改</button>
</div>
<div class="card">
<div class="card-header">
<h2>通知设置</h2>
<i class="fas fa-bell"></i>
</div>
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between;">
<label for="notify-usage">流量使用提醒</label>
<input type="checkbox" id="notify-usage" checked style="width: auto;">
</div>
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between;">
<label for="notify-expiry">服务到期提醒</label>
<input type="checkbox" id="notify-expiry" checked style="width: auto;">
</div>
<div class="form-group" style="display: flex; align-items: center; justify-content: space-between;">
<label for="notify-news">产品更新通知</label>
<input type="checkbox" id="notify-news" style="width: auto;">
</div>
<button class="btn" id="save-notify-btn">保存设置</button>
</div>
</div>
</div>
<!-- 管理员面板 -->
<div class="view-section" id="admin-view">
<div class="card">
<div class="card-header">
<h2>用户管理</h2>
<i class="fas fa-users"></i>
</div>
<div style="display: flex; justify-content: space-between; margin-bottom: 15px;">
<input type="text" placeholder="搜索用户..." style="padding: 8px; width: 250px; border-radius: 4px; border: 1px solid var(--border-color); background-color: rgba(0,0,0,0.2); color: var(--text-color);">
<button class="btn btn-success" id="admin-add-user"><i class="fas fa-plus"></i> 添加用户</button>
</div>
<table>
<thead>
<tr>
<th>用户名</th>
<th>邮箱</th>
<th>状态</th>
<th>流量使用</th>
<th>到期时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>user1</td>
<td>[email protected]</td>
<td><span class="status status-active">活跃</span></td>
<td>4.2/20 GB</td>
<td>2023-12-31</td>
<td class="actions">
<button class="icon-btn"><i class="fas fa-edit"></i></button>
<button class="icon-btn"><i class="fas fa-trash"></i></button>
</td>
</tr>
<tr>
<td>user2</td>
<td>[email protected]</td>
<td><span class="status status-active">活跃</span></td>
<td>7.8/50 GB</td>
<td>2023-11-15</td>
<td class="actions">
<button class="icon-btn"><i class="fas fa-edit"></i></button>
<button class="icon-btn"><i class="fas fa-trash"></i></button>
</td>
</tr>
<tr>
<td>user3</td>
<td>[email protected]</td>
<td><span class="status status-inactive">离线</span></td>
<td>12.3/100 GB</td>
<td>2024-01-20</td>
<td class="actions">
<button class="icon-btn"><i class="fas fa-edit"></i></button>
<button class="icon-btn"><i class="fas fa-trash"></i></button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="dashboard" style="margin-top: 20px;">
<div class="card">
<div class="card-header">
<h2>系统状态</h2>
<i class="fas fa-server"></i>
</div>
<div class="stat">
<div class="stat-label">运行状态</div>
<div class="status status-active">运行中</div>
</div>
<div class="stat">
<div class="stat-label">运行时间</div>
<div class="stat-value">12天</div>
</div>
<div class="stat">
<div class="stat-label">总用户数</div>
<div class="stat-value">25</div>
</div>
<div class="stat">
<div class="stat-label">活跃用户</div>
<div class="stat-value">18</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h2>系统设置</h2>
<i class="fas fa-cog"></i>
</div>
<button class="btn" style="margin-bottom: 10px; width: 100%;">服务器配置</button>
<button class="btn" style="margin-bottom: 10px; width: 100%;">订阅设置</button>
<button class="btn" style="margin-bottom: 10px; width: 100%;">邮件模板</button>
<button class="btn btn-danger" style="width: 100%;">重启服务</button>
</div>
</div>
</div>
</div>
<!-- 登录模态框 -->
<div class="modal" id="login-modal">
<div class="modal-content">
<div class="modal-header">
<h2>用户登录</h2>
<button class="close-btn">×</button>
</div>
<form id="login-form">
<div class="form-group">
<label for="login-username">用户名或邮箱</label>
<input type="text" id="login-username" required>
</div>
<div class="form-group">
<label for="login-password">密码</label>
<input type="password" id="login-password" required>
</div>
<div class="form-actions">
<button type="button" class="btn btn-danger" id="login-cancel-btn">取消</button>
<button type="submit" class="btn">登录</button>
</div>
</form>
</div>
</div>
<!-- 注册模态框 -->
<div class="modal" id="register-modal">
<div class="modal-content">
<div class="modal-header">
<h2>用户注册</h2>
<button class="close-btn">×</button>
</div>
<form id="register-form">
<div class="form-group">
<label for="register-username">用户名</label>
<input type="text" id="register-username" required>
</div>
<div class="form-group">
<label for="register-email">邮箱地址</label>
<input type="email" id="register-email" required>
</div>
<div class="form-group">
<label for="register-password">密码</label>
<input type="password" id="register-password" required>
</div>
<div class="form-group">
<label for="register-confirm-password">确认密码</label>
<input type="password" id="register-confirm-password" required>
</div>
<div class="form-actions">
<button type="button" class="btn btn-danger" id="register-cancel-btn">取消</button>
<button type="submit" class="btn btn-success">注册</button>
</div>
</form>
</div>
</div>
<!-- 消息提示 -->
<div class="toast" id="toast"></div>
<!-- 引入QRCode生成库 -->
<script src="https://cdn.jsdelivr.net/gh/davidshimjs/qrcodejs@gh-pages/qrcode.min.js"></script>
<script>
// 用户数据和状态管理
let currentUser = null;
let isAdmin = false;
let isDarkMode = true;
// DOM 元素
const themeSwitch = document.getElementById('theme-switch');
const userInfo = document.getElementById('user-info');
const userName = document.getElementById('user-name');
const userRole = document.getElementById('user-role');
const loginBtn = document.getElementById('login-btn');
const registerBtn = document.getElementById('register-btn');
const logoutBtn = document.getElementById('logout-btn');
const adminBtn = document.getElementById('admin-btn');
const adminTab = document.getElementById('admin-tab');
const loginModal = document.getElementById('login-modal');
const registerModal = document.getElementById('register-modal');
const closeBtns = document.querySelectorAll('.close-btn');
const loginCancelBtn = document.getElementById('login-cancel-btn');
const registerCancelBtn = document.getElementById('register-cancel-btn');
const loginForm = document.getElementById('login-form');
const registerForm = document.getElementById('register-form');
const toast = document.getElementById('toast');
const navTabs = document.querySelectorAll('.nav-tab');
const viewSections = document.querySelectorAll('.view-section');
const copyLinkBtn = document.getElementById('copy-link-btn');
const resetLinkBtn = document.getElementById('reset-link-btn');
const subscribeLink = document.getElementById('subscribe-link');
const qrcodeDiv = document.getElementById('qrcode');
// 初始化页面
function initPage() {
// 检查本地存储中的用户登录状态
const savedUser = localStorage.getItem('v2rayUser');
if (savedUser) {
currentUser = JSON.parse(savedUser);
updateUIAfterLogin();
}
// 初始化QR码
generateQRCode();
// 设置事件监听器
setupEventListeners();
}
// 设置事件监听器
function setupEventListeners() {
// 主题切换
themeSwitch.addEventListener('click', toggleTheme);
// 登录注册按钮
loginBtn.addEventListener('click', () => loginModal.style.display = 'flex');
registerBtn.addEventListener('click', () => registerModal.style.display = 'flex');
logoutBtn.addEventListener('click', logout);
adminBtn.addEventListener('click', () => switchView('admin'));
// 关闭模态框
closeBtns.forEach(btn => {
btn.addEventListener('click', () => {
loginModal.style.display = 'none';
registerModal.style.display = 'none';
});
});
loginCancelBtn.addEventListener('click', () => loginModal.style.display = 'none');
registerCancelBtn.addEventListener('click', () => registerModal.style.display = 'none');
// 表单提交
loginForm.addEventListener('submit', handleLogin);
registerForm.addEventListener('submit', handleRegister);
// 导航标签
navTabs.forEach(tab => {
tab.addEventListener('click', () => {
const view = tab.getAttribute('data-view');
switchView(view);
});
});
// 订阅相关按钮
copyLinkBtn.addEventListener('click', copySubscribeLink);
resetLinkBtn.addEventListener('click', resetSubscribeLink);
}
// 切换主题
function toggleTheme() {
isDarkMode = !isDarkMode;
if (isDarkMode) {
document.body.classList.remove('light-mode');
themeSwitch.classList.remove('fa-sun');
themeSwitch.classList.add('fa-moon');
} else {
document.body.classList.add('light-mode');
themeSwitch.classList.remove('fa-moon');
themeSwitch.classList.add('fa-sun');
}
}
// 处理登录
function handleLogin(e) {
e.preventDefault();
const username = document.getElementById('login-username').value;
const password = document.getElementById('login-password').value;
// 模拟登录验证
if (username && password) {
// 检查是否是管理员
if (username === 'admin' && password === 'admin123') {
currentUser = {
username: 'admin',
email: '[email protected]',
role: 'admin',
token: 'admin-token-' + Date.now()
};
isAdmin = true;
} else {
currentUser = {
username: username,
email: username + '@example.com',
role: 'user',
token: 'user-token-' + Date.now()
};
isAdmin = false;
}
// 保存到本地存储
localStorage.setItem('v2rayUser', JSON.stringify(currentUser));
// 更新UI
updateUIAfterLogin();
// 关闭模态框
loginModal.style.display = 'none';
// 显示成功消息
showToast('登录成功!');
} else {
showToast('请输入用户名和密码', true);
}
}
// 处理注册
function handleRegister(e) {
e.preventDefault();
const username = document.getElementById('register-username').value;
const email = document.getElementById('register-email').value;
const password = document.getElementById('register-password').value;
const confirmPassword = document.getElementById('register-confirm-password').value;
if (password !== confirmPassword) {
showToast('密码不匹配', true);
return;
}
// 模拟注册过程
currentUser = {
username: username,
email: email,
role: 'user',
token: 'user-token-' +
**时间**: `2025-09-16T19:53:23`
---
---
## 对话统计
- **消息总数**: 6 条
- **处理状态**: 成功转换