mirror of
https://github.com/fish2018/GoComicMosaic.git
synced 2025-11-25 03:15:02 +08:00
待审批区增加批量拒绝功能
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
BASE_URL=https://dm.xueximeng.com
|
||||
ASSETS_PATH=../assets
|
||||
# ASSETS_PATH=../data/assets
|
||||
# ASSETS_PATH=../assets
|
||||
ASSETS_PATH=../data/assets
|
||||
@@ -2,19 +2,19 @@
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/</loc>
|
||||
<lastmod>2025-08-08</lastmod>
|
||||
<lastmod>2025-08-13</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>1</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/submit</loc>
|
||||
<lastmod>2025-08-08</lastmod>
|
||||
<lastmod>2025-08-13</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/about</loc>
|
||||
<lastmod>2025-08-08</lastmod>
|
||||
<lastmod>2025-08-13</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
@@ -37,14 +37,14 @@
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/46</loc>
|
||||
<lastmod>2025-07-25</lastmod>
|
||||
<loc>https://dm.xueximeng.com/resource/50</loc>
|
||||
<lastmod>2025-07-18</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/50</loc>
|
||||
<lastmod>2025-07-18</lastmod>
|
||||
<loc>https://dm.xueximeng.com/resource/46</loc>
|
||||
<lastmod>2025-07-25</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
@@ -55,14 +55,14 @@
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/18</loc>
|
||||
<lastmod>2025-07-19</lastmod>
|
||||
<loc>https://dm.xueximeng.com/resource/24</loc>
|
||||
<lastmod>2025-06-18</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/24</loc>
|
||||
<lastmod>2025-06-18</lastmod>
|
||||
<loc>https://dm.xueximeng.com/resource/18</loc>
|
||||
<lastmod>2025-07-19</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
@@ -74,7 +74,7 @@
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/34</loc>
|
||||
<lastmod>2025-06-27</lastmod>
|
||||
<lastmod>2025-08-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
@@ -86,13 +86,7 @@
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/30</loc>
|
||||
<lastmod>2025-06-27</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/26</loc>
|
||||
<lastmod>2025-07-18</lastmod>
|
||||
<lastmod>2025-08-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
@@ -102,9 +96,21 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/26</loc>
|
||||
<lastmod>2025-07-18</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/161</loc>
|
||||
<lastmod>2025-07-17</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/8</loc>
|
||||
<lastmod>2025-06-20</lastmod>
|
||||
<lastmod>2025-08-08</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
@@ -132,6 +138,12 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/9</loc>
|
||||
<lastmod>2025-08-08</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/28</loc>
|
||||
<lastmod>2025-06-27</lastmod>
|
||||
@@ -175,20 +187,20 @@
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/9</loc>
|
||||
<lastmod>2025-06-22</lastmod>
|
||||
<loc>https://dm.xueximeng.com/resource/184</loc>
|
||||
<lastmod>2025-07-18</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/14</loc>
|
||||
<lastmod>2025-05-30</lastmod>
|
||||
<lastmod>2025-08-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/38</loc>
|
||||
<lastmod>2025-06-25</lastmod>
|
||||
<lastmod>2025-08-12</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
@@ -204,18 +216,6 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/161</loc>
|
||||
<lastmod>2025-07-17</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/184</loc>
|
||||
<lastmod>2025-07-18</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/185</loc>
|
||||
<lastmod>2025-07-16</lastmod>
|
||||
@@ -228,6 +228,12 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/15</loc>
|
||||
<lastmod>2025-07-16</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/16</loc>
|
||||
<lastmod>2025-07-22</lastmod>
|
||||
@@ -258,6 +264,12 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/89</loc>
|
||||
<lastmod>2025-06-21</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/90</loc>
|
||||
<lastmod>2025-06-23</lastmod>
|
||||
@@ -288,12 +300,6 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/15</loc>
|
||||
<lastmod>2025-07-16</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/21</loc>
|
||||
<lastmod>2025-06-28</lastmod>
|
||||
@@ -337,7 +343,7 @@
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/89</loc>
|
||||
<loc>https://dm.xueximeng.com/resource/88</loc>
|
||||
<lastmod>2025-06-21</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
@@ -420,6 +426,12 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/35</loc>
|
||||
<lastmod>2025-06-27</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/39</loc>
|
||||
<lastmod>2025-06-27</lastmod>
|
||||
@@ -446,7 +458,7 @@
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/57</loc>
|
||||
<lastmod>2025-06-10</lastmod>
|
||||
<lastmod>2025-08-11</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
@@ -493,8 +505,8 @@
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/88</loc>
|
||||
<lastmod>2025-06-21</lastmod>
|
||||
<loc>https://dm.xueximeng.com/resource/97</loc>
|
||||
<lastmod>2025-06-19</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
@@ -510,12 +522,24 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/118</loc>
|
||||
<lastmod>2025-06-18</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/119</loc>
|
||||
<lastmod>2025-07-18</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/121</loc>
|
||||
<lastmod>2025-06-19</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/135</loc>
|
||||
<lastmod>2025-06-22</lastmod>
|
||||
@@ -534,6 +558,12 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/152</loc>
|
||||
<lastmod>2025-07-25</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/153</loc>
|
||||
<lastmod>2025-06-24</lastmod>
|
||||
@@ -578,7 +608,7 @@
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/17</loc>
|
||||
<lastmod>2025-06-15</lastmod>
|
||||
<lastmod>2025-08-12</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
@@ -594,12 +624,6 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/35</loc>
|
||||
<lastmod>2025-06-27</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/44</loc>
|
||||
<lastmod>2025-06-15</lastmod>
|
||||
@@ -662,7 +686,7 @@
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/78</loc>
|
||||
<lastmod>2025-06-25</lastmod>
|
||||
<lastmod>2025-08-11</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
@@ -696,12 +720,6 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/97</loc>
|
||||
<lastmod>2025-06-19</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/98</loc>
|
||||
<lastmod>2025-06-19</lastmod>
|
||||
@@ -750,24 +768,12 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/118</loc>
|
||||
<lastmod>2025-06-18</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/120</loc>
|
||||
<lastmod>2025-07-03</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/121</loc>
|
||||
<lastmod>2025-06-19</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/122</loc>
|
||||
<lastmod>2025-06-19</lastmod>
|
||||
@@ -870,12 +876,6 @@
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/152</loc>
|
||||
<lastmod>2025-07-25</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.9</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://dm.xueximeng.com/resource/155</loc>
|
||||
<lastmod>2025-07-02</lastmod>
|
||||
|
||||
216
frontend/public/test-settings-api.html
Normal file
216
frontend/public/test-settings-api.html
Normal file
@@ -0,0 +1,216 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>测试设置API</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
h1 {
|
||||
color: #333;
|
||||
text-align: center;
|
||||
}
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ddd;
|
||||
margin-bottom: 10px;
|
||||
font-family: monospace;
|
||||
resize: vertical;
|
||||
}
|
||||
button {
|
||||
background: #4f46e5;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 15px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
button:hover {
|
||||
background: #4338ca;
|
||||
}
|
||||
.result {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
white-space: pre-wrap;
|
||||
font-family: monospace;
|
||||
background-color: #f8f8f8;
|
||||
border: 1px solid #ddd;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
}
|
||||
.success {
|
||||
background-color: #d1fae5;
|
||||
border-color: #10b981;
|
||||
}
|
||||
.error {
|
||||
background-color: #fee2e2;
|
||||
border-color: #ef4444;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>设置API测试工具</h1>
|
||||
|
||||
<div class="card">
|
||||
<h2>测试页脚设置API</h2>
|
||||
<p>这个工具可以帮助您测试页脚设置API,发送PUT请求到 <code>/api/settings/footer</code> 端点。</p>
|
||||
|
||||
<div>
|
||||
<label for="token"><strong>认证令牌 (Bearer Token)</strong></label>
|
||||
<input type="text" id="token" style="width: 100%; padding: 8px; margin: 5px 0 15px;" placeholder="粘贴您的认证令牌">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="payload"><strong>请求数据 (JSON)</strong></label>
|
||||
<textarea id="payload" placeholder="输入JSON格式的请求数据">{
|
||||
"setting_value": {
|
||||
"links": [
|
||||
{ "text": "关于我们", "url": "/about", "type": "internal" },
|
||||
{ "text": "GitHub", "url": "https://github.com/fish2018/GoComicMosaic", "icon": "bi bi-github", "type": "external", "title": "查看GitHub源码" }
|
||||
],
|
||||
"copyright": "© 2025 美漫资源共建. 保留所有权利",
|
||||
"show_visitor_count": true
|
||||
}
|
||||
}</textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button id="sendRequest">发送PUT请求</button>
|
||||
<button id="checkToken" style="background: #059669;">检查令牌</button>
|
||||
</div>
|
||||
|
||||
<div id="result" class="result" style="display: none;">
|
||||
待发送请求...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 从localStorage中获取token
|
||||
try {
|
||||
const savedToken = localStorage.getItem('accessToken');
|
||||
if (savedToken) {
|
||||
document.getElementById('token').value = savedToken;
|
||||
console.log('已从localStorage加载令牌');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('读取localStorage失败:', e);
|
||||
}
|
||||
|
||||
// 发送请求按钮
|
||||
document.getElementById('sendRequest').addEventListener('click', async function() {
|
||||
const token = document.getElementById('token').value.trim();
|
||||
const payload = document.getElementById('payload').value.trim();
|
||||
const resultElement = document.getElementById('result');
|
||||
|
||||
resultElement.style.display = 'block';
|
||||
resultElement.className = 'result';
|
||||
resultElement.textContent = '发送请求中...';
|
||||
|
||||
if (!token) {
|
||||
resultElement.textContent = '错误: 请提供认证令牌';
|
||||
resultElement.className = 'result error';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let payloadObj = JSON.parse(payload);
|
||||
|
||||
// 创建一个XHR请求
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('PUT', '/api/settings/footer', true);
|
||||
xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
xhr.setRequestHeader('Authorization', `Bearer ${token}`);
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
xhr.withCredentials = true;
|
||||
|
||||
xhr.onload = function() {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
resultElement.textContent = `请求成功 (${xhr.status} ${xhr.statusText}):\n\n${xhr.responseText}`;
|
||||
resultElement.className = 'result success';
|
||||
} else {
|
||||
resultElement.textContent = `请求失败 (${xhr.status} ${xhr.statusText}):\n\n${xhr.responseText}`;
|
||||
resultElement.className = 'result error';
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = function() {
|
||||
resultElement.textContent = '网络错误,请检查控制台';
|
||||
resultElement.className = 'result error';
|
||||
};
|
||||
|
||||
const requestStart = new Date();
|
||||
xhr.send(payload);
|
||||
console.log('已发送请求:', payload);
|
||||
console.log('请求头:', {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${token.substring(0, 10)}...` // 只显示部分令牌
|
||||
});
|
||||
} catch (e) {
|
||||
resultElement.textContent = `请求错误: ${e.message}`;
|
||||
resultElement.className = 'result error';
|
||||
}
|
||||
});
|
||||
|
||||
// 检查令牌按钮
|
||||
document.getElementById('checkToken').addEventListener('click', function() {
|
||||
const token = document.getElementById('token').value.trim();
|
||||
const resultElement = document.getElementById('result');
|
||||
|
||||
resultElement.style.display = 'block';
|
||||
|
||||
if (!token) {
|
||||
resultElement.textContent = '错误: 请提供认证令牌';
|
||||
resultElement.className = 'result error';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 尝试解码令牌
|
||||
const tokenParts = token.split('.');
|
||||
if (tokenParts.length !== 3) {
|
||||
resultElement.textContent = '无效的JWT令牌格式';
|
||||
resultElement.className = 'result error';
|
||||
return;
|
||||
}
|
||||
|
||||
// 解码payload部分
|
||||
const payload = JSON.parse(atob(tokenParts[1]));
|
||||
const expDate = new Date(payload.exp * 1000);
|
||||
const now = new Date();
|
||||
const isExpired = expDate < now;
|
||||
|
||||
resultElement.textContent = `令牌信息:\n\n`;
|
||||
resultElement.textContent += `有效期至: ${expDate.toLocaleString()}\n`;
|
||||
resultElement.textContent += `当前时间: ${now.toLocaleString()}\n`;
|
||||
resultElement.textContent += `是否过期: ${isExpired ? '已过期' : '有效'}\n\n`;
|
||||
resultElement.textContent += `载荷数据:\n${JSON.stringify(payload, null, 2)}`;
|
||||
|
||||
resultElement.className = isExpired ? 'result error' : 'result success';
|
||||
} catch (e) {
|
||||
resultElement.textContent = `令牌解析错误: ${e.message}\n可能不是有效的JWT格式`;
|
||||
resultElement.className = 'result error';
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3031,4 +3031,31 @@ textarea.custom-input {
|
||||
font-size: 0.85rem;
|
||||
color: var(--gray-color);
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/* 批量操作相关样式 */
|
||||
.checkbox-column {
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.checkbox-column input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.batch-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.selected-count {
|
||||
font-size: 0.9rem;
|
||||
color: var(--gray-color);
|
||||
font-weight: 500;
|
||||
padding: 0.25rem 0.75rem;
|
||||
background: rgba(99, 102, 241, 0.1);
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
@@ -1367,6 +1367,22 @@
|
||||
<div v-if="pendingResources.length > 0" class="badge-count badge-inline">{{ pendingResources.length }}</div>
|
||||
</h4>
|
||||
</div>
|
||||
<!-- 批量操作按钮 -->
|
||||
<div v-if="pendingResources.length > 0" class="header-actions">
|
||||
<div v-if="selectedPendingResources.length > 0" class="batch-actions">
|
||||
<span class="selected-count">已选择 {{ selectedPendingResources.length }} 项</span>
|
||||
<button
|
||||
type="button"
|
||||
class="btn-custom btn-accent btn-sm"
|
||||
@click="batchRejectResources"
|
||||
:disabled="batchRejectLoading"
|
||||
>
|
||||
<div v-if="batchRejectLoading" class="spinner small-spinner"></div>
|
||||
<i v-else class="bi bi-x-circle"></i>
|
||||
<span class="btn-text">{{ batchRejectLoading ? '处理中...' : '批量拒绝' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div v-if="loadingPending" class="loading-inline">
|
||||
@@ -1381,6 +1397,14 @@
|
||||
<table class="custom-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="checkbox-column">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="isAllPendingSelected"
|
||||
@change="toggleAllPending"
|
||||
:indeterminate="isIndeterminate"
|
||||
/>
|
||||
</th>
|
||||
<th>ID</th>
|
||||
<th>标题</th>
|
||||
<th>类型</th>
|
||||
@@ -1392,6 +1416,13 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="resource in pendingResources" :key="resource.id">
|
||||
<td class="checkbox-column">
|
||||
<input
|
||||
type="checkbox"
|
||||
:value="resource.id"
|
||||
v-model="selectedPendingResources"
|
||||
/>
|
||||
</td>
|
||||
<td><span class="id-badge">#{{ resource.id }}</span></td>
|
||||
<td>{{ resource.title || resource.title_en }}</td>
|
||||
<td><span class="type-badge">{{ resource.resource_type }}</span></td>
|
||||
@@ -1789,6 +1820,8 @@ import { ElMessage } from 'element-plus'
|
||||
const router = useRouter()
|
||||
const resources = ref([])
|
||||
const pendingResources = ref([])
|
||||
const selectedPendingResources = ref([])
|
||||
const batchRejectLoading = ref(false)
|
||||
const loading = ref(true)
|
||||
const loadingPending = ref(true)
|
||||
const loadingUsers = ref(true)
|
||||
@@ -1862,6 +1895,67 @@ const formatDate = (dateString) => {
|
||||
}).format(date)
|
||||
}
|
||||
|
||||
// 批量操作相关计算属性
|
||||
const isAllPendingSelected = computed(() => {
|
||||
if (pendingResources.value.length === 0) return false
|
||||
return selectedPendingResources.value.length === pendingResources.value.length
|
||||
})
|
||||
|
||||
const isIndeterminate = computed(() => {
|
||||
return selectedPendingResources.value.length > 0 && selectedPendingResources.value.length < pendingResources.value.length
|
||||
})
|
||||
|
||||
// 全选/取消全选
|
||||
const toggleAllPending = (event) => {
|
||||
if (event.target.checked) {
|
||||
selectedPendingResources.value = pendingResources.value.map(r => r.id)
|
||||
} else {
|
||||
selectedPendingResources.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 批量拒绝资源
|
||||
const batchRejectResources = async () => {
|
||||
if (selectedPendingResources.value.length === 0) {
|
||||
ElMessage.warning('请选择要拒绝的资源')
|
||||
return
|
||||
}
|
||||
|
||||
const confirmMsg = `确定要拒绝选中的 ${selectedPendingResources.value.length} 个资源吗?`
|
||||
if (!confirm(confirmMsg)) {
|
||||
return
|
||||
}
|
||||
|
||||
batchRejectLoading.value = true
|
||||
|
||||
try {
|
||||
// 使用新的批量拒绝API
|
||||
const response = await axios.post('/api/resources/batch-reject', {
|
||||
resource_ids: selectedPendingResources.value,
|
||||
notes: '批量拒绝'
|
||||
})
|
||||
|
||||
const data = response.data
|
||||
|
||||
if (data.success_count > 0) {
|
||||
ElMessage.success(`成功拒绝 ${data.success_count} 个资源`)
|
||||
// 重新加载待审批资源列表
|
||||
await fetchPendingResources()
|
||||
// 清空选中项
|
||||
selectedPendingResources.value = []
|
||||
}
|
||||
|
||||
if (data.failed_ids && data.failed_ids.length > 0) {
|
||||
ElMessage.error(`${data.failed_ids.length} 个资源拒绝失败`)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('批量拒绝失败:', err)
|
||||
ElMessage.error('批量拒绝失败,请稍后重试')
|
||||
} finally {
|
||||
batchRejectLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取所有已审批资源
|
||||
const fetchResources = async () => {
|
||||
loading.value = true
|
||||
|
||||
@@ -1292,4 +1292,121 @@ func GetResourceApprovalRecords(c *gin.Context) {
|
||||
"resource": resource,
|
||||
"records": records,
|
||||
})
|
||||
}
|
||||
|
||||
// BatchRejectResources 批量拒绝资源 - 仅管理员可访问
|
||||
func BatchRejectResources(c *gin.Context) {
|
||||
// 解析请求体中的资源ID列表
|
||||
var request struct {
|
||||
ResourceIDs []int `json:"resource_ids" binding:"required"`
|
||||
Notes string `json:"notes"`
|
||||
}
|
||||
|
||||
if errBind := c.ShouldBindJSON(&request); errBind != nil {
|
||||
log.Printf("解析请求体失败: %v", errBind)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求参数"})
|
||||
return
|
||||
}
|
||||
|
||||
if len(request.ResourceIDs) == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "资源ID列表为空"})
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("批量拒绝资源,ID数量: %d, IDs: %v", len(request.ResourceIDs), request.ResourceIDs)
|
||||
|
||||
// 批量处理资源
|
||||
var successCount int
|
||||
var failedIDs []int
|
||||
|
||||
for _, resourceID := range request.ResourceIDs {
|
||||
// 检查资源是否存在且为待审批状态
|
||||
var resource models.Resource
|
||||
errGet := models.DB.Get(&resource, `SELECT * FROM resources WHERE id = ?`, resourceID)
|
||||
if errGet != nil {
|
||||
log.Printf("资源 %d 未找到: %v", resourceID, errGet)
|
||||
failedIDs = append(failedIDs, resourceID)
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查资源状态
|
||||
if resource.Status != models.ResourceStatusPending {
|
||||
log.Printf("资源 %d 不是待审批状态: %s", resourceID, resource.Status)
|
||||
failedIDs = append(failedIDs, resourceID)
|
||||
continue
|
||||
}
|
||||
|
||||
// 更新资源状态为拒绝
|
||||
resource.Status = models.ResourceStatusRejected
|
||||
resource.UpdatedAt = time.Now()
|
||||
|
||||
// 创建审批记录
|
||||
approvalRecord := models.ApprovalRecord{
|
||||
ResourceID: resourceID,
|
||||
Status: models.ResourceStatusRejected,
|
||||
FieldApprovals: models.JsonMap{},
|
||||
FieldRejections: models.JsonMap{},
|
||||
ApprovedImages: []string{},
|
||||
RejectedImages: []string{},
|
||||
Notes: request.Notes,
|
||||
ApprovedLinks: models.JsonMap{},
|
||||
RejectedLinks: models.JsonMap{},
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
// 开始事务
|
||||
tx := models.DB.MustBegin()
|
||||
|
||||
// 更新资源状态
|
||||
_, errUpdate := tx.Exec(
|
||||
`UPDATE resources SET status = ?, updated_at = ? WHERE id = ?`,
|
||||
resource.Status, resource.UpdatedAt, resourceID,
|
||||
)
|
||||
|
||||
if errUpdate != nil {
|
||||
log.Printf("更新资源 %d 状态失败: %v", resourceID, errUpdate)
|
||||
tx.Rollback()
|
||||
failedIDs = append(failedIDs, resourceID)
|
||||
continue
|
||||
}
|
||||
|
||||
// 插入审批记录
|
||||
_, errInsert := tx.Exec(
|
||||
`INSERT INTO approval_records (
|
||||
resource_id, status, field_approvals, field_rejections,
|
||||
approved_images, rejected_images, poster_image, notes,
|
||||
approved_links, rejected_links, created_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
approvalRecord.ResourceID, approvalRecord.Status,
|
||||
approvalRecord.FieldApprovals, approvalRecord.FieldRejections,
|
||||
models.JsonList(approvalRecord.ApprovedImages), models.JsonList(approvalRecord.RejectedImages),
|
||||
"", approvalRecord.Notes,
|
||||
approvalRecord.ApprovedLinks, approvalRecord.RejectedLinks,
|
||||
approvalRecord.CreatedAt,
|
||||
)
|
||||
|
||||
if errInsert != nil {
|
||||
log.Printf("创建资源 %d 的审批记录失败: %v", resourceID, errInsert)
|
||||
tx.Rollback()
|
||||
failedIDs = append(failedIDs, resourceID)
|
||||
continue
|
||||
}
|
||||
|
||||
// 提交事务
|
||||
if errCommit := tx.Commit(); errCommit != nil {
|
||||
log.Printf("提交资源 %d 的事务失败: %v", resourceID, errCommit)
|
||||
failedIDs = append(failedIDs, resourceID)
|
||||
continue
|
||||
}
|
||||
|
||||
successCount++
|
||||
log.Printf("成功拒绝资源 %d", resourceID)
|
||||
}
|
||||
|
||||
log.Printf("批量拒绝完成,成功: %d, 失败: %d", successCount, len(failedIDs))
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success_count": successCount,
|
||||
"failed_ids": failedIDs,
|
||||
"total": len(request.ResourceIDs),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -136,6 +136,7 @@ func SetupRoutes(router *gin.Engine) {
|
||||
adminResources.GET("/:id/supplement", GetResourceSupplement)
|
||||
adminResources.PUT("/:id", UpdateResource)
|
||||
adminResources.PUT("/:id/approve", ApproveResource)
|
||||
adminResources.POST("/batch-reject", BatchRejectResources)
|
||||
adminResources.DELETE("/:id", DeleteResource)
|
||||
adminResources.DELETE("/:id/record", DeleteApprovalRecord)
|
||||
adminResources.DELETE("/batch-delete-records", DeleteApprovalRecords)
|
||||
|
||||
Reference in New Issue
Block a user