Done
This commit is contained in:
43
frontend/src/views/Admin/Blacklist.vue
Normal file
43
frontend/src/views/Admin/Blacklist.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div class="blacklist">
|
||||
<h2>黑名单管理</h2>
|
||||
<div v-if="blacklist.length > 0">
|
||||
<ul>
|
||||
<li v-for="item in blacklist" :key="item.id">
|
||||
{{ item.target }} - {{ item.type }}
|
||||
<button @click="removeFromBlacklist(item.id)">移除</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<input v-model="newTarget" placeholder="输入IP或用户名">
|
||||
<button @click="addToBlacklist">加入黑名单</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import axios from 'axios'
|
||||
|
||||
const blacklist = ref([])
|
||||
const newTarget = ref('')
|
||||
|
||||
const fetchBlacklist = async () => {
|
||||
const response = await axios.get('/api/admin/blacklist')
|
||||
blacklist.value = response.data
|
||||
}
|
||||
|
||||
const addToBlacklist = async () => {
|
||||
await axios.post('/api/admin/blacklist', { target: newTarget.value })
|
||||
newTarget.value = ''
|
||||
await fetchBlacklist()
|
||||
}
|
||||
|
||||
const removeFromBlacklist = async (id) => {
|
||||
await axios.delete(`/api/admin/blacklist/${id}`)
|
||||
await fetchBlacklist()
|
||||
}
|
||||
|
||||
onMounted(fetchBlacklist)
|
||||
</script>
|
139
frontend/src/views/Admin/Dashboard.vue
Normal file
139
frontend/src/views/Admin/Dashboard.vue
Normal file
@@ -0,0 +1,139 @@
|
||||
<template>
|
||||
<div class="admin-dashboard">
|
||||
<h2>管理员面板</h2>
|
||||
<div v-if="pendingRequests.length > 0">
|
||||
<h3>待审批请求</h3>
|
||||
<table class="requests-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>用户</th>
|
||||
<th>IP 地址</th>
|
||||
<th>申请时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="req in pendingRequests" :key="req.id">
|
||||
<td>{{ req.username }}</td>
|
||||
<td>{{ req.ip }}</td>
|
||||
<td>{{ formatDate(req.created_at) }}</td>
|
||||
<td>
|
||||
<button @click="approveRequest(req.id)" class="approve-btn">批准</button>
|
||||
<button @click="rejectRequest(req.id)" class="reject-btn">拒绝</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>没有待审批的请求。</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import axios from 'axios'
|
||||
|
||||
// 待审批请求列表
|
||||
const pendingRequests = ref([])
|
||||
|
||||
// 获取待审批请求
|
||||
const fetchPendingRequests = async () => {
|
||||
try {
|
||||
const response = await axios.get('/api/admin/requests')
|
||||
pendingRequests.value = response.data
|
||||
} catch (error) {
|
||||
console.error('获取待审批请求失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 批准请求
|
||||
const approveRequest = async (requestId) => {
|
||||
try {
|
||||
await axios.post('/api/admin/approve', { request_id: requestId })
|
||||
await fetchPendingRequests()
|
||||
alert('请求已批准')
|
||||
} catch (error) {
|
||||
console.error('批准请求失败:', error)
|
||||
alert('操作失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 拒绝请求
|
||||
const rejectRequest = async (requestId) => {
|
||||
try {
|
||||
await axios.post('/api/admin/reject', { request_id: requestId })
|
||||
await fetchPendingRequests()
|
||||
alert('请求已拒绝')
|
||||
} catch (error) {
|
||||
console.error('拒绝请求失败:', error)
|
||||
alert('操作失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateString) => {
|
||||
return new Date(dateString).toLocaleString()
|
||||
}
|
||||
|
||||
// 组件加载时获取数据
|
||||
onMounted(fetchPendingRequests)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.admin-dashboard {
|
||||
padding: 20px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.requests-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.requests-table th,
|
||||
.requests-table td {
|
||||
padding: 12px;
|
||||
border: 1px solid #ddd;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.requests-table th {
|
||||
background-color: #f5f5f5;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.requests-table tr:hover {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.approve-btn {
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.approve-btn:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
|
||||
.reject-btn {
|
||||
background-color: #f44336;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.reject-btn:hover {
|
||||
background-color: #e53935;
|
||||
}
|
||||
</style>
|
32
frontend/src/views/User/Dashboard.vue
Normal file
32
frontend/src/views/User/Dashboard.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div class="dashboard">
|
||||
<h2>用户面板</h2>
|
||||
<div v-if="requests.length > 0">
|
||||
<h3>我的请求</h3>
|
||||
<ul>
|
||||
<li v-for="req in requests" :key="req.id">
|
||||
{{ req.ip }} - {{ req.status }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<button @click="requestNewIP">申请新IP</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRequestsStore } from '../stores/requests'
|
||||
|
||||
const requestsStore = useRequestsStore()
|
||||
const requests = ref([])
|
||||
|
||||
const requestNewIP = async () => {
|
||||
await axios.post('/api/user/request')
|
||||
await requestsStore.fetchApprovedRequests()
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await requestsStore.fetchApprovedRequests()
|
||||
requests.value = requestsStore.approvedRequests
|
||||
})
|
||||
</script>
|
26
frontend/src/views/User/RequestForm.vue
Normal file
26
frontend/src/views/User/RequestForm.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div class="request-form">
|
||||
<h3>申请新IP</h3>
|
||||
<form @submit.prevent="submitRequest">
|
||||
<CfTurnstile @verify="setToken" />
|
||||
<button type="submit" :disabled="!token">提交申请</button>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import axios from 'axios'
|
||||
import CfTurnstile from '../components/CfTurnstile.vue'
|
||||
|
||||
const token = ref('')
|
||||
|
||||
const setToken = (newToken) => {
|
||||
token.value = newToken
|
||||
}
|
||||
|
||||
const submitRequest = async () => {
|
||||
await axios.post('/api/user/request', { token: token.value })
|
||||
alert('申请已提交')
|
||||
}
|
||||
</script>
|
Reference in New Issue
Block a user