key table

This commit is contained in:
hptangxi
2025-07-06 21:21:00 +08:00
parent bf7fb2221b
commit a992c28593
5 changed files with 125 additions and 93 deletions

View File

@@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import GlobalProviders from "@/components/GlobalProviders.vue"; import GlobalProviders from "@/components/GlobalProviders.vue";
import GlobalTaskProgressBar from "@/components/GlobalTaskProgressBar.vue";
import Layout from "@/components/Layout.vue"; import Layout from "@/components/Layout.vue";
import { useAuthKey } from "@/services/auth"; import { useAuthKey } from "@/services/auth";
import { computed } from "vue"; import { computed } from "vue";
@@ -15,7 +16,7 @@ const isLoggedIn = computed(() => !!authKey.value);
<router-view v-else key="auth" /> <router-view v-else key="auth" />
<!-- 全局任务进度条 --> <!-- 全局任务进度条 -->
<!-- <global-task-progress-bar /> --> <global-task-progress-bar />
</div> </div>
</global-providers> </global-providers>
</template> </template>

View File

@@ -118,17 +118,31 @@ export const keysApi = {
// 清空所有无效密钥 // 清空所有无效密钥
clearAllInvalidKeys(group_id: number): Promise<void> { clearAllInvalidKeys(group_id: number): Promise<void> {
return http.post("/keys/clear-all-invalid", { group_id }); return http.post(
"/keys/clear-all-invalid",
{ group_id },
{
hideMessage: true,
}
);
}, },
// 导出密钥 // 导出密钥
async exportKeys( exportKeys(groupId: number, status: "all" | "active" | "inactive" = "all") {
groupId: number, let url = `${http.defaults.baseURL}/groups/${groupId}/keys/export`;
filter: "all" | "valid" | "invalid" = "all" if (status !== "all") {
): Promise<{ keys: string[] }> { url += `?status=${status}`;
const params: any = { filter }; }
const res = await http.get(`/groups/${groupId}/keys/export`, { params });
return res.data; // 创建隐藏的 a 标签实现下载
const link = document.createElement("a");
link.href = url;
link.download = `group-${groupId}-keys-${status}.txt`;
link.style.display = "none";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}, },
// 验证分组密钥 // 验证分组密钥

View File

@@ -1,12 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { keysApi } from "@/api/keys"; import { keysApi } from "@/api/keys";
import type { TaskInfo } from "@/types/models"; import type { TaskInfo } from "@/types/models";
import { NButton, NCard, NProgress, NText } from "naive-ui"; import { NButton, NCard, NProgress, NText, useMessage } from "naive-ui";
import { onBeforeUnmount, onMounted, ref } from "vue"; import { onBeforeUnmount, onMounted, ref } from "vue";
const taskInfo = ref<TaskInfo>({ is_running: false }); const taskInfo = ref<TaskInfo>({ is_running: false });
const visible = ref(false); const visible = ref(false);
let pollTimer: number | null = null; let pollTimer: number | null = null;
let isPolling = false; // 添加标志位
const message = useMessage();
onMounted(() => { onMounted(() => {
startPolling(); startPolling();
@@ -18,18 +20,49 @@ onBeforeUnmount(() => {
function startPolling() { function startPolling() {
stopPolling(); stopPolling();
pollTimer = setInterval(async () => { isPolling = true;
pollOnce();
}
async function pollOnce() {
if (!isPolling) {
return;
}
try { try {
const task = await keysApi.getTaskStatus(); const task = await keysApi.getTaskStatus();
taskInfo.value = task; taskInfo.value = task;
visible.value = task.is_running; visible.value = task.is_running;
if (!task.is_running) {
stopPolling();
if (task.result) {
const lastTask = localStorage.getItem("last_closed_task");
if (lastTask !== task.finished_at) {
const { total_keys, valid_keys, invalid_keys } = task.result;
const msg = `任务已完成,处理了 ${total_keys} 个密钥,其中 ${valid_keys} 个有效密钥,${invalid_keys} 个无效密钥。`;
message.info(msg, {
closable: true,
duration: 0,
onClose: () => {
localStorage.setItem("last_closed_task", task.finished_at || "");
},
});
}
}
return;
}
} catch (_error) { } catch (_error) {
// 错误已记录 // 错误已记录
} }
}, 1000);
// 如果仍在轮询状态1秒后发起下一次请求
if (isPolling) {
pollTimer = setTimeout(pollOnce, 1000);
}
} }
function stopPolling() { function stopPolling() {
isPolling = false;
if (pollTimer) { if (pollTimer) {
clearInterval(pollTimer); clearInterval(pollTimer);
pollTimer = null; pollTimer = null;
@@ -61,7 +94,7 @@ function handleClose() {
<span class="progress-icon"></span> <span class="progress-icon"></span>
<div class="progress-details"> <div class="progress-details">
<n-text strong class="progress-title"> <n-text strong class="progress-title">
{{ taskInfo.task_name || "正在处理任务" }} 正在处理分组 {{ taskInfo.group_name }} 的任务
</n-text> </n-text>
<n-text depth="3" class="progress-subtitle"> <n-text depth="3" class="progress-subtitle">
{{ getProgressText() }} ({{ getProgressPercentage() }}%) {{ getProgressText() }} ({{ getProgressPercentage() }}%)
@@ -156,6 +189,8 @@ function handleClose() {
.progress-details { .progress-details {
flex: 1; flex: 1;
display: flex;
flex-direction: column;
} }
.progress-title { .progress-title {

View File

@@ -279,125 +279,104 @@ function getStatusClass(status: KeyStatus): string {
} }
async function copyAllKeys() { async function copyAllKeys() {
if (!props.selectedGroup) { if (!props.selectedGroup?.id) {
return; return;
} }
try { keysApi.exportKeys(props.selectedGroup.id, "all");
const result = await keysApi.exportKeys(props.selectedGroup.id, "all");
const keysText = result.keys.join("\n");
navigator.clipboard
.writeText(keysText)
.then(() => {
window.$message.success(`已复制${result.keys.length}个密钥到剪贴板`);
})
.catch(() => {
window.$message.error("复制失败");
});
} catch (_error) {
// 错误已记录
window.$message.error("导出失败");
}
} }
async function copyValidKeys() { async function copyValidKeys() {
if (!props.selectedGroup) { if (!props.selectedGroup?.id) {
return; return;
} }
try { keysApi.exportKeys(props.selectedGroup.id, "active");
const result = await keysApi.exportKeys(props.selectedGroup.id, "valid");
const keysText = result.keys.join("\n");
navigator.clipboard
.writeText(keysText)
.then(() => {
window.$message.success(`已复制${result.keys.length}个有效密钥到剪贴板`);
})
.catch(() => {
window.$message.error("复制失败");
});
} catch (_error) {
// 错误已记录
window.$message.error("导出失败");
}
} }
async function copyInvalidKeys() { async function copyInvalidKeys() {
if (!props.selectedGroup) { if (!props.selectedGroup?.id) {
return; return;
} }
try { keysApi.exportKeys(props.selectedGroup.id, "inactive");
const result = await keysApi.exportKeys(props.selectedGroup.id, "invalid");
const keysText = result.keys.join("\n");
navigator.clipboard
.writeText(keysText)
.then(() => {
window.$message.success(`已复制${result.keys.length}个无效密钥到剪贴板`);
})
.catch(() => {
window.$message.error("复制失败");
});
} catch (_error) {
// 错误已记录
window.$message.error("导出失败");
}
} }
async function restoreAllInvalid() { async function restoreAllInvalid() {
if (!props.selectedGroup) { if (!props.selectedGroup?.id || restoreMsg) {
return; return;
} }
dialog.warning({ dialog.warning({
// title: "恢复密钥", title: "恢复密钥",
content: "确定要恢复所有无效密钥吗?", content: "确定要恢复所有无效密钥吗?",
positiveText: "确定", positiveText: "确定",
negativeText: "取消", negativeText: "取消",
onPositiveClick: async () => { onPositiveClick: async () => {
restoreMsg = window.$message.info("正在恢复密钥...", {
duration: 0,
});
try { try {
window.$message.success("所有无效密钥已恢复"); await keysApi.restoreAllInvalidKeys(props.selectedGroup.id);
await loadKeys(); await loadKeys();
} catch (_error) { } catch (_error) {
// 错误已记录 console.error("恢复失败");
window.$message.error("恢复失败"); } finally {
restoreMsg?.destroy();
restoreMsg = null;
} }
}, },
}); });
} }
async function validateAllKeys() { async function validateAllKeys() {
if (!props.selectedGroup) { if (!props.selectedGroup?.id || testingMsg) {
return; return;
} }
testingMsg = window.$message.info("正在验证密钥...", {
duration: 0,
});
try { try {
const result = await keysApi.validateGroupKeys(props.selectedGroup.id); await keysApi.validateGroupKeys(props.selectedGroup.id);
window.$message.success(`验证完成: 有效${result.valid_count}个,无效${result.invalid_count}`); localStorage.removeItem("last_closed_task");
} catch (_error) { } catch (_error) {
// 错误已记录 console.error("测试失败");
window.$message.error("验证失败"); } finally {
testingMsg?.destroy();
testingMsg = null;
} }
} }
async function clearAllInvalid() { async function clearAllInvalid() {
if (!props.selectedGroup) { if (!props.selectedGroup?.id || deleteMsg) {
return; return;
} }
// eslint-disable-next-line no-alert dialog.warning({
const confirmed = window.confirm("确定要清除所有无效密钥吗?此操作不可恢复!"); title: "清除密钥",
if (!confirmed) { content: "确定要清除所有无效密钥吗?此操作不可恢复!",
return; positiveText: "确定",
} negativeText: "取消",
onPositiveClick: async () => {
deleteMsg = window.$message.info("正在清除密钥...", {
duration: 0,
});
try { try {
window.$message.success("所有无效密钥已清除"); const { data } = await keysApi.clearAllInvalidKeys(props.selectedGroup.id);
window.$message.success(data?.message || "清除成功");
await loadKeys(); await loadKeys();
} catch (_error) { } catch (_error) {
// 错误已记录 console.error("删除失败");
window.$message.error("清除失败"); } finally {
deleteMsg?.destroy();
deleteMsg = null;
} }
},
});
} }
function changePage(page: number) { function changePage(page: number) {

View File

@@ -57,13 +57,16 @@ export interface GroupStats {
export interface TaskInfo { export interface TaskInfo {
is_running: boolean; is_running: boolean;
task_name?: string;
group_id?: number;
group_name?: string; group_name?: string;
processed?: number; processed?: number;
total?: number; total?: number;
started_at?: string; started_at?: string;
message?: string; finished_at?: string;
result?: {
invalid_keys: number;
total_keys: number;
valid_keys: number;
};
} }
export interface RequestLog { export interface RequestLog {