File size: 11,176 Bytes
8d7d15e c16f98d 8d7d15e c16f98d 8d7d15e c16f98d 8d7d15e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SP Website - 用户管理</title>
<!-- Bootstrap 5.3 CSS CDN -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome CDN for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<div id="app">
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
<div class="container-fluid">
<a class="navbar-brand d-flex align-items-center" href="/">
<img src="/static/images/ShareAPI.png" alt="API Router Logo" width="30" height="30" class="d-inline-block align-text-top me-2">
<span class="fw-bold fs-5">API Router</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link d-flex align-items-center me-3" :class="{ active: currentPath === '/admin' }" href="/admin">
<i class="fas fa-home me-1"></i> 后台首页
</a>
</li>
<li class="nav-item">
<a class="nav-link d-flex align-items-center me-3" :class="{ active: currentPath === '/admin/users' }" href="/admin/users">
<i class="fas fa-users-cog me-1"></i> 用户管理
</a>
</li>
<li class="nav-item">
<a class="nav-link d-flex align-items-center me-3" :class="{ active: currentPath === '/admin/pictures' }" href="/admin/pictures">
<i class="fas fa-images me-1"></i> 图片管理
</a>
</li>
</ul>
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
<li class="nav-item dropdown" v-if="isLoggedIn">
<a class="nav-link dropdown-toggle d-flex align-items-center me-3" href="#" id="userDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fas fa-user me-1"></i> {{ userEmail }}
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
<li><a class="dropdown-item" href="#" @click.prevent="handleShowChangePasswordModal">修改密码</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" @click.prevent="logout">退出</a></li>
</ul>
</li>
<li class="nav-item" v-else>
<a class="nav-link d-flex align-items-center" href="/login">
<i class="fas fa-user me-1"></i> 登录
</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-5 pt-5">
<h2 class="mb-4">用户管理</h2>
<!-- 用户管理模块 -->
<div class="card shadow-sm p-4 mb-4">
<h4 class="mb-3">用户管理</h4>
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="按邮箱搜索用户" v-model="userSearchQuery" @keyup.enter="fetchUsers">
<button class="btn btn-outline-secondary" type="button" @click="fetchUsers">搜索</button>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>邮箱</th>
<th>已验证</th>
<th>管理员</th>
<th>禁用</th> <!-- Add new column header -->
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="user in users" :key="user.id">
<td>{{ user.id }}</td>
<td>{{ user.email }}</td>
<td>
<span v-if="user.email_verified" class="badge bg-success">是</span>
<span v-else class="badge bg-danger">否</span>
</td>
<td>
<span v-if="user.is_admin" class="badge bg-primary">是</span>
<span v-else class="badge bg-secondary">否</span>
</td>
<td>
<span v-if="user.disabled" class="badge bg-warning">是</span> <!-- Display disabled status -->
<span v-else class="badge bg-success">否</span>
</td>
<td>{{ new Date(user.created_at).toLocaleString() }}</td>
<td>
<button class="btn btn-sm btn-info me-2" @click="editUser(user)">编辑</button>
<button class="btn btn-sm btn-danger" @click="deleteUser(user.id)">删除</button>
</td>
</tr>
</tbody>
</table>
</div>
<nav aria-label="User pagination" v-if="totalPages > 1">
<ul class="pagination justify-content-center">
<li class="page-item" :class="{ disabled: currentPage === 1 }">
<a class="page-link" href="#" @click.prevent="changePage(currentPage - 1)">上一页</a>
</li>
<li class="page-item" v-for="pageNumber in totalPages" :key="pageNumber" :class="{ active: pageNumber === currentPage }">
<a class="page-link" href="#" @click.prevent="changePage(pageNumber)">{{ pageNumber }}</a>
</li>
<li class="page-item" :class="{ disabled: currentPage === totalPages }">
<a class="page-link" href="#" @click.prevent="changePage(currentPage + 1)">下一页</a>
</li>
</ul>
</nav>
<p v-if="userMessage" class="mt-3 text-info">{{ userMessage }}</p>
</div>
<!-- 编辑用户模态框 -->
<div class="modal fade" id="editUserModal" tabindex="-1" aria-labelledby="editUserModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editUserModalLabel">编辑用户</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form @submit.prevent="saveUserChanges">
<div class="mb-3">
<label for="editUserEmail" class="form-label">邮箱</label>
<input type="email" class="form-control" id="editUserEmail" v-model="editingUser.email" required>
</div>
<div class="mb-3">
<label for="editUserPassword" class="form-label">新密码 (留空则不修改)</label>
<input type="password" class="form-control" id="editUserPassword" v-model="editingUser.password">
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="editEmailVerified" v-model="editingUser.email_verified">
<label class="form-check-label" for="editEmailVerified">邮箱已验证</label>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="editIsAdmin" v-model="editingUser.is_admin">
<label class="form-check-label" for="editIsAdmin">管理员</label>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="editIsDisabled" v-model="editingUser.disabled">
<label class="form-check-label" for="editIsDisabled">禁用</label>
</div>
<button type="submit" class="btn btn-primary" :disabled="isSavingUser">
<span v-if="isSavingUser" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
{{ isSavingUser ? '保存中...' : '保存更改' }}
</button>
</form>
</div>
</div>
</div>
</div>
</div>
<footer class="footer text-center py-3 mt-auto">
<p class="mb-0 text-muted">API Router v0.6.11-preview.6 由 JustSong 构建,源代码遵循 <a href="#" class="text-decoration-none">MIT 协议</a></p>
</footer>
</div>
<!-- Vue 3 CDN -->
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
<!-- Bootstrap 5.3 JS CDN -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<!-- Custom Vue.js App Logic -->
<script type="module" src="/static/js/admin/users.js"></script>
</body>
</html>
|