fix(frontend): 批量编辑添加跨平台模型映射警告与过滤

- 新增 selectedPlatforms prop,从父组件传入选中账号的平台集合
- 根据选中平台过滤模型列表与预设映射按钮,避免误操作
- 混选多平台时显示 amber 警告横幅,提醒用户注意映射适用性
- 仅警告不阻断,保持加法兼容
This commit is contained in:
cagedbird043
2026-02-26 15:24:50 +08:00
parent 0dd6986e28
commit e350aab1bd
2 changed files with 49 additions and 5 deletions

View File

@@ -21,6 +21,16 @@
</p>
</div>
<!-- Mixed platform warning -->
<div v-if="isMixedPlatform" class="rounded-lg bg-amber-50 p-4 dark:bg-amber-900/20">
<p class="text-sm text-amber-700 dark:text-amber-400">
<svg class="mr-1.5 inline h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
{{ t('admin.accounts.bulkEdit.mixedPlatformWarning', { platforms: selectedPlatforms.join(', ') }) }}
</p>
</div>
<!-- Base URL (API Key only) -->
<div class="border-t border-gray-200 pt-4 dark:border-dark-600">
<div class="mb-3 flex items-center justify-between">
@@ -157,7 +167,7 @@
<!-- Model Checkbox List -->
<div class="mb-3 grid grid-cols-2 gap-2">
<label
v-for="model in allModels"
v-for="model in filteredModels"
:key="model.value"
class="flex cursor-pointer items-center rounded-lg border p-3 transition-all hover:bg-gray-50 dark:border-dark-600 dark:hover:bg-dark-700"
:class="
@@ -278,7 +288,7 @@
<!-- Quick Add Buttons -->
<div class="flex flex-wrap gap-2">
<button
v-for="preset in presetMappings"
v-for="preset in filteredPresets"
:key="preset.label"
type="button"
:class="['rounded-lg px-3 py-1 text-xs transition-colors', preset.color]"
@@ -648,7 +658,7 @@ import { ref, watch, computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app'
import { adminAPI } from '@/api/admin'
import type { Proxy as ProxyConfig, AdminGroup } from '@/types'
import type { Proxy as ProxyConfig, AdminGroup, AccountPlatform } from '@/types'
import BaseDialog from '@/components/common/BaseDialog.vue'
import Select from '@/components/common/Select.vue'
import ProxySelector from '@/components/common/ProxySelector.vue'
@@ -659,6 +669,7 @@ import { buildModelMappingObject as buildModelMappingPayload } from '@/composabl
interface Props {
show: boolean
accountIds: number[]
selectedPlatforms: AccountPlatform[]
proxies: ProxyConfig[]
groups: AdminGroup[]
}
@@ -672,6 +683,31 @@ const emit = defineEmits<{
const { t } = useI18n()
const appStore = useAppStore()
// Platform awareness
const isMixedPlatform = computed(() => props.selectedPlatforms.length > 1)
const platformModelPrefix: Record<string, string[]> = {
anthropic: ['claude-'],
antigravity: ['claude-'],
openai: ['gpt-'],
gemini: ['gemini-'],
sora: []
}
const filteredModels = computed(() => {
if (props.selectedPlatforms.length === 0) return allModels
const prefixes = [...new Set(props.selectedPlatforms.flatMap(p => platformModelPrefix[p] || []))]
if (prefixes.length === 0) return allModels
return allModels.filter(m => prefixes.some(prefix => m.value.startsWith(prefix)))
})
const filteredPresets = computed(() => {
if (props.selectedPlatforms.length === 0) return presetMappings
const prefixes = [...new Set(props.selectedPlatforms.flatMap(p => platformModelPrefix[p] || []))]
if (prefixes.length === 0) return presetMappings
return presetMappings.filter(m => prefixes.some(prefix => m.from.startsWith(prefix)))
})
// Model mapping type
interface ModelMapping {
from: string

View File

@@ -259,7 +259,7 @@
<AccountActionMenu :show="menu.show" :account="menu.acc" :position="menu.pos" @close="menu.show = false" @test="handleTest" @stats="handleViewStats" @reauth="handleReAuth" @refresh-token="handleRefresh" @reset-status="handleResetStatus" @clear-rate-limit="handleClearRateLimit" />
<SyncFromCrsModal :show="showSync" @close="showSync = false" @synced="reload" />
<ImportDataModal :show="showImportData" @close="showImportData = false" @imported="handleDataImported" />
<BulkEditAccountModal :show="showBulkEdit" :account-ids="selIds" :proxies="proxies" :groups="groups" @close="showBulkEdit = false" @updated="handleBulkUpdated" />
<BulkEditAccountModal :show="showBulkEdit" :account-ids="selIds" :selected-platforms="selPlatforms" :proxies="proxies" :groups="groups" @close="showBulkEdit = false" @updated="handleBulkUpdated" />
<TempUnschedStatusModal :show="showTempUnsched" :account="tempUnschedAcc" @close="showTempUnsched = false" @reset="handleTempUnschedReset" />
<ConfirmDialog :show="showDeleteDialog" :title="t('admin.accounts.deleteAccount')" :message="t('admin.accounts.deleteConfirm', { name: deletingAcc?.name })" :confirm-text="t('common.delete')" :cancel-text="t('common.cancel')" :danger="true" @confirm="confirmDelete" @cancel="showDeleteDialog = false" />
<ConfirmDialog :show="showExportDataDialog" :title="t('admin.accounts.dataExport')" :message="t('admin.accounts.dataExportConfirmMessage')" :confirm-text="t('admin.accounts.dataExportConfirm')" :cancel-text="t('common.cancel')" @confirm="handleExportData" @cancel="showExportDataDialog = false">
@@ -303,7 +303,7 @@ import PlatformTypeBadge from '@/components/common/PlatformTypeBadge.vue'
import Icon from '@/components/icons/Icon.vue'
import ErrorPassthroughRulesModal from '@/components/admin/ErrorPassthroughRulesModal.vue'
import { formatDateTime, formatRelativeTime } from '@/utils/format'
import type { Account, Proxy, AdminGroup } from '@/types'
import type { Account, AccountPlatform, Proxy, AdminGroup } from '@/types'
const { t } = useI18n()
const appStore = useAppStore()
@@ -312,6 +312,14 @@ const authStore = useAuthStore()
const proxies = ref<Proxy[]>([])
const groups = ref<AdminGroup[]>([])
const selIds = ref<number[]>([])
const selPlatforms = computed<AccountPlatform[]>(() => {
const platforms = new Set(
accounts.value
.filter(a => selIds.value.includes(a.id))
.map(a => a.platform)
)
return [...platforms]
})
const showCreate = ref(false)
const showEdit = ref(false)
const showSync = ref(false)