From d9e27df9afeed5b04387264d87e3173b78e56926 Mon Sep 17 00:00:00 2001 From: shaw Date: Tue, 23 Dec 2025 11:20:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=B4=A6=E5=8F=B7=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E6=89=80=E5=B1=9E=E5=88=86=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Account模型新增Groups虚拟字段 - 账号列表API预加载Group信息 - 账号管理页面新增分组列,使用GroupBadge展示 --- backend/internal/model/account.go | 3 ++- backend/internal/repository/account_repo.go | 8 ++++++-- frontend/src/i18n/locales/en.ts | 1 + frontend/src/i18n/locales/zh.ts | 3 +++ frontend/src/types/index.ts | 1 + frontend/src/views/admin/AccountsView.vue | 17 +++++++++++++++++ 6 files changed, 30 insertions(+), 3 deletions(-) diff --git a/backend/internal/model/account.go b/backend/internal/model/account.go index 28e96ef9..9b09b114 100644 --- a/backend/internal/model/account.go +++ b/backend/internal/model/account.go @@ -68,7 +68,8 @@ type Account struct { AccountGroups []AccountGroup `gorm:"foreignKey:AccountID" json:"account_groups,omitempty"` // 虚拟字段 (不存储到数据库) - GroupIDs []int64 `gorm:"-" json:"group_ids,omitempty"` + GroupIDs []int64 `gorm:"-" json:"group_ids,omitempty"` + Groups []*Group `gorm:"-" json:"groups,omitempty"` } func (Account) TableName() string { diff --git a/backend/internal/repository/account_repo.go b/backend/internal/repository/account_repo.go index 639e8046..1296ef1b 100644 --- a/backend/internal/repository/account_repo.go +++ b/backend/internal/repository/account_repo.go @@ -78,15 +78,19 @@ func (r *AccountRepository) ListWithFilters(ctx context.Context, params paginati return nil, nil, err } - if err := db.Preload("Proxy").Preload("AccountGroups").Offset(params.Offset()).Limit(params.Limit()).Order("id DESC").Find(&accounts).Error; err != nil { + if err := db.Preload("Proxy").Preload("AccountGroups.Group").Offset(params.Offset()).Limit(params.Limit()).Order("id DESC").Find(&accounts).Error; err != nil { return nil, nil, err } - // 填充每个 Account 的 GroupIDs 虚拟字段 + // 填充每个 Account 的虚拟字段(GroupIDs 和 Groups) for i := range accounts { accounts[i].GroupIDs = make([]int64, 0, len(accounts[i].AccountGroups)) + accounts[i].Groups = make([]*model.Group, 0, len(accounts[i].AccountGroups)) for _, ag := range accounts[i].AccountGroups { accounts[i].GroupIDs = append(accounts[i].GroupIDs, ag.GroupID) + if ag.Group != nil { + accounts[i].Groups = append(accounts[i].Groups, ag.Group) + } } } diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index d632ea29..fd391c30 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -666,6 +666,7 @@ export default { status: 'Status', schedulable: 'Schedule', todayStats: "Today's Stats", + groups: 'Groups', usageWindows: 'Usage Windows', priority: 'Priority', lastUsed: 'Last Used', diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index b8859c23..d1ecc119 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -721,6 +721,9 @@ export default { weight: '权重', status: '状态', schedulable: '调度', + todayStats: '今日统计', + groups: '分组', + usageWindows: '用量窗口', lastUsed: '最近使用', actions: '操作', }, diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index de5ac92c..1f1ae441 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -324,6 +324,7 @@ export interface Account { updated_at: string; proxy?: Proxy; group_ids?: number[]; // Groups this account belongs to + groups?: Group[]; // Preloaded group objects // Rate limit & scheduling fields schedulable: boolean; diff --git a/frontend/src/views/admin/AccountsView.vue b/frontend/src/views/admin/AccountsView.vue index 04c6a959..6b55fe42 100644 --- a/frontend/src/views/admin/AccountsView.vue +++ b/frontend/src/views/admin/AccountsView.vue @@ -123,6 +123,21 @@ + + @@ -301,6 +316,7 @@ import AccountStatusIndicator from '@/components/account/AccountStatusIndicator. import AccountUsageCell from '@/components/account/AccountUsageCell.vue' import AccountTodayStatsCell from '@/components/account/AccountTodayStatsCell.vue' import AccountTestModal from '@/components/account/AccountTestModal.vue' +import GroupBadge from '@/components/common/GroupBadge.vue' import { formatRelativeTime } from '@/utils/format' const { t } = useI18n() @@ -314,6 +330,7 @@ const columns = computed(() => [ { key: 'status', label: t('admin.accounts.columns.status'), sortable: true }, { key: 'schedulable', label: t('admin.accounts.columns.schedulable'), sortable: true }, { key: 'today_stats', label: t('admin.accounts.columns.todayStats'), sortable: false }, + { key: 'groups', label: t('admin.accounts.columns.groups'), sortable: false }, { key: 'usage', label: t('admin.accounts.columns.usageWindows'), sortable: false }, { key: 'priority', label: t('admin.accounts.columns.priority'), sortable: true }, { key: 'last_used_at', label: t('admin.accounts.columns.lastUsed'), sortable: true },