实现修改密码和注销功能
This commit is contained in:
@@ -82,6 +82,10 @@ func (s *Server) Start() error {
|
|||||||
if s.config.EnableAPI {
|
if s.config.EnableAPI {
|
||||||
// 登录API端点,不需要认证
|
// 登录API端点,不需要认证
|
||||||
mux.HandleFunc("/api/login", s.handleLogin)
|
mux.HandleFunc("/api/login", s.handleLogin)
|
||||||
|
// 注销API端点,不需要认证
|
||||||
|
mux.HandleFunc("/api/logout", s.handleLogout)
|
||||||
|
// 修改密码API端点,需要认证
|
||||||
|
mux.HandleFunc("/api/change-password", s.loginRequired(s.handleChangePassword))
|
||||||
|
|
||||||
// 重定向/api到Swagger UI页面
|
// 重定向/api到Swagger UI页面
|
||||||
mux.HandleFunc("/api", s.loginRequired(func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/api", s.loginRequired(func(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -1468,3 +1472,82 @@ func (s *Server) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(map[string]string{"status": "success", "message": "登录成功"})
|
json.NewEncoder(w).Encode(map[string]string{"status": "success", "message": "登录成功"})
|
||||||
logger.Info(fmt.Sprintf("用户 %s 登录成功", loginData.Username))
|
logger.Info(fmt.Sprintf("用户 %s 登录成功", loginData.Username))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleLogout 处理注销请求
|
||||||
|
func (s *Server) handleLogout(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从Cookie中获取会话ID
|
||||||
|
cookie, err := r.Cookie("session_id")
|
||||||
|
if err == nil {
|
||||||
|
// 删除会话
|
||||||
|
s.sessionsMutex.Lock()
|
||||||
|
delete(s.sessions, cookie.Value)
|
||||||
|
s.sessionsMutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除Cookie
|
||||||
|
clearCookie := &http.Cookie{
|
||||||
|
Name: "session_id",
|
||||||
|
Value: "",
|
||||||
|
Path: "/",
|
||||||
|
Expires: time.Unix(0, 0),
|
||||||
|
HttpOnly: true,
|
||||||
|
Secure: false,
|
||||||
|
}
|
||||||
|
http.SetCookie(w, clearCookie)
|
||||||
|
|
||||||
|
// 返回成功响应
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{"status": "success", "message": "注销成功"})
|
||||||
|
logger.Info("用户注销成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleChangePassword 处理修改密码请求
|
||||||
|
func (s *Server) handleChangePassword(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析请求体
|
||||||
|
var changePasswordData struct {
|
||||||
|
CurrentPassword string `json:"currentPassword"`
|
||||||
|
NewPassword string `json:"newPassword"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&changePasswordData); err != nil {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{"error": "无效的请求体"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证当前密码
|
||||||
|
if changePasswordData.CurrentPassword != s.config.Password {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{"error": "当前密码错误"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新密码
|
||||||
|
s.config.Password = changePasswordData.NewPassword
|
||||||
|
|
||||||
|
// 保存配置到文件
|
||||||
|
if err := saveConfigToFile(s.globalConfig, "./config.json"); err != nil {
|
||||||
|
logger.Error("保存配置文件失败", "error", err)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{"error": "保存密码失败"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回成功响应
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(map[string]string{"status": "success", "message": "密码修改成功"})
|
||||||
|
logger.Info("密码修改成功")
|
||||||
|
}
|
||||||
|
|||||||
41
server.log
41
server.log
@@ -1,41 +0,0 @@
|
|||||||
2025/11/30 11:09:05 正在创建所需的文件和文件夹...
|
|
||||||
2025/11/30 11:09:05 所需文件和文件夹创建成功
|
|
||||||
time="2025-11-30T11:09:05+08:00" level=debug msg="尝试加载Shield统计数据" file=/root/dnsbak/data/shield_stats.json
|
|
||||||
time="2025-11-30T11:09:05+08:00" level=info msg="Shield计数数据加载成功" blocked_entries=0 resolved_entries=0
|
|
||||||
time="2025-11-30T11:09:05+08:00" level=info msg="从缓存加载远程规则" url="https://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/filter.txt"
|
|
||||||
time="2025-11-30T11:09:05+08:00" level=info msg="从缓存加载远程规则" url="https://gitea.amazehome.xyz/AMAZEHOME/hosts-and-Filters/raw/branch/main/hosts/adaway.txt"
|
|
||||||
time="2025-11-30T11:09:06+08:00" level=info msg="从缓存加载远程规则" url="https://gitea.amazehome.xyz/AMAZEHOME/hosts-and-Filters/raw/branch/main/list/easylist.txt"
|
|
||||||
time="2025-11-30T11:09:06+08:00" level=info msg="从缓存加载远程规则" url="https://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/rules/costomize.txt"
|
|
||||||
time="2025-11-30T11:09:06+08:00" level=info msg="从缓存加载远程规则" url="http://gitea.amazehome.xyz/AMAZEHOME/hosts-and-Filters/raw/branch/main/hosts/dsjh.txt"
|
|
||||||
time="2025-11-30T11:09:06+08:00" level=info msg="从缓存加载远程规则" url="http://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/hate-and-junk-extended.txt"
|
|
||||||
time="2025-11-30T11:09:06+08:00" level=info msg="从缓存加载远程规则" url="http://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/hosts/costomize.txt"
|
|
||||||
time="2025-11-30T11:09:06+08:00" level=info msg="从缓存加载远程规则" url="http://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/hosts/anti-remoterequests.txt"
|
|
||||||
time="2025-11-30T11:09:07+08:00" level=info msg="从缓存加载远程规则" url="http://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/rules/url-based-adguard.txt"
|
|
||||||
time="2025-11-30T11:09:07+08:00" level=info msg="从缓存加载远程规则" url="http://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/rules/ads-and-trackers.txt"
|
|
||||||
time="2025-11-30T11:09:08+08:00" level=info msg="从缓存加载远程规则" url="http://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/rules/malware.txt"
|
|
||||||
time="2025-11-30T11:09:09+08:00" level=info msg="从缓存加载远程规则" url="http://gitea.amazehome.xyz/AMAZEHOME/hosts-and-Filters/raw/branch/main/hosts/costomize.txt"
|
|
||||||
time="2025-11-30T11:09:09+08:00" level=info msg="从缓存加载远程规则" url="http://gitea.amazehome.xyz/AMAZEHOME/hosts-and-Filters/raw/branch/main/rules/AWAvenue-Ads-Rule.txt"
|
|
||||||
time="2025-11-30T11:09:09+08:00" level=info msg="从缓存加载远程规则" url="https://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/rules/cheat.txt"
|
|
||||||
time="2025-11-30T11:09:10+08:00" level=info msg="规则加载完成,域名规则: 189895, 排除规则: 653, 正则规则: 24094, hosts规则: 0"
|
|
||||||
time="2025-11-30T11:09:10+08:00" level=info msg="统计数据加载成功"
|
|
||||||
time="2025-11-30T11:09:10+08:00" level=info msg="查询日志加载成功" count=8608
|
|
||||||
time="2025-11-30T11:09:10+08:00" level=info msg="DNS服务器已启动,监听端口: 5353"
|
|
||||||
time="2025-11-30T11:09:10+08:00" level=info msg="HTTP控制台已启动,监听端口: 8081"
|
|
||||||
time="2025-11-30T11:09:10+08:00" level=info msg="DNS TCP服务器启动,监听端口: 5353"
|
|
||||||
time="2025-11-30T11:09:10+08:00" level=info msg="启动Shield计数数据自动保存功能" file=./data/shield_stats.json interval=60
|
|
||||||
time="2025-11-30T11:09:10+08:00" level=info msg="HTTP控制台服务器启动,监听地址: 0.0.0.0:8081"
|
|
||||||
time="2025-11-30T11:09:10+08:00" level=info msg="规则自动更新已启动" interval=3600
|
|
||||||
time="2025-11-30T11:09:10+08:00" level=info msg="DNS UDP服务器启动,监听端口: 5353"
|
|
||||||
time="2025-11-30T11:09:10+08:00" level=info msg="启动统计数据自动保存功能" file=data/stats.json interval=300
|
|
||||||
time="2025-11-30T11:09:10+08:00" level=error msg="DNS UDP服务器启动失败" error="listen udp :5353: bind: address already in use"
|
|
||||||
time="2025-11-30T11:09:10+08:00" level=info msg="Shield计数数据保存成功" blocked_entries=0 file=/root/dnsbak/data/shield_stats.json resolved_entries=0
|
|
||||||
2025/11/30 11:09:18 正在关闭服务...
|
|
||||||
time="2025-11-30T11:09:18+08:00" level=info msg="统计数据保存成功" file=/root/dnsbak/data/stats.json
|
|
||||||
time="2025-11-30T11:09:18+08:00" level=info msg="查询日志保存成功" file=/root/dnsbak/data/querylog.json
|
|
||||||
time="2025-11-30T11:09:18+08:00" level=info msg="DNS服务器已停止"
|
|
||||||
time="2025-11-30T11:09:18+08:00" level=error msg="HTTP控制台服务器启动失败" error="http: Server closed"
|
|
||||||
time="2025-11-30T11:09:18+08:00" level=info msg="HTTP控制台服务器已停止"
|
|
||||||
time="2025-11-30T11:09:18+08:00" level=info msg="Shield计数数据保存成功" blocked_entries=0 file=/root/dnsbak/data/shield_stats.json resolved_entries=0
|
|
||||||
time="2025-11-30T11:09:18+08:00" level=info msg="规则自动更新已停止"
|
|
||||||
2025/11/30 11:09:18 服务已关闭
|
|
||||||
time="2025-11-30T11:09:18+08:00" level=warning msg="日志系统已关闭"
|
|
||||||
@@ -149,9 +149,22 @@
|
|||||||
<button class="p-2 text-gray-500 hover:text-gray-700 rounded-full hover:bg-gray-100">
|
<button class="p-2 text-gray-500 hover:text-gray-700 rounded-full hover:bg-gray-100">
|
||||||
<i class="fa fa-bell text-lg"></i>
|
<i class="fa fa-bell text-lg"></i>
|
||||||
</button>
|
</button>
|
||||||
<div class="flex items-center">
|
<!-- 账户下拉菜单 -->
|
||||||
<img src="https://picsum.photos/id/1005/40/40" alt="用户头像" class="w-8 h-8 rounded-full">
|
<div class="relative group" id="account-dropdown">
|
||||||
<span class="ml-2 hidden md:block">管理员</span>
|
<button class="flex items-center p-2 rounded-full hover:bg-gray-100 transition-colors focus:outline-none">
|
||||||
|
<img src="https://picsum.photos/id/1005/40/40" alt="用户头像" class="w-8 h-8 rounded-full">
|
||||||
|
<span class="ml-2 hidden md:block">管理员</span>
|
||||||
|
<i class="fa fa-caret-down ml-1 text-xs hidden md:block"></i>
|
||||||
|
</button>
|
||||||
|
<!-- 下拉菜单 -->
|
||||||
|
<div class="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg py-2 z-50 hidden group-hover:block" id="account-menu">
|
||||||
|
<button id="change-password-btn" class="w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors">
|
||||||
|
<i class="fa fa-key mr-2"></i>修改密码
|
||||||
|
</button>
|
||||||
|
<button id="logout-btn" class="w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors">
|
||||||
|
<i class="fa fa-sign-out mr-2"></i>注销
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -1047,6 +1060,41 @@
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 修改密码模态框 -->
|
||||||
|
<div id="change-password-modal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden flex items-center justify-center">
|
||||||
|
<div class="bg-white rounded-lg shadow-xl w-full max-w-md p-6">
|
||||||
|
<div class="flex items-center justify-between mb-6">
|
||||||
|
<h3 class="text-xl font-semibold">修改密码</h3>
|
||||||
|
<button id="close-modal-btn" class="text-gray-500 hover:text-gray-700 focus:outline-none">
|
||||||
|
<i class="fa fa-times text-xl"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="change-password-form">
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="current-password" class="block text-sm font-medium text-gray-700 mb-1">当前密码</label>
|
||||||
|
<input type="password" id="current-password" name="currentPassword" placeholder="请输入当前密码" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label for="new-password" class="block text-sm font-medium text-gray-700 mb-1">新密码</label>
|
||||||
|
<input type="password" id="new-password" name="newPassword" placeholder="请输入新密码" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<label for="confirm-password" class="block text-sm font-medium text-gray-700 mb-1">确认密码</label>
|
||||||
|
<input type="password" id="confirm-password" name="confirmPassword" placeholder="请再次输入新密码" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" required>
|
||||||
|
<div id="password-mismatch" class="text-danger text-sm mt-1 hidden">新密码和确认密码不匹配</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-end space-x-3">
|
||||||
|
<button type="button" id="cancel-change-password" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300 transition-colors">取消</button>
|
||||||
|
<button type="submit" id="save-password-btn" class="px-4 py-2 bg-primary text-white rounded-md hover:bg-primary/90 transition-colors">保存</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 脚本 -->
|
<!-- 脚本 -->
|
||||||
<script src="js/main.js"></script>
|
<script src="js/main.js"></script>
|
||||||
<script src="js/api.js"></script>
|
<script src="js/api.js"></script>
|
||||||
|
|||||||
@@ -231,5 +231,175 @@ function formatUptime(milliseconds) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 账户功能 - 下拉菜单、注销和修改密码
|
||||||
|
function setupAccountFeatures() {
|
||||||
|
// 下拉菜单功能
|
||||||
|
const accountDropdown = document.getElementById('account-dropdown');
|
||||||
|
const accountMenu = document.getElementById('account-menu');
|
||||||
|
const changePasswordBtn = document.getElementById('change-password-btn');
|
||||||
|
const logoutBtn = document.getElementById('logout-btn');
|
||||||
|
const changePasswordModal = document.getElementById('change-password-modal');
|
||||||
|
const closeModalBtn = document.getElementById('close-modal-btn');
|
||||||
|
const cancelChangePasswordBtn = document.getElementById('cancel-change-password');
|
||||||
|
const changePasswordForm = document.getElementById('change-password-form');
|
||||||
|
const passwordMismatch = document.getElementById('password-mismatch');
|
||||||
|
const newPassword = document.getElementById('new-password');
|
||||||
|
const confirmPassword = document.getElementById('confirm-password');
|
||||||
|
|
||||||
|
// 点击外部关闭下拉菜单
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (accountDropdown && !accountDropdown.contains(e.target)) {
|
||||||
|
accountMenu.classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 点击账户区域切换下拉菜单
|
||||||
|
if (accountDropdown) {
|
||||||
|
accountDropdown.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
accountMenu.classList.toggle('hidden');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开修改密码模态框
|
||||||
|
if (changePasswordBtn) {
|
||||||
|
changePasswordBtn.addEventListener('click', () => {
|
||||||
|
accountMenu.classList.add('hidden');
|
||||||
|
changePasswordModal.classList.remove('hidden');
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭修改密码模态框
|
||||||
|
function closeModal() {
|
||||||
|
changePasswordModal.classList.add('hidden');
|
||||||
|
document.body.style.overflow = '';
|
||||||
|
changePasswordForm.reset();
|
||||||
|
passwordMismatch.classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定关闭模态框事件
|
||||||
|
if (closeModalBtn) {
|
||||||
|
closeModalBtn.addEventListener('click', closeModal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cancelChangePasswordBtn) {
|
||||||
|
cancelChangePasswordBtn.addEventListener('click', closeModal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击模态框外部关闭模态框
|
||||||
|
if (changePasswordModal) {
|
||||||
|
changePasswordModal.addEventListener('click', (e) => {
|
||||||
|
if (e.target === changePasswordModal) {
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按ESC键关闭模态框
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape' && !changePasswordModal.classList.contains('hidden')) {
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 密码匹配验证
|
||||||
|
if (newPassword && confirmPassword) {
|
||||||
|
confirmPassword.addEventListener('input', () => {
|
||||||
|
if (newPassword.value !== confirmPassword.value) {
|
||||||
|
passwordMismatch.classList.remove('hidden');
|
||||||
|
} else {
|
||||||
|
passwordMismatch.classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
newPassword.addEventListener('input', () => {
|
||||||
|
if (newPassword.value !== confirmPassword.value) {
|
||||||
|
passwordMismatch.classList.remove('hidden');
|
||||||
|
} else {
|
||||||
|
passwordMismatch.classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改密码表单提交
|
||||||
|
if (changePasswordForm) {
|
||||||
|
changePasswordForm.addEventListener('submit', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// 验证密码匹配
|
||||||
|
if (newPassword.value !== confirmPassword.value) {
|
||||||
|
passwordMismatch.classList.remove('hidden');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData(changePasswordForm);
|
||||||
|
const data = {
|
||||||
|
currentPassword: formData.get('currentPassword'),
|
||||||
|
newPassword: formData.get('newPassword')
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/change-password', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok && result.status === 'success') {
|
||||||
|
// 密码修改成功
|
||||||
|
alert('密码修改成功');
|
||||||
|
closeModal();
|
||||||
|
} else {
|
||||||
|
// 密码修改失败
|
||||||
|
alert(result.error || '密码修改失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('修改密码失败:', error);
|
||||||
|
alert('修改密码失败,请稍后重试');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注销功能
|
||||||
|
if (logoutBtn) {
|
||||||
|
logoutBtn.addEventListener('click', async () => {
|
||||||
|
try {
|
||||||
|
await fetch('/api/logout', {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 重定向到登录页面
|
||||||
|
window.location.href = '/login';
|
||||||
|
} catch (error) {
|
||||||
|
console.error('注销失败:', error);
|
||||||
|
alert('注销失败,请稍后重试');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化函数
|
||||||
|
function init() {
|
||||||
|
// 设置导航
|
||||||
|
setupNavigation();
|
||||||
|
|
||||||
|
// 设置账户功能
|
||||||
|
setupAccountFeatures();
|
||||||
|
|
||||||
|
// 初始化页面
|
||||||
|
initPageByHash();
|
||||||
|
|
||||||
|
// 添加hashchange事件监听,处理浏览器前进/后退按钮
|
||||||
|
window.addEventListener('hashchange', initPageByHash);
|
||||||
|
|
||||||
|
// 定期更新系统状态
|
||||||
|
setInterval(updateSystemStatus, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
// 页面加载完成后执行初始化
|
// 页面加载完成后执行初始化
|
||||||
window.addEventListener('DOMContentLoaded', init);
|
window.addEventListener('DOMContentLoaded', init);
|
||||||
Reference in New Issue
Block a user