delete group
This commit is contained in:
@@ -245,11 +245,9 @@ async function handleSubmit() {
|
|||||||
if (props.group?.id) {
|
if (props.group?.id) {
|
||||||
// 编辑模式
|
// 编辑模式
|
||||||
res = await keysApi.updateGroup(props.group.id, submitData);
|
res = await keysApi.updateGroup(props.group.id, submitData);
|
||||||
message.success("分组更新成功");
|
|
||||||
} else {
|
} else {
|
||||||
// 新建模式
|
// 新建模式
|
||||||
res = await keysApi.createGroup(submitData);
|
res = await keysApi.createGroup(submitData);
|
||||||
message.success("分组创建成功");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit("success", res);
|
emit("success", res);
|
||||||
@@ -295,11 +293,7 @@ async function handleSubmit() {
|
|||||||
<h4 class="section-title">基础信息</h4>
|
<h4 class="section-title">基础信息</h4>
|
||||||
|
|
||||||
<n-form-item label="分组名称" path="name">
|
<n-form-item label="分组名称" path="name">
|
||||||
<n-input
|
<n-input v-model:value="formData.name" placeholder="请输入分组名称,如:gemini" />
|
||||||
v-model:value="formData.name"
|
|
||||||
placeholder="请输入分组名称,如:gemini"
|
|
||||||
:disabled="!!group"
|
|
||||||
/>
|
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
|
||||||
<n-form-item label="显示名称" path="display_name">
|
<n-form-item label="显示名称" path="display_name">
|
||||||
|
@@ -7,12 +7,13 @@ import {
|
|||||||
NCard,
|
NCard,
|
||||||
NCollapse,
|
NCollapse,
|
||||||
NCollapseItem,
|
NCollapseItem,
|
||||||
NFlex,
|
|
||||||
NForm,
|
NForm,
|
||||||
NFormItem,
|
NFormItem,
|
||||||
|
NGrid,
|
||||||
|
NGridItem,
|
||||||
NSpin,
|
NSpin,
|
||||||
NTag,
|
NTag,
|
||||||
useMessage,
|
useDialog,
|
||||||
} from "naive-ui";
|
} from "naive-ui";
|
||||||
import { onMounted, ref, watch } from "vue";
|
import { onMounted, ref, watch } from "vue";
|
||||||
import GroupFormModal from "./GroupFormModal.vue";
|
import GroupFormModal from "./GroupFormModal.vue";
|
||||||
@@ -23,6 +24,7 @@ interface Props {
|
|||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: "refresh", value: Group): void;
|
(e: "refresh", value: Group): void;
|
||||||
|
(e: "delete", value: Group): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
@@ -31,7 +33,7 @@ const emit = defineEmits<Emits>();
|
|||||||
|
|
||||||
const stats = ref<GroupStats | null>(null);
|
const stats = ref<GroupStats | null>(null);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const message = useMessage();
|
const dialog = useDialog();
|
||||||
const showEditModal = ref(false);
|
const showEditModal = ref(false);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -53,7 +55,9 @@ async function loadStats() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
stats.value = await keysApi.getGroupStats(props.group.id);
|
if (props.group?.id) {
|
||||||
|
stats.value = await keysApi.getGroupStats(props.group.id);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
@@ -71,7 +75,26 @@ function handleGroupEdited(newGroup: Group) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleDelete() {
|
function handleDelete() {
|
||||||
message.info("删除分组功能开发中...");
|
if (!props.group) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.warning({
|
||||||
|
title: "删除分组",
|
||||||
|
content: `确定要删除分组 "${getGroupDisplayName(props.group)}" 吗?此操作不可恢复。`,
|
||||||
|
positiveText: "确定",
|
||||||
|
negativeText: "取消",
|
||||||
|
onPositiveClick: async () => {
|
||||||
|
try {
|
||||||
|
if (props.group?.id) {
|
||||||
|
await keysApi.deleteGroup(props.group.id);
|
||||||
|
emit("delete", props.group);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("删除分组失败:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatNumber(num: number): string {
|
function formatNumber(num: number): string {
|
||||||
@@ -108,12 +131,8 @@ function copyUrl(url: string) {
|
|||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<h3 class="group-title">
|
<h3 class="group-title">
|
||||||
{{ group ? getGroupDisplayName(group) : "请选择分组" }}
|
{{ group ? getGroupDisplayName(group) : "请选择分组" }}
|
||||||
<code
|
<code v-if="group" class="group-url" @click="copyUrl(group?.endpoint || '')">
|
||||||
v-if="group"
|
{{ group.endpoint }}
|
||||||
class="group-url"
|
|
||||||
@click="copyUrl(`https://gpt-load.com/${group?.name}`)"
|
|
||||||
>
|
|
||||||
https://gpt-load.com/{{ group?.name }}
|
|
||||||
</code>
|
</code>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
@@ -134,6 +153,7 @@ function copyUrl(url: string) {
|
|||||||
@click="handleDelete"
|
@click="handleDelete"
|
||||||
title="删除分组"
|
title="删除分组"
|
||||||
type="error"
|
type="error"
|
||||||
|
:disabled="!group"
|
||||||
>
|
>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||||
@@ -150,27 +170,40 @@ function copyUrl(url: string) {
|
|||||||
<!-- 统计摘要区 -->
|
<!-- 统计摘要区 -->
|
||||||
<div class="stats-summary">
|
<div class="stats-summary">
|
||||||
<n-spin :show="loading" size="small">
|
<n-spin :show="loading" size="small">
|
||||||
<n-flex class="status-cards-container">
|
<n-grid :cols="5" :x-gap="12" :y-gap="12" responsive="screen">
|
||||||
<n-card :title="`${stats?.active_keys || 0} / ${stats?.total_keys || 0}`" size="large">
|
<n-grid-item span="1">
|
||||||
<template #header-extra><span class="status-title">密钥数量</span></template>
|
<n-card
|
||||||
</n-card>
|
:title="`${stats?.active_keys || 0} / ${stats?.total_keys || 0}`"
|
||||||
<n-card
|
size="large"
|
||||||
class="status-card-failure"
|
>
|
||||||
:title="formatPercentage(stats?.failure_rate_24h || 0)"
|
<template #header-extra><span class="status-title">密钥数量</span></template>
|
||||||
size="large"
|
</n-card>
|
||||||
>
|
</n-grid-item>
|
||||||
<template #header-extra><span class="status-title">失败率</span></template>
|
<n-grid-item span="1">
|
||||||
</n-card>
|
<n-card
|
||||||
<n-card :title="formatNumber(stats?.requests_1h || 0)" size="large">
|
class="status-card-failure"
|
||||||
<template #header-extra><span class="status-title">近1小时</span></template>
|
:title="formatPercentage(stats?.failure_rate_24h || 0)"
|
||||||
</n-card>
|
size="large"
|
||||||
<n-card :title="formatNumber(stats?.requests_24h || 0)" size="large">
|
>
|
||||||
<template #header-extra><span class="status-title">近24小时</span></template>
|
<template #header-extra><span class="status-title">失败率</span></template>
|
||||||
</n-card>
|
</n-card>
|
||||||
<n-card :title="formatNumber(stats?.requests_7d || 0)" size="large">
|
</n-grid-item>
|
||||||
<template #header-extra><span class="status-title">近7天</span></template>
|
<n-grid-item span="1">
|
||||||
</n-card>
|
<n-card :title="formatNumber(stats?.requests_1h || 0)" size="large">
|
||||||
</n-flex>
|
<template #header-extra><span class="status-title">近1小时</span></template>
|
||||||
|
</n-card>
|
||||||
|
</n-grid-item>
|
||||||
|
<n-grid-item span="1">
|
||||||
|
<n-card :title="formatNumber(stats?.requests_24h || 0)" size="large">
|
||||||
|
<template #header-extra><span class="status-title">近24小时</span></template>
|
||||||
|
</n-card>
|
||||||
|
</n-grid-item>
|
||||||
|
<n-grid-item span="1">
|
||||||
|
<n-card :title="formatNumber(stats?.requests_7d || 0)" size="large">
|
||||||
|
<template #header-extra><span class="status-title">近7天</span></template>
|
||||||
|
</n-card>
|
||||||
|
</n-grid-item>
|
||||||
|
</n-grid>
|
||||||
</n-spin>
|
</n-spin>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@@ -30,6 +30,7 @@ export interface Group {
|
|||||||
upstreams: UpstreamInfo[];
|
upstreams: UpstreamInfo[];
|
||||||
config: Record<string, unknown>;
|
config: Record<string, unknown>;
|
||||||
api_keys?: APIKey[];
|
api_keys?: APIKey[];
|
||||||
|
endpoint?: string;
|
||||||
param_overrides: any;
|
param_overrides: any;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
updated_at?: string;
|
updated_at?: string;
|
||||||
|
@@ -38,6 +38,16 @@ async function handleGroupRefresh() {
|
|||||||
selectedGroup.value = groups.value.find(g => g.id === selectedGroup.value?.id) || null;
|
selectedGroup.value = groups.value.find(g => g.id === selectedGroup.value?.id) || null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleGroupDelete(deletedGroup: Group) {
|
||||||
|
// 从分组列表中移除已删除的分组
|
||||||
|
groups.value = groups.value.filter(g => g.id !== deletedGroup.id);
|
||||||
|
|
||||||
|
// 如果删除的是当前选中的分组,则切换到第一个分组
|
||||||
|
if (selectedGroup.value?.id === deletedGroup.id) {
|
||||||
|
selectedGroup.value = groups.value.length > 0 ? groups.value[0] : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -56,7 +66,11 @@ async function handleGroupRefresh() {
|
|||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<!-- 分组信息卡片,更紧凑 -->
|
<!-- 分组信息卡片,更紧凑 -->
|
||||||
<div class="group-info">
|
<div class="group-info">
|
||||||
<group-info-card :group="selectedGroup" @refresh="handleGroupRefresh" />
|
<group-info-card
|
||||||
|
:group="selectedGroup"
|
||||||
|
@refresh="handleGroupRefresh"
|
||||||
|
@delete="handleGroupDelete"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 密钥表格区域,占主要空间 -->
|
<!-- 密钥表格区域,占主要空间 -->
|
||||||
|
Reference in New Issue
Block a user