feat(website): 支持基于现有绑定一键整理分组,支持账号校验补齐、自愈重建与缺失 Key 提示 (含异常保守跳过及名称层级匹配)
This commit is contained in:
@@ -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 })
|
||||
|
||||
Reference in New Issue
Block a user