update
Browse files- static/admin.html +49 -1
- static/admin/pictures.html +47 -1
- static/admin/users.html +57 -8
- static/components/admin_navbar.html +60 -31
- static/js/admin.js +17 -117
- static/js/admin/pictures.js +14 -28
- static/js/admin/users.js +12 -26
- static/js/components/AdminNavbar.js +59 -0
static/admin.html
CHANGED
|
@@ -13,7 +13,53 @@
|
|
| 13 |
</head>
|
| 14 |
<body>
|
| 15 |
<div id="app">
|
| 16 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
<div class="container mt-5 pt-5">
|
| 19 |
<h2 class="mb-4">后台管理</h2>
|
|
@@ -31,3 +77,5 @@
|
|
| 31 |
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
| 32 |
<!-- Custom Vue.js App Logic -->
|
| 33 |
<script type="module" src="/static/js/admin.js"></script>
|
|
|
|
|
|
|
|
|
| 13 |
</head>
|
| 14 |
<body>
|
| 15 |
<div id="app">
|
| 16 |
+
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
|
| 17 |
+
<div class="container-fluid">
|
| 18 |
+
<a class="navbar-brand d-flex align-items-center" href="/">
|
| 19 |
+
<img src="/static/images/ShareAPI.png" alt="API Router Logo" width="30" height="30" class="d-inline-block align-text-top me-2">
|
| 20 |
+
<span class="fw-bold fs-5">API Router</span>
|
| 21 |
+
</a>
|
| 22 |
+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
| 23 |
+
<span class="navbar-toggler-icon"></span>
|
| 24 |
+
</button>
|
| 25 |
+
<div class="collapse navbar-collapse" id="navbarNav">
|
| 26 |
+
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
| 27 |
+
<li class="nav-item">
|
| 28 |
+
<a class="nav-link d-flex align-items-center me-3" :class="{ active: currentPath === '/admin' }" href="/admin">
|
| 29 |
+
<i class="fas fa-home me-1"></i> 后台首页
|
| 30 |
+
</a>
|
| 31 |
+
</li>
|
| 32 |
+
<li class="nav-item">
|
| 33 |
+
<a class="nav-link d-flex align-items-center me-3" :class="{ active: currentPath === '/admin/users' }" href="/admin/users">
|
| 34 |
+
<i class="fas fa-users-cog me-1"></i> 用户管理
|
| 35 |
+
</a>
|
| 36 |
+
</li>
|
| 37 |
+
<li class="nav-item">
|
| 38 |
+
<a class="nav-link d-flex align-items-center me-3" :class="{ active: currentPath === '/admin/pictures' }" href="/admin/pictures">
|
| 39 |
+
<i class="fas fa-images me-1"></i> 图片管理
|
| 40 |
+
</a>
|
| 41 |
+
</li>
|
| 42 |
+
</ul>
|
| 43 |
+
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
|
| 44 |
+
<li class="nav-item dropdown" v-if="isLoggedIn">
|
| 45 |
+
<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">
|
| 46 |
+
<i class="fas fa-user me-1"></i> {{ userEmail }}
|
| 47 |
+
</a>
|
| 48 |
+
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
| 49 |
+
<li><a class="dropdown-item" href="#" @click.prevent="handleShowChangePasswordModal">修改密码</a></li>
|
| 50 |
+
<li><hr class="dropdown-divider"></li>
|
| 51 |
+
<li><a class="dropdown-item" href="#" @click.prevent="logout">退出</a></li>
|
| 52 |
+
</ul>
|
| 53 |
+
</li>
|
| 54 |
+
<li class="nav-item" v-else>
|
| 55 |
+
<a class="nav-link d-flex align-items-center" href="/login">
|
| 56 |
+
<i class="fas fa-user me-1"></i> 登录
|
| 57 |
+
</a>
|
| 58 |
+
</li>
|
| 59 |
+
</ul>
|
| 60 |
+
</div>
|
| 61 |
+
</div>
|
| 62 |
+
</nav>
|
| 63 |
|
| 64 |
<div class="container mt-5 pt-5">
|
| 65 |
<h2 class="mb-4">后台管理</h2>
|
|
|
|
| 77 |
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
| 78 |
<!-- Custom Vue.js App Logic -->
|
| 79 |
<script type="module" src="/static/js/admin.js"></script>
|
| 80 |
+
</body>
|
| 81 |
+
</html>
|
static/admin/pictures.html
CHANGED
|
@@ -13,7 +13,53 @@
|
|
| 13 |
</head>
|
| 14 |
<body>
|
| 15 |
<div id="app">
|
| 16 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
<div class="container mt-5 pt-5">
|
| 19 |
<h2 class="mb-4">图片管理</h2>
|
|
|
|
| 13 |
</head>
|
| 14 |
<body>
|
| 15 |
<div id="app">
|
| 16 |
+
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
|
| 17 |
+
<div class="container-fluid">
|
| 18 |
+
<a class="navbar-brand d-flex align-items-center" href="/">
|
| 19 |
+
<img src="/static/images/ShareAPI.png" alt="API Router Logo" width="30" height="30" class="d-inline-block align-text-top me-2">
|
| 20 |
+
<span class="fw-bold fs-5">API Router</span>
|
| 21 |
+
</a>
|
| 22 |
+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
| 23 |
+
<span class="navbar-toggler-icon"></span>
|
| 24 |
+
</button>
|
| 25 |
+
<div class="collapse navbar-collapse" id="navbarNav">
|
| 26 |
+
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
| 27 |
+
<li class="nav-item">
|
| 28 |
+
<a class="nav-link d-flex align-items-center me-3" :class="{ active: currentPath === '/admin' }" href="/admin">
|
| 29 |
+
<i class="fas fa-home me-1"></i> 后台首页
|
| 30 |
+
</a>
|
| 31 |
+
</li>
|
| 32 |
+
<li class="nav-item">
|
| 33 |
+
<a class="nav-link d-flex align-items-center me-3" :class="{ active: currentPath === '/admin/users' }" href="/admin/users">
|
| 34 |
+
<i class="fas fa-users-cog me-1"></i> 用户管理
|
| 35 |
+
</a>
|
| 36 |
+
</li>
|
| 37 |
+
<li class="nav-item">
|
| 38 |
+
<a class="nav-link d-flex align-items-center me-3" :class="{ active: currentPath === '/admin/pictures' }" href="/admin/pictures">
|
| 39 |
+
<i class="fas fa-images me-1"></i> 图片管理
|
| 40 |
+
</a>
|
| 41 |
+
</li>
|
| 42 |
+
</ul>
|
| 43 |
+
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
|
| 44 |
+
<li class="nav-item dropdown" v-if="isLoggedIn">
|
| 45 |
+
<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">
|
| 46 |
+
<i class="fas fa-user me-1"></i> {{ userEmail }}
|
| 47 |
+
</a>
|
| 48 |
+
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
| 49 |
+
<li><a class="dropdown-item" href="#" @click.prevent="handleShowChangePasswordModal">修改密码</a></li>
|
| 50 |
+
<li><hr class="dropdown-divider"></li>
|
| 51 |
+
<li><a class="dropdown-item" href="#" @click.prevent="logout">退出</a></li>
|
| 52 |
+
</ul>
|
| 53 |
+
</li>
|
| 54 |
+
<li class="nav-item" v-else>
|
| 55 |
+
<a class="nav-link d-flex align-items-center" href="/login">
|
| 56 |
+
<i class="fas fa-user me-1"></i> 登录
|
| 57 |
+
</a>
|
| 58 |
+
</li>
|
| 59 |
+
</ul>
|
| 60 |
+
</div>
|
| 61 |
+
</div>
|
| 62 |
+
</nav>
|
| 63 |
|
| 64 |
<div class="container mt-5 pt-5">
|
| 65 |
<h2 class="mb-4">图片管理</h2>
|
static/admin/users.html
CHANGED
|
@@ -13,7 +13,53 @@
|
|
| 13 |
</head>
|
| 14 |
<body>
|
| 15 |
<div id="app">
|
| 16 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
<div class="container mt-5 pt-5">
|
| 19 |
<h2 class="mb-4">用户管理</h2>
|
|
@@ -108,18 +154,21 @@
|
|
| 108 |
<div class="mb-3 form-check">
|
| 109 |
<input type="checkbox" class="form-check-input" id="editIsDisabled" v-model="editingUser.disabled">
|
| 110 |
<label class="form-check-label" for="editIsDisabled">禁用</label>
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
|
|
|
| 117 |
</div>
|
| 118 |
</div>
|
| 119 |
</div>
|
| 120 |
</div>
|
|
|
|
|
|
|
|
|
|
| 121 |
</div>
|
| 122 |
-
</div>
|
| 123 |
|
| 124 |
<!-- Vue 3 CDN -->
|
| 125 |
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
|
|
|
|
| 13 |
</head>
|
| 14 |
<body>
|
| 15 |
<div id="app">
|
| 16 |
+
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
|
| 17 |
+
<div class="container-fluid">
|
| 18 |
+
<a class="navbar-brand d-flex align-items-center" href="/">
|
| 19 |
+
<img src="/static/images/ShareAPI.png" alt="API Router Logo" width="30" height="30" class="d-inline-block align-text-top me-2">
|
| 20 |
+
<span class="fw-bold fs-5">API Router</span>
|
| 21 |
+
</a>
|
| 22 |
+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
| 23 |
+
<span class="navbar-toggler-icon"></span>
|
| 24 |
+
</button>
|
| 25 |
+
<div class="collapse navbar-collapse" id="navbarNav">
|
| 26 |
+
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
| 27 |
+
<li class="nav-item">
|
| 28 |
+
<a class="nav-link d-flex align-items-center me-3" :class="{ active: currentPath === '/admin' }" href="/admin">
|
| 29 |
+
<i class="fas fa-home me-1"></i> 后台首页
|
| 30 |
+
</a>
|
| 31 |
+
</li>
|
| 32 |
+
<li class="nav-item">
|
| 33 |
+
<a class="nav-link d-flex align-items-center me-3" :class="{ active: currentPath === '/admin/users' }" href="/admin/users">
|
| 34 |
+
<i class="fas fa-users-cog me-1"></i> 用户管理
|
| 35 |
+
</a>
|
| 36 |
+
</li>
|
| 37 |
+
<li class="nav-item">
|
| 38 |
+
<a class="nav-link d-flex align-items-center me-3" :class="{ active: currentPath === '/admin/pictures' }" href="/admin/pictures">
|
| 39 |
+
<i class="fas fa-images me-1"></i> 图片管理
|
| 40 |
+
</a>
|
| 41 |
+
</li>
|
| 42 |
+
</ul>
|
| 43 |
+
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
|
| 44 |
+
<li class="nav-item dropdown" v-if="isLoggedIn">
|
| 45 |
+
<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">
|
| 46 |
+
<i class="fas fa-user me-1"></i> {{ userEmail }}
|
| 47 |
+
</a>
|
| 48 |
+
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
| 49 |
+
<li><a class="dropdown-item" href="#" @click.prevent="handleShowChangePasswordModal">修改密码</a></li>
|
| 50 |
+
<li><hr class="dropdown-divider"></li>
|
| 51 |
+
<li><a class="dropdown-item" href="#" @click.prevent="logout">退出</a></li>
|
| 52 |
+
</ul>
|
| 53 |
+
</li>
|
| 54 |
+
<li class="nav-item" v-else>
|
| 55 |
+
<a class="nav-link d-flex align-items-center" href="/login">
|
| 56 |
+
<i class="fas fa-user me-1"></i> 登录
|
| 57 |
+
</a>
|
| 58 |
+
</li>
|
| 59 |
+
</ul>
|
| 60 |
+
</div>
|
| 61 |
+
</div>
|
| 62 |
+
</nav>
|
| 63 |
|
| 64 |
<div class="container mt-5 pt-5">
|
| 65 |
<h2 class="mb-4">用户管理</h2>
|
|
|
|
| 154 |
<div class="mb-3 form-check">
|
| 155 |
<input type="checkbox" class="form-check-input" id="editIsDisabled" v-model="editingUser.disabled">
|
| 156 |
<label class="form-check-label" for="editIsDisabled">禁用</label>
|
| 157 |
+
</div>
|
| 158 |
+
<button type="submit" class="btn btn-primary" :disabled="isSavingUser">
|
| 159 |
+
<span v-if="isSavingUser" class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
| 160 |
+
{{ isSavingUser ? '保存中...' : '保存更改' }}
|
| 161 |
+
</button>
|
| 162 |
+
</form>
|
| 163 |
+
</div>
|
| 164 |
</div>
|
| 165 |
</div>
|
| 166 |
</div>
|
| 167 |
</div>
|
| 168 |
+
<footer class="footer text-center py-3 mt-auto">
|
| 169 |
+
<p class="mb-0 text-muted">API Router v0.6.11-preview.6 由 JustSong 构建,源代码遵循 <a href="#" class="text-decoration-none">MIT 协议</a></p>
|
| 170 |
+
</footer>
|
| 171 |
</div>
|
|
|
|
| 172 |
|
| 173 |
<!-- Vue 3 CDN -->
|
| 174 |
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
|
static/components/admin_navbar.html
CHANGED
|
@@ -1,32 +1,61 @@
|
|
| 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 |
</div>
|
| 31 |
-
</
|
| 32 |
-
</
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<template>
|
| 2 |
+
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
|
| 3 |
+
<div class="container-fluid">
|
| 4 |
+
<a class="navbar-brand d-flex align-items-center" href="/">
|
| 5 |
+
<img src="/static/images/ShareAPI.png" alt="API Router Logo" width="30" height="30" class="d-inline-block align-text-top me-2">
|
| 6 |
+
<span class="fw-bold fs-5">API Router</span>
|
| 7 |
+
</a>
|
| 8 |
+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
| 9 |
+
<span class="navbar-toggler-icon"></span>
|
| 10 |
+
</button>
|
| 11 |
+
<div class="collapse navbar-collapse" id="navbarNav">
|
| 12 |
+
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
| 13 |
+
<li class="nav-item">
|
| 14 |
+
<a class="nav-link d-flex align-items-center me-3" href="/admin/users">
|
| 15 |
+
<i class="fas fa-users-cog me-1"></i> 用户管理
|
| 16 |
+
</a>
|
| 17 |
+
</li>
|
| 18 |
+
<li class="nav-item">
|
| 19 |
+
<a class="nav-link d-flex align-items-center me-3" href="/admin/pictures">
|
| 20 |
+
<i class="fas fa-images me-1"></i> 图片管理
|
| 21 |
+
</a>
|
| 22 |
+
</li>
|
| 23 |
+
</ul>
|
| 24 |
+
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
|
| 25 |
+
<li class="nav-item dropdown" v-if="isLoggedIn">
|
| 26 |
+
<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">
|
| 27 |
+
<i class="fas fa-user me-1"></i> {{ userEmail }}
|
| 28 |
+
</a>
|
| 29 |
+
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
| 30 |
+
<li><a class="dropdown-item" href="#" @click.prevent="emitShowChangePasswordModal">修改密码</a></li>
|
| 31 |
+
<li><hr class="dropdown-divider"></li>
|
| 32 |
+
<li><a class="dropdown-item" href="#" @click.prevent="emitLogout">退出</a></li>
|
| 33 |
+
</ul>
|
| 34 |
+
</li>
|
| 35 |
+
<li class="nav-item" v-else>
|
| 36 |
+
<a class="nav-link d-flex align-items-center" href="/login">
|
| 37 |
+
<i class="fas fa-user me-1"></i> 登录
|
| 38 |
+
</a>
|
| 39 |
+
</li>
|
| 40 |
+
</ul>
|
| 41 |
+
</div>
|
| 42 |
</div>
|
| 43 |
+
</nav>
|
| 44 |
+
</template>
|
| 45 |
+
|
| 46 |
+
<script>
|
| 47 |
+
export default {
|
| 48 |
+
props: {
|
| 49 |
+
isLoggedIn: Boolean,
|
| 50 |
+
userEmail: String
|
| 51 |
+
},
|
| 52 |
+
methods: {
|
| 53 |
+
emitLogout() {
|
| 54 |
+
this.$emit('logout');
|
| 55 |
+
},
|
| 56 |
+
emitShowChangePasswordModal() {
|
| 57 |
+
this.$emit('show-change-password-modal');
|
| 58 |
+
}
|
| 59 |
+
}
|
| 60 |
+
}
|
| 61 |
+
</script>
|
static/js/admin.js
CHANGED
|
@@ -5,148 +5,48 @@ const { createApp } = Vue;
|
|
| 5 |
|
| 6 |
import { authData, authMethods, authMounted } from './auth.js';
|
| 7 |
import { proxyData, proxyMethods } from './proxy.js';
|
| 8 |
-
import { appData, appMethods, appMounted } from './store.js';
|
| 9 |
-
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
| 11 |
data() {
|
| 12 |
return {
|
| 13 |
...authData(),
|
| 14 |
...proxyData(),
|
| 15 |
-
...appData(),
|
| 16 |
selectedFile: null,
|
| 17 |
isUploading: false,
|
| 18 |
uploadMessage: '',
|
| 19 |
uploadedImageUrl: '',
|
| 20 |
-
showImageUpload: false,
|
| 21 |
-
showUserManagement: false,
|
| 22 |
-
showImageManagement: false,
|
| 23 |
-
images: [],
|
| 24 |
-
imageMessage: '',
|
| 25 |
-
adminNavbar: '', // 存储 admin_navbar.html 的内容
|
| 26 |
-
|
| 27 |
-
// 用户管理相关数据 (已移除,现在在 users.js 中处理)
|
| 28 |
-
// users: [],
|
| 29 |
-
// userSearchQuery: '',
|
| 30 |
-
// currentPage: 1,
|
| 31 |
-
// pageSize: 10,
|
| 32 |
-
// totalUsers: 0,
|
| 33 |
-
// totalPages: 0,
|
| 34 |
-
// userMessage: '',
|
| 35 |
-
// editingUser: {
|
| 36 |
-
// id: null,
|
| 37 |
-
// email: '',
|
| 38 |
-
// password: '',
|
| 39 |
-
// email_verified: false,
|
| 40 |
-
// is_admin: false,
|
| 41 |
-
// disabled: false
|
| 42 |
-
// },
|
| 43 |
-
// isSavingUser: false,
|
| 44 |
-
// editUserModal: null // Bootstrap Modal instance
|
| 45 |
};
|
| 46 |
},
|
| 47 |
methods: {
|
| 48 |
...authMethods(this),
|
| 49 |
...proxyMethods(),
|
| 50 |
-
...appMethods(this),
|
| 51 |
handleFileUpload(event) {
|
| 52 |
this.selectedFile = event.target.files[0];
|
| 53 |
},
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
// this.uploadMessage = '请选择一个文件。';
|
| 57 |
-
// return;
|
| 58 |
-
// }
|
| 59 |
-
|
| 60 |
-
// this.isUploading = true;
|
| 61 |
-
// this.uploadMessage = '';
|
| 62 |
-
// this.uploadedImageUrl = '';
|
| 63 |
-
|
| 64 |
-
// const formData = new FormData();
|
| 65 |
-
// formData.append('file', this.selectedFile);
|
| 66 |
-
|
| 67 |
-
// try {
|
| 68 |
-
// const token = localStorage.getItem('access_token');
|
| 69 |
-
// const response = await fetch('/api/admin/upload-image', {
|
| 70 |
-
// method: 'POST',
|
| 71 |
-
// headers: {
|
| 72 |
-
// 'Authorization': `Bearer ${token}`
|
| 73 |
-
// },
|
| 74 |
-
// body: formData
|
| 75 |
-
// });
|
| 76 |
-
// const data = await response.json();
|
| 77 |
-
|
| 78 |
-
// if (response.ok) {
|
| 79 |
-
// this.uploadMessage = data.message || '图片上传成功!';
|
| 80 |
-
// this.uploadedImageUrl = data.path;
|
| 81 |
-
// document.getElementById('imageUpload').value = '';
|
| 82 |
-
// this.selectedFile = null;
|
| 83 |
-
// } else {
|
| 84 |
-
// this.uploadMessage = data.detail || '图片上传失败。';
|
| 85 |
-
// }
|
| 86 |
-
// } catch (error) {
|
| 87 |
-
// this.uploadMessage = '网络错误或服务器无响应。';
|
| 88 |
-
// console.error('图片上传错误:', error);
|
| 89 |
-
// } finally {
|
| 90 |
-
// this.isUploading = false;
|
| 91 |
-
// }
|
| 92 |
-
// },
|
| 93 |
-
// async fetchImages() { // Moved to pictures.js
|
| 94 |
-
// this.imageMessage = '加载图片中...';
|
| 95 |
-
// try {
|
| 96 |
-
// const token = localStorage.getItem('access_token');
|
| 97 |
-
// const response = await fetch('/api/admin/images', {
|
| 98 |
-
// headers: {
|
| 99 |
-
// 'Authorization': `Bearer ${token}`
|
| 100 |
-
// }
|
| 101 |
-
// });
|
| 102 |
-
// const data = await response.json();
|
| 103 |
-
|
| 104 |
-
// if (response.ok) {
|
| 105 |
-
// this.images = data;
|
| 106 |
-
// this.imageMessage = '';
|
| 107 |
-
// } else {
|
| 108 |
-
// this.imageMessage = data.detail || '获取图片失败。';
|
| 109 |
-
// }
|
| 110 |
-
// } catch (error) {
|
| 111 |
-
// this.imageMessage = '网络错误或服务器无响应。';
|
| 112 |
-
// console.error('删除图片错误:', error);
|
| 113 |
-
// }
|
| 114 |
-
// },
|
| 115 |
-
async loadAdminNavbar() {
|
| 116 |
-
try {
|
| 117 |
-
const response = await fetch('/static/components/admin_navbar.html');
|
| 118 |
-
if (response.ok) {
|
| 119 |
-
this.adminNavbar = await response.text();
|
| 120 |
-
} else {
|
| 121 |
-
console.error('Failed to load admin navbar:', response.statusText);
|
| 122 |
-
}
|
| 123 |
-
} catch (error) {
|
| 124 |
-
console.error('Error loading admin navbar:', error);
|
| 125 |
-
}
|
| 126 |
-
},
|
| 127 |
-
// Navigation methods for shared navbar
|
| 128 |
-
navigateToUserManagement() {
|
| 129 |
-
window.location.href = '/admin/users';
|
| 130 |
-
},
|
| 131 |
-
navigateToImageManagement() {
|
| 132 |
-
window.location.href = '/admin/pictures'; // Navigate to pictures page
|
| 133 |
-
},
|
| 134 |
-
navigateToImageUpload() {
|
| 135 |
-
// This method is no longer directly used as image upload is part of pictures.html
|
| 136 |
-
window.location.href = '/admin/pictures';
|
| 137 |
}
|
| 138 |
},
|
| 139 |
mounted() {
|
| 140 |
authMounted(this);
|
| 141 |
appMounted(this);
|
| 142 |
-
this.loadAdminNavbar(); // Load the shared navbar
|
| 143 |
console.log('Admin app mounted!');
|
| 144 |
-
// No specific logic for admin.js as it only acts as a container now
|
| 145 |
},
|
| 146 |
watch: {
|
| 147 |
// No specific watchers needed for this page
|
| 148 |
}
|
| 149 |
-
}
|
| 150 |
|
| 151 |
-
|
| 152 |
console.log('Vue app successfully mounted to #app element.');
|
|
|
|
| 5 |
|
| 6 |
import { authData, authMethods, authMounted } from './auth.js';
|
| 7 |
import { proxyData, proxyMethods } from './proxy.js';
|
| 8 |
+
import { appData, appMethods, appMounted } from './store.js';
|
| 9 |
+
const AdminApp = {
|
| 10 |
+
components: {
|
| 11 |
+
// AdminNavbar // Register the component
|
| 12 |
+
},
|
| 13 |
+
// template: document.querySelector('#app template').innerHTML, // Removed template option
|
| 14 |
data() {
|
| 15 |
return {
|
| 16 |
...authData(),
|
| 17 |
...proxyData(),
|
| 18 |
+
...appData(),
|
| 19 |
selectedFile: null,
|
| 20 |
isUploading: false,
|
| 21 |
uploadMessage: '',
|
| 22 |
uploadedImageUrl: '',
|
| 23 |
+
showImageUpload: false,
|
| 24 |
+
showUserManagement: false,
|
| 25 |
+
showImageManagement: false,
|
| 26 |
+
images: [],
|
| 27 |
+
imageMessage: '',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
};
|
| 29 |
},
|
| 30 |
methods: {
|
| 31 |
...authMethods(this),
|
| 32 |
...proxyMethods(),
|
| 33 |
+
...appMethods(this),
|
| 34 |
handleFileUpload(event) {
|
| 35 |
this.selectedFile = event.target.files[0];
|
| 36 |
},
|
| 37 |
+
handleShowChangePasswordModal() {
|
| 38 |
+
this.showChangePasswordModal = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
}
|
| 40 |
},
|
| 41 |
mounted() {
|
| 42 |
authMounted(this);
|
| 43 |
appMounted(this);
|
|
|
|
| 44 |
console.log('Admin app mounted!');
|
|
|
|
| 45 |
},
|
| 46 |
watch: {
|
| 47 |
// No specific watchers needed for this page
|
| 48 |
}
|
| 49 |
+
};
|
| 50 |
|
| 51 |
+
createApp(AdminApp).mount('#app');
|
| 52 |
console.log('Vue app successfully mounted to #app element.');
|
static/js/admin/pictures.js
CHANGED
|
@@ -3,37 +3,30 @@ const { createApp } = Vue;
|
|
| 3 |
|
| 4 |
import { authData, authMethods, authMounted } from '../auth.js';
|
| 5 |
import { appData, appMethods, appMounted } from '../store.js';
|
|
|
|
| 6 |
|
| 7 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
data() {
|
| 9 |
return {
|
| 10 |
...authData(),
|
| 11 |
...appData(),
|
| 12 |
-
|
| 13 |
|
| 14 |
selectedFile: null,
|
| 15 |
isUploading: false,
|
| 16 |
uploadMessage: '',
|
| 17 |
uploadedImageUrl: '',
|
| 18 |
-
images: [],
|
| 19 |
-
imageMessage: '',
|
| 20 |
};
|
| 21 |
},
|
| 22 |
methods: {
|
| 23 |
...authMethods(this),
|
| 24 |
...appMethods(this),
|
| 25 |
-
async loadAdminNavbar() {
|
| 26 |
-
try {
|
| 27 |
-
const response = await fetch('/static/components/admin_navbar.html');
|
| 28 |
-
if (response.ok) {
|
| 29 |
-
this.adminNavbar = await response.text();
|
| 30 |
-
} else {
|
| 31 |
-
console.error('Failed to load admin navbar:', response.statusText);
|
| 32 |
-
}
|
| 33 |
-
} catch (error) {
|
| 34 |
-
console.error('Error loading admin navbar:', error);
|
| 35 |
-
}
|
| 36 |
-
},
|
| 37 |
handleFileUpload(event) {
|
| 38 |
this.selectedFile = event.target.files[0];
|
| 39 |
},
|
|
@@ -126,28 +119,21 @@ const app = createApp({
|
|
| 126 |
console.error('删除图片错误:', error);
|
| 127 |
}
|
| 128 |
},
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
window.location.href = '/admin/users';
|
| 132 |
-
},
|
| 133 |
-
navigateToImageManagement() {
|
| 134 |
-
// Already on image management page, no need to navigate
|
| 135 |
-
},
|
| 136 |
-
navigateToImageUpload() {
|
| 137 |
-
// Already on image management page, upload is part of this page
|
| 138 |
}
|
| 139 |
},
|
| 140 |
mounted() {
|
| 141 |
authMounted(this);
|
| 142 |
appMounted(this);
|
| 143 |
-
this.loadAdminNavbar(); // Load the shared navbar
|
| 144 |
this.fetchImages(); // Fetch images on page load
|
| 145 |
console.log('Image management app mounted!');
|
|
|
|
| 146 |
},
|
| 147 |
watch: {
|
| 148 |
// No specific watchers needed for this standalone page yet
|
| 149 |
}
|
| 150 |
-
}
|
| 151 |
|
| 152 |
-
|
| 153 |
console.log('Image management Vue app successfully mounted to #app element.');
|
|
|
|
| 3 |
|
| 4 |
import { authData, authMethods, authMounted } from '../auth.js';
|
| 5 |
import { appData, appMethods, appMounted } from '../store.js';
|
| 6 |
+
import AdminNavbar from '../components/AdminNavbar.js'; // Import the new component
|
| 7 |
|
| 8 |
+
const PictureManagementApp = {
|
| 9 |
+
components: {
|
| 10 |
+
AdminNavbar // Register the component
|
| 11 |
+
},
|
| 12 |
+
// template: document.querySelector('#app template').innerHTML, // Removed template option
|
| 13 |
data() {
|
| 14 |
return {
|
| 15 |
...authData(),
|
| 16 |
...appData(),
|
| 17 |
+
currentPath: window.location.pathname, // Add currentPath to data
|
| 18 |
|
| 19 |
selectedFile: null,
|
| 20 |
isUploading: false,
|
| 21 |
uploadMessage: '',
|
| 22 |
uploadedImageUrl: '',
|
| 23 |
+
images: [],
|
| 24 |
+
imageMessage: '',
|
| 25 |
};
|
| 26 |
},
|
| 27 |
methods: {
|
| 28 |
...authMethods(this),
|
| 29 |
...appMethods(this),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
handleFileUpload(event) {
|
| 31 |
this.selectedFile = event.target.files[0];
|
| 32 |
},
|
|
|
|
| 119 |
console.error('删除图片错误:', error);
|
| 120 |
}
|
| 121 |
},
|
| 122 |
+
handleShowChangePasswordModal() {
|
| 123 |
+
this.showChangePasswordModal = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
}
|
| 125 |
},
|
| 126 |
mounted() {
|
| 127 |
authMounted(this);
|
| 128 |
appMounted(this);
|
|
|
|
| 129 |
this.fetchImages(); // Fetch images on page load
|
| 130 |
console.log('Image management app mounted!');
|
| 131 |
+
|
| 132 |
},
|
| 133 |
watch: {
|
| 134 |
// No specific watchers needed for this standalone page yet
|
| 135 |
}
|
| 136 |
+
};
|
| 137 |
|
| 138 |
+
createApp(PictureManagementApp).mount('#app');
|
| 139 |
console.log('Image management Vue app successfully mounted to #app element.');
|
static/js/admin/users.js
CHANGED
|
@@ -3,13 +3,18 @@ const { createApp } = Vue;
|
|
| 3 |
|
| 4 |
import { authData, authMethods, authMounted } from '../auth.js';
|
| 5 |
import { appData, appMethods, appMounted } from '../store.js';
|
|
|
|
| 6 |
|
| 7 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
data() {
|
| 9 |
return {
|
| 10 |
...authData(),
|
| 11 |
...appData(),
|
| 12 |
-
|
| 13 |
|
| 14 |
// 用户管理相关数据
|
| 15 |
users: [],
|
|
@@ -138,42 +143,23 @@ const app = createApp({
|
|
| 138 |
console.error('删除用户错误:', error);
|
| 139 |
}
|
| 140 |
},
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
const response = await fetch('/static/components/admin_navbar.html');
|
| 144 |
-
if (response.ok) {
|
| 145 |
-
this.adminNavbar = await response.text();
|
| 146 |
-
} else {
|
| 147 |
-
console.error('Failed to load admin navbar:', response.statusText);
|
| 148 |
-
}
|
| 149 |
-
} catch (error) {
|
| 150 |
-
console.error('Error loading admin navbar:', error);
|
| 151 |
-
}
|
| 152 |
-
},
|
| 153 |
-
// Navigation methods for shared navbar
|
| 154 |
-
navigateToUserManagement() {
|
| 155 |
-
// Already on user management page, no need to navigate
|
| 156 |
-
},
|
| 157 |
-
navigateToImageManagement() {
|
| 158 |
-
window.location.href = '/admin/pictures'; // Navigate to pictures page
|
| 159 |
-
},
|
| 160 |
-
navigateToImageUpload() {
|
| 161 |
-
window.location.href = '/admin/pictures'; // Navigate to pictures page
|
| 162 |
}
|
| 163 |
},
|
| 164 |
mounted() {
|
| 165 |
authMounted(this);
|
| 166 |
appMounted(this);
|
| 167 |
-
this.loadAdminNavbar(); // Load the shared navbar
|
| 168 |
console.log('User management app mounted!');
|
| 169 |
|
| 170 |
this.editUserModal = new bootstrap.Modal(document.getElementById('editUserModal'));
|
| 171 |
this.fetchUsers(); // Fetch users on page load
|
|
|
|
| 172 |
},
|
| 173 |
watch: {
|
| 174 |
// No specific watchers needed for this standalone page yet
|
| 175 |
}
|
| 176 |
-
}
|
| 177 |
|
| 178 |
-
|
| 179 |
console.log('User management Vue app successfully mounted to #app element.');
|
|
|
|
| 3 |
|
| 4 |
import { authData, authMethods, authMounted } from '../auth.js';
|
| 5 |
import { appData, appMethods, appMounted } from '../store.js';
|
| 6 |
+
import AdminNavbar from '../components/AdminNavbar.js'; // Import the new component
|
| 7 |
|
| 8 |
+
const UserManagementApp = {
|
| 9 |
+
components: {
|
| 10 |
+
AdminNavbar // Register the component
|
| 11 |
+
},
|
| 12 |
+
// template: document.querySelector('#app template').innerHTML, // Removed template option
|
| 13 |
data() {
|
| 14 |
return {
|
| 15 |
...authData(),
|
| 16 |
...appData(),
|
| 17 |
+
currentPath: window.location.pathname, // Add currentPath to data
|
| 18 |
|
| 19 |
// 用户管理相关数据
|
| 20 |
users: [],
|
|
|
|
| 143 |
console.error('删除用户错误:', error);
|
| 144 |
}
|
| 145 |
},
|
| 146 |
+
handleShowChangePasswordModal() {
|
| 147 |
+
this.showChangePasswordModal = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
}
|
| 149 |
},
|
| 150 |
mounted() {
|
| 151 |
authMounted(this);
|
| 152 |
appMounted(this);
|
|
|
|
| 153 |
console.log('User management app mounted!');
|
| 154 |
|
| 155 |
this.editUserModal = new bootstrap.Modal(document.getElementById('editUserModal'));
|
| 156 |
this.fetchUsers(); // Fetch users on page load
|
| 157 |
+
|
| 158 |
},
|
| 159 |
watch: {
|
| 160 |
// No specific watchers needed for this standalone page yet
|
| 161 |
}
|
| 162 |
+
};
|
| 163 |
|
| 164 |
+
createApp(UserManagementApp).mount('#app');
|
| 165 |
console.log('User management Vue app successfully mounted to #app element.');
|
static/js/components/AdminNavbar.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// static/js/components/AdminNavbar.js
|
| 2 |
+
export default {
|
| 3 |
+
template: `
|
| 4 |
+
<nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top">
|
| 5 |
+
<div class="container-fluid">
|
| 6 |
+
<a class="navbar-brand d-flex align-items-center" href="/">
|
| 7 |
+
<img src="/static/images/ShareAPI.png" alt="API Router Logo" width="30" height="30" class="d-inline-block align-text-top me-2">
|
| 8 |
+
<span class="fw-bold fs-5">API Router</span>
|
| 9 |
+
</a>
|
| 10 |
+
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
| 11 |
+
<span class="navbar-toggler-icon"></span>
|
| 12 |
+
</button>
|
| 13 |
+
<div class="collapse navbar-collapse" id="navbarNav">
|
| 14 |
+
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
| 15 |
+
<li class="nav-item">
|
| 16 |
+
<a class="nav-link d-flex align-items-center me-3" href="/admin/users">
|
| 17 |
+
<i class="fas fa-users-cog me-1"></i> 用户管理
|
| 18 |
+
</a>
|
| 19 |
+
</li>
|
| 20 |
+
<li class="nav-item">
|
| 21 |
+
<a class="nav-link d-flex align-items-center me-3" href="/admin/pictures">
|
| 22 |
+
<i class="fas fa-images me-1"></i> 图片管理
|
| 23 |
+
</a>
|
| 24 |
+
</li>
|
| 25 |
+
</ul>
|
| 26 |
+
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
|
| 27 |
+
<li class="nav-item dropdown" v-if="isLoggedIn">
|
| 28 |
+
<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">
|
| 29 |
+
<i class="fas fa-user me-1"></i> {{ userEmail }}
|
| 30 |
+
</a>
|
| 31 |
+
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
| 32 |
+
<li><a class="dropdown-item" href="#" @click.prevent="emitShowChangePasswordModal">修改密码</a></li>
|
| 33 |
+
<li><hr class="dropdown-divider"></li>
|
| 34 |
+
<li><a class="dropdown-item" href="#" @click.prevent="emitLogout">退出</a></li>
|
| 35 |
+
</ul>
|
| 36 |
+
</li>
|
| 37 |
+
<li class="nav-item" v-else>
|
| 38 |
+
<a class="nav-link d-flex align-items-center" href="/login">
|
| 39 |
+
<i class="fas fa-user me-1"></i> 登录
|
| 40 |
+
</a>
|
| 41 |
+
</li>
|
| 42 |
+
</ul>
|
| 43 |
+
</div>
|
| 44 |
+
</div>
|
| 45 |
+
</nav>
|
| 46 |
+
`,
|
| 47 |
+
props: {
|
| 48 |
+
isLoggedIn: Boolean,
|
| 49 |
+
userEmail: String
|
| 50 |
+
},
|
| 51 |
+
methods: {
|
| 52 |
+
emitLogout() {
|
| 53 |
+
this.$emit('logout');
|
| 54 |
+
},
|
| 55 |
+
emitShowChangePasswordModal() {
|
| 56 |
+
this.$emit('show-change-password-modal');
|
| 57 |
+
}
|
| 58 |
+
}
|
| 59 |
+
}
|