feat(website): 支持基于现有绑定一键整理分组,支持账号校验补齐、自愈重建与缺失 Key 提示 (含异常保守跳过及名称层级匹配)

This commit is contained in:
liumangmang
2026-07-01 11:21:20 +08:00
parent bf5470caf3
commit 397a14c978
5 changed files with 828 additions and 0 deletions
+95
View File
@@ -111,6 +111,16 @@
>
一键同步
</el-button>
<el-button
size="small"
text
:loading="organizeLoading"
:disabled="!selectedWebsite || selectedWebsiteBindings.length === 0"
@click="organizeWebsiteGroups"
title="按当前网站的现有绑定关系一键整理分组并导入账号"
>
一键整理分组
</el-button>
<el-button size="small" text :disabled="websites.length === 0" @click="openBindingCreate(selectedWebsite || websites[0])">新增绑定</el-button>
</div>
<div class="binding-list" v-loading="bindingLoading">
@@ -449,6 +459,58 @@
<el-button type="primary" :loading="importingAccounts" :disabled="importingAccounts" @click="submitImportAccounts">创建账号管理账号</el-button>
</template>
</el-dialog>
<el-dialog v-model="organizeDialog" title="一键整理分组结果" width="850px" destroy-on-close>
<div v-if="organizeMessage" style="margin-bottom:12px; font-weight:bold; color:var(--el-color-primary)">
{{ organizeMessage }}
</div>
<el-table :data="organizeResults" size="small" border style="width:100%">
<el-table-column label="目标分组" min-width="150">
<template #default="{ row }">
<div>{{ row.target_group_name }}</div>
<div style="font-size:11px;color:var(--text-muted)">ID: {{ row.target_group_id }}</div>
</template>
</el-table-column>
<el-table-column label="来源上游/分组" min-width="165">
<template #default="{ row }">
<div>{{ row.upstream_name }}</div>
<div style="font-size:11px;color:var(--text-muted)">{{ row.source_group_name }} (ID: {{ row.source_group_id }})</div>
</template>
</el-table-column>
<el-table-column label="上游 Key" min-width="140">
<template #default="{ row }">
<span v-if="row.key_name">{{ row.key_name }}</span>
<span v-else class="muted"></span>
</template>
</el-table-column>
<el-table-column label="目标账号" min-width="150">
<template #default="{ row }">
<div v-if="row.account_id">
<div>{{ row.account_name }}</div>
<div style="font-size:11px;color:var(--text-muted)">ID: {{ row.account_id }}</div>
</div>
<span v-else class="muted"></span>
</template>
</el-table-column>
<el-table-column label="状态" width="110">
<template #default="{ row }">
<el-tag v-if="row.status === 'created'" size="small" type="success">已创建</el-tag>
<el-tag v-else-if="row.status === 'recreated'" size="small" type="warning">清理后重建</el-tag>
<el-tag v-else-if="row.status === 'exists'" size="small" type="info">已存在</el-tag>
<el-tag v-else-if="row.status === 'missing_key'" size="small" type="danger">缺少 Key</el-tag>
<el-tag v-else size="small" type="danger">失败</el-tag>
</template>
</el-table-column>
<el-table-column prop="message" label="说明/原因" min-width="150">
<template #default="{ row }">
<span :class="{ 'error-text': row.status === 'failed' }">{{ row.message }}</span>
</template>
</el-table-column>
</el-table>
<template #footer>
<el-button @click="organizeDialog = false">关闭</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -472,6 +534,7 @@ import {
type WebsiteForm,
type WebsiteGroup,
type WebsiteSyncLog,
type OrganizeGroupsItem,
} from '@/api'
const websites = ref<(WebsiteData & { _testing?: boolean })[]>([])
@@ -576,6 +639,11 @@ const importAccountsForm = ref({
})
const importAccountResults = ref<ImportAccountItem[]>([])
const organizeDialog = ref(false)
const organizeLoading = ref(false)
const organizeResults = ref<OrganizeGroupsItem[]>([])
const organizeMessage = ref('')
const upstreamGroupOptions = computed(() => {
const rows: Array<{ key: string; label: string; source: BindingSourceGroup }> = []
for (const upstream of upstreams.value) {
@@ -1000,6 +1068,33 @@ async function syncAllWebsiteBindings() {
}
}
async function organizeWebsiteGroups() {
if (!selectedWebsite.value) return
try {
await ElMessageBox.confirm(
'一键整理分组将根据当前的“分组管理”绑定关系,自动执行已导入账号的校验补齐,以及未导入账号的自动创建。确认执行?',
'一键整理分组',
{ type: 'info' }
)
} catch {
return
}
organizeLoading.value = true
try {
const res = await websitesApi.organizeGroups(selectedWebsite.value.id)
organizeMessage.value = res.data.message
organizeResults.value = res.data.items
organizeDialog.value = true
// 刷新相关数据
await Promise.all([loadLogs(), loadWebsiteGroups(), loadBindings()])
} catch (e: any) {
ElMessage.error(e.response?.data?.detail || '一键整理分组失败')
} finally {
organizeLoading.value = false
}
}
async function toggleBinding(row: GroupBindingData) {
try {
await websitesApi.updateBinding(row.id, { enabled: row.enabled })