mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-31 08:25:35 +00:00
Add composite group routing
This commit is contained in:
@@ -94,11 +94,11 @@ const filteredGroups = computed(() => {
|
||||
// antigravity 账户启用混合调度后,可选择 anthropic/gemini 分组
|
||||
if (props.platform === 'antigravity' && props.mixedScheduling) {
|
||||
result = result.filter(
|
||||
(g) => g.platform === 'antigravity' || g.platform === 'anthropic' || g.platform === 'gemini'
|
||||
(g) => g.platform === 'antigravity' || g.platform === 'anthropic' || g.platform === 'gemini' || g.platform === 'composite'
|
||||
)
|
||||
} else {
|
||||
// 默认:只能选择同 platform 的分组
|
||||
result = result.filter((g) => g.platform === props.platform)
|
||||
// 默认:只能选择同 platform 的分组;composite 分组可接收任意具体平台账号
|
||||
result = result.filter((g) => g.platform === props.platform || g.platform === 'composite')
|
||||
}
|
||||
}
|
||||
if (isSearchable.value && searchText.value) {
|
||||
|
||||
@@ -25,6 +25,13 @@
|
||||
d="M9.27 15.29l7.978-5.897c.391-.29.95-.177 1.137.272.98 2.369.542 5.215-1.41 7.169-1.951 1.954-4.667 2.382-7.149 1.406l-2.711 1.257c3.889 2.661 8.611 2.003 11.562-.953 2.341-2.344 3.066-5.539 2.388-8.42l.006.007c-.983-4.232.242-5.924 2.75-9.383.06-.082.12-.164.179-.248l-3.301 3.305v-.01L9.267 15.292M7.623 16.723c-2.792-2.67-2.31-6.801.071-9.184 1.761-1.763 4.647-2.483 7.166-1.425l2.705-1.25a7.808 7.808 0 00-1.829-1A8.975 8.975 0 005.984 5.83c-2.533 2.536-3.33 6.436-1.962 9.764 1.022 2.487-.653 4.246-2.34 6.022-.599.63-1.199 1.259-1.682 1.925l7.62-6.815"
|
||||
/>
|
||||
</svg>
|
||||
<!-- Composite group icon -->
|
||||
<svg v-else-if="platform === 'composite'" :class="sizeClass" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="6" cy="12" r="3" />
|
||||
<circle cx="18" cy="6" r="3" />
|
||||
<circle cx="18" cy="18" r="3" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.7 10.7 15.3 7.3M8.7 13.3l6.6 3.4" />
|
||||
</svg>
|
||||
<!-- Fallback: generic platform icon -->
|
||||
<svg v-else :class="sizeClass" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
|
||||
@@ -944,6 +944,7 @@ export default {
|
||||
gemini: 'Gemini',
|
||||
antigravity: 'Antigravity',
|
||||
grok: 'Grok',
|
||||
composite: 'Composite',
|
||||
},
|
||||
deleteConfirm:
|
||||
"Are you sure you want to delete '{name}'? All associated API keys will no longer belong to any group.",
|
||||
|
||||
@@ -877,6 +877,7 @@ export default {
|
||||
gemini: 'Gemini',
|
||||
antigravity: 'Antigravity',
|
||||
grok: 'Grok',
|
||||
composite: 'Composite',
|
||||
},
|
||||
saving: '保存中...',
|
||||
noGroups: '暂无分组',
|
||||
|
||||
@@ -492,7 +492,7 @@ export interface PaginationConfig {
|
||||
|
||||
// ==================== API Key & Group Types ====================
|
||||
|
||||
export type GroupPlatform = 'anthropic' | 'openai' | 'gemini' | 'antigravity' | 'grok'
|
||||
export type GroupPlatform = 'anthropic' | 'openai' | 'gemini' | 'antigravity' | 'grok' | 'composite'
|
||||
|
||||
export type SubscriptionType = 'standard' | 'subscription'
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* instead of defining their own color mappings.
|
||||
*/
|
||||
|
||||
export type Platform = 'anthropic' | 'openai' | 'antigravity' | 'gemini' | 'grok'
|
||||
export type Platform = 'anthropic' | 'openai' | 'antigravity' | 'gemini' | 'grok' | 'composite'
|
||||
|
||||
// ── Badge (bg + text + border, for inline badges with border) ───────
|
||||
const BADGE: Record<Platform, string> = {
|
||||
@@ -14,6 +14,7 @@ const BADGE: Record<Platform, string> = {
|
||||
antigravity: 'bg-purple-500/10 text-purple-600 border-purple-500/30 dark:text-purple-400',
|
||||
gemini: 'bg-blue-500/10 text-blue-600 border-blue-500/30 dark:text-blue-400',
|
||||
grok: 'bg-zinc-800/10 text-zinc-800 border-zinc-800/30 dark:bg-zinc-500/10 dark:text-zinc-200 dark:border-zinc-500/30',
|
||||
composite: 'bg-cyan-500/10 text-cyan-700 border-cyan-500/30 dark:text-cyan-300',
|
||||
}
|
||||
const BADGE_DEFAULT = 'bg-slate-500/10 text-slate-600 border-slate-500/30 dark:text-slate-400'
|
||||
|
||||
@@ -24,6 +25,7 @@ const BADGE_LIGHT: Record<Platform, string> = {
|
||||
antigravity: 'bg-purple-500/10 text-purple-600 dark:bg-purple-500/10 dark:text-purple-300',
|
||||
gemini: 'bg-blue-500/10 text-blue-600 dark:bg-blue-500/10 dark:text-blue-300',
|
||||
grok: 'bg-zinc-800/10 text-zinc-800 dark:bg-zinc-500/10 dark:text-zinc-200',
|
||||
composite: 'bg-cyan-500/10 text-cyan-700 dark:bg-cyan-500/10 dark:text-cyan-300',
|
||||
}
|
||||
|
||||
// ── Border ──────────────────────────────────────────────────────────
|
||||
@@ -33,6 +35,7 @@ const BORDER: Record<Platform, string> = {
|
||||
antigravity: 'border-purple-500/20 dark:border-purple-500/20',
|
||||
gemini: 'border-blue-500/20 dark:border-blue-500/20',
|
||||
grok: 'border-zinc-800/20 dark:border-zinc-500/20',
|
||||
composite: 'border-cyan-500/20 dark:border-cyan-500/20',
|
||||
}
|
||||
const BORDER_DEFAULT = 'border-gray-200 dark:border-dark-700'
|
||||
|
||||
@@ -43,6 +46,7 @@ const ACCENT_BAR: Record<Platform, string> = {
|
||||
antigravity: 'bg-gradient-to-r from-purple-400 to-purple-500',
|
||||
gemini: 'bg-gradient-to-r from-blue-400 to-blue-500',
|
||||
grok: 'bg-gradient-to-r from-zinc-700 to-zinc-900',
|
||||
composite: 'bg-gradient-to-r from-slate-500 to-cyan-500',
|
||||
}
|
||||
const ACCENT_BAR_DEFAULT = 'bg-gradient-to-r from-primary-400 to-primary-500'
|
||||
|
||||
@@ -53,6 +57,7 @@ const TEXT: Record<Platform, string> = {
|
||||
antigravity: 'text-purple-600 dark:text-purple-400',
|
||||
gemini: 'text-blue-600 dark:text-blue-400',
|
||||
grok: 'text-zinc-800 dark:text-zinc-200',
|
||||
composite: 'text-cyan-700 dark:text-cyan-300',
|
||||
}
|
||||
const TEXT_DEFAULT = 'text-primary-600 dark:text-primary-400'
|
||||
|
||||
@@ -63,6 +68,7 @@ const ICON: Record<Platform, string> = {
|
||||
antigravity: 'text-purple-500 dark:text-purple-400',
|
||||
gemini: 'text-blue-500 dark:text-blue-400',
|
||||
grok: 'text-zinc-800 dark:text-zinc-200',
|
||||
composite: 'text-cyan-600 dark:text-cyan-300',
|
||||
}
|
||||
const ICON_DEFAULT = 'text-primary-500 dark:text-primary-400'
|
||||
|
||||
@@ -73,6 +79,7 @@ const BUTTON: Record<Platform, string> = {
|
||||
antigravity: 'bg-purple-500 text-white hover:bg-purple-600 active:bg-purple-700 dark:bg-purple-500/80 dark:hover:bg-purple-500',
|
||||
gemini: 'bg-blue-500 text-white hover:bg-blue-600 active:bg-blue-700 dark:bg-blue-500/80 dark:hover:bg-blue-500',
|
||||
grok: 'bg-zinc-800 text-white hover:bg-zinc-900 active:bg-black dark:bg-zinc-700 dark:hover:bg-zinc-600',
|
||||
composite: 'bg-cyan-700 text-white hover:bg-cyan-800 active:bg-cyan-900 dark:bg-cyan-600 dark:hover:bg-cyan-500',
|
||||
}
|
||||
const BUTTON_DEFAULT = 'bg-primary-500 text-white hover:bg-primary-600 dark:bg-primary-600 dark:hover:bg-primary-500'
|
||||
|
||||
@@ -83,6 +90,7 @@ const DISCOUNT: Record<Platform, string> = {
|
||||
antigravity: 'bg-purple-100 text-purple-700 dark:bg-purple-900/40 dark:text-purple-300',
|
||||
gemini: 'bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300',
|
||||
grok: 'bg-zinc-100 text-zinc-800 dark:bg-zinc-800 dark:text-zinc-200',
|
||||
composite: 'bg-cyan-100 text-cyan-800 dark:bg-cyan-900/40 dark:text-cyan-300',
|
||||
}
|
||||
const DISCOUNT_DEFAULT = 'bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300'
|
||||
|
||||
@@ -93,6 +101,7 @@ const GRADIENT: Record<Platform, string> = {
|
||||
antigravity: 'from-purple-500 to-purple-600',
|
||||
gemini: 'from-blue-500 to-blue-600',
|
||||
grok: 'from-zinc-700 to-zinc-900',
|
||||
composite: 'from-slate-600 to-cyan-600',
|
||||
}
|
||||
const GRADIENT_DEFAULT = 'from-primary-500 to-primary-600'
|
||||
|
||||
@@ -103,6 +112,7 @@ const GRADIENT_TEXT: Record<Platform, string> = {
|
||||
antigravity: 'text-purple-100',
|
||||
gemini: 'text-blue-100',
|
||||
grok: 'text-zinc-100',
|
||||
composite: 'text-cyan-100',
|
||||
}
|
||||
const GRADIENT_TEXT_DEFAULT = 'text-primary-100'
|
||||
|
||||
@@ -112,13 +122,14 @@ const GRADIENT_SUBTEXT: Record<Platform, string> = {
|
||||
antigravity: 'text-purple-200',
|
||||
gemini: 'text-blue-200',
|
||||
grok: 'text-zinc-300',
|
||||
composite: 'text-cyan-200',
|
||||
}
|
||||
const GRADIENT_SUBTEXT_DEFAULT = 'text-primary-200'
|
||||
|
||||
// ── Public API ──────────────────────────────────────────────────────
|
||||
|
||||
function isPlatform(p: string): p is Platform {
|
||||
return p === 'anthropic' || p === 'openai' || p === 'antigravity' || p === 'gemini' || p === 'grok'
|
||||
return p === 'anthropic' || p === 'openai' || p === 'antigravity' || p === 'gemini' || p === 'grok' || p === 'composite'
|
||||
}
|
||||
|
||||
export function platformBadgeClass(p: string): string {
|
||||
@@ -172,6 +183,7 @@ export function platformLabel(p: string): string {
|
||||
case 'antigravity': return 'Antigravity'
|
||||
case 'gemini': return 'Gemini'
|
||||
case 'grok': return 'Grok'
|
||||
case 'composite': return 'Composite'
|
||||
default: return p || 'API'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3841,6 +3841,7 @@ const platformOptions = computed(() => [
|
||||
{ value: "gemini", label: "Gemini" },
|
||||
{ value: "antigravity", label: "Antigravity" },
|
||||
{ value: "grok", label: "Grok" },
|
||||
{ value: "composite", label: "Composite" },
|
||||
]);
|
||||
|
||||
const platformFilterOptions = computed(() => [
|
||||
@@ -3850,6 +3851,7 @@ const platformFilterOptions = computed(() => [
|
||||
{ value: "gemini", label: "Gemini" },
|
||||
{ value: "antigravity", label: "Antigravity" },
|
||||
{ value: "grok", label: "Grok" },
|
||||
{ value: "composite", label: "Composite" },
|
||||
]);
|
||||
|
||||
const editStatusOptions = computed(() => [
|
||||
@@ -3936,29 +3938,40 @@ const invalidRequestFallbackOptionsForEdit = computed(() => {
|
||||
return options;
|
||||
});
|
||||
|
||||
// 复制账号的源分组选项(创建时)- 仅包含相同平台且有账号的分组
|
||||
const canCopyAccountsFromGroup = (targetPlatform: GroupPlatform, sourcePlatform: GroupPlatform) =>
|
||||
targetPlatform === "composite" || sourcePlatform === targetPlatform;
|
||||
|
||||
const copyAccountsGroupLabel = (g: AdminGroup) => {
|
||||
const count = g.account_count || 0;
|
||||
const platform = t("admin.groups.platforms." + g.platform);
|
||||
return `${g.name} - ${platform} (${t("admin.groups.accountsCount", { count })})`;
|
||||
};
|
||||
|
||||
// 复制账号的源分组选项(创建时)- 相同平台;composite 分组可汇总各平台账号
|
||||
const copyAccountsGroupOptions = computed(() => {
|
||||
const eligibleGroups = groups.value.filter(
|
||||
(g) => g.platform === createForm.platform && (g.account_count || 0) > 0,
|
||||
(g) =>
|
||||
canCopyAccountsFromGroup(createForm.platform, g.platform) &&
|
||||
(g.account_count || 0) > 0,
|
||||
);
|
||||
return eligibleGroups.map((g) => ({
|
||||
value: g.id,
|
||||
label: `${g.name} (${t("admin.groups.accountsCount", { count: g.account_count || 0 })})`,
|
||||
label: copyAccountsGroupLabel(g),
|
||||
}));
|
||||
});
|
||||
|
||||
// 复制账号的源分组选项(编辑时)- 仅包含相同平台且有账号的分组,排除自身
|
||||
// 复制账号的源分组选项(编辑时)- 相同平台;composite 分组可汇总各平台账号,排除自身
|
||||
const copyAccountsGroupOptionsForEdit = computed(() => {
|
||||
const currentId = editingGroup.value?.id;
|
||||
const eligibleGroups = groups.value.filter(
|
||||
(g) =>
|
||||
g.platform === editForm.platform &&
|
||||
canCopyAccountsFromGroup(editForm.platform, g.platform) &&
|
||||
(g.account_count || 0) > 0 &&
|
||||
g.id !== currentId,
|
||||
);
|
||||
return eligibleGroups.map((g) => ({
|
||||
value: g.id,
|
||||
label: `${g.name} (${t("admin.groups.accountsCount", { count: g.account_count || 0 })})`,
|
||||
label: copyAccountsGroupLabel(g),
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user