mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-13 12:39:50 +08:00
feat(provider): 补充 Windsurf 邮箱密码导入表单
This commit is contained in:
@@ -581,7 +581,70 @@
|
|||||||
class="flex flex-col gap-3 justify-center transition-opacity duration-150"
|
class="flex flex-col gap-3 justify-center transition-opacity duration-150"
|
||||||
:class="mode === 'import' ? 'opacity-100' : 'opacity-0 pointer-events-none'"
|
:class="mode === 'import' ? 'opacity-100' : 'opacity-0 pointer-events-none'"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
v-if="isWindsurfProvider"
|
||||||
|
class="grid grid-cols-2 gap-1.5 rounded-lg border border-border p-0.5 bg-muted/30"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
v-for="method in ([
|
||||||
|
{ key: 'email_password', label: '邮箱密码' },
|
||||||
|
{ key: 'token_json', label: 'Token / JSON' },
|
||||||
|
] as const)"
|
||||||
|
:key="method.key"
|
||||||
|
class="h-8 text-xs font-medium rounded-md transition-colors"
|
||||||
|
:class="windsurfImportMethod === method.key
|
||||||
|
? 'bg-background text-foreground shadow-sm'
|
||||||
|
: 'text-muted-foreground hover:text-foreground'"
|
||||||
|
:disabled="importing"
|
||||||
|
@click="setWindsurfImportMethod(method.key)"
|
||||||
|
>
|
||||||
|
{{ method.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="isWindsurfEmailPasswordImport"
|
||||||
|
class="space-y-3"
|
||||||
|
>
|
||||||
|
<div class="space-y-1.5">
|
||||||
|
<label class="text-xs font-medium">邮箱</label>
|
||||||
|
<input
|
||||||
|
v-model="windsurfEmail"
|
||||||
|
type="email"
|
||||||
|
autocomplete="username"
|
||||||
|
:disabled="importing"
|
||||||
|
placeholder="[email protected]"
|
||||||
|
class="w-full h-9 px-2.5 text-xs rounded-md border border-border bg-background focus:outline-none focus:ring-1 focus:ring-ring focus:relative focus:z-10"
|
||||||
|
spellcheck="false"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1.5">
|
||||||
|
<label class="text-xs font-medium">密码</label>
|
||||||
|
<input
|
||||||
|
v-model="windsurfPassword"
|
||||||
|
type="password"
|
||||||
|
autocomplete="current-password"
|
||||||
|
:disabled="importing"
|
||||||
|
placeholder="Windsurf 密码"
|
||||||
|
class="w-full h-9 px-2.5 text-xs rounded-md border border-border bg-background focus:outline-none focus:ring-1 focus:ring-ring focus:relative focus:z-10"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1.5">
|
||||||
|
<label class="text-xs font-medium text-muted-foreground">名称(可选)</label>
|
||||||
|
<input
|
||||||
|
v-model="windsurfAccountName"
|
||||||
|
type="text"
|
||||||
|
autocomplete="off"
|
||||||
|
:disabled="importing"
|
||||||
|
placeholder="未填写时使用邮箱"
|
||||||
|
class="w-full h-9 px-2.5 text-xs rounded-md border border-border bg-background focus:outline-none focus:ring-1 focus:ring-ring focus:relative focus:z-10"
|
||||||
|
spellcheck="false"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<JsonImportInput
|
<JsonImportInput
|
||||||
|
v-else
|
||||||
v-model="importText"
|
v-model="importText"
|
||||||
:disabled="importing"
|
:disabled="importing"
|
||||||
:reset-key="importInputResetKey"
|
:reset-key="importInputResetKey"
|
||||||
@@ -596,7 +659,7 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="importTask"
|
v-if="importTask && !isWindsurfEmailPasswordImport"
|
||||||
class="rounded-xl border border-border bg-muted/20 p-3 space-y-2"
|
class="rounded-xl border border-border bg-muted/20 p-3 space-y-2"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between text-xs">
|
<div class="flex items-center justify-between text-xs">
|
||||||
@@ -669,7 +732,7 @@
|
|||||||
:disabled="!canImport"
|
:disabled="!canImport"
|
||||||
@click="handleImport"
|
@click="handleImport"
|
||||||
>
|
>
|
||||||
{{ importing ? (importTask ? `导入中 ${importTask.progress_percent}%` : '导入中...') : importButtonLabel }}
|
{{ importButtonText }}
|
||||||
</Button>
|
</Button>
|
||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@@ -774,6 +837,7 @@ function getSelectedNodeLabel(): string {
|
|||||||
// 模式
|
// 模式
|
||||||
type DialogMode = 'oauth' | 'import'
|
type DialogMode = 'oauth' | 'import'
|
||||||
const mode = ref<DialogMode>((props.providerType || '').toLowerCase() === 'grok' ? 'import' : 'oauth')
|
const mode = ref<DialogMode>((props.providerType || '').toLowerCase() === 'grok' ? 'import' : 'oauth')
|
||||||
|
type WindsurfImportMethod = 'email_password' | 'token_json'
|
||||||
|
|
||||||
// OAuth 状态
|
// OAuth 状态
|
||||||
interface OAuthState {
|
interface OAuthState {
|
||||||
@@ -862,6 +926,10 @@ const importInputResetKey = ref(0)
|
|||||||
const importTask = ref<OAuthBatchImportTaskStatusResponse | null>(null)
|
const importTask = ref<OAuthBatchImportTaskStatusResponse | null>(null)
|
||||||
let importPollTimer: ReturnType<typeof setTimeout> | null = null
|
let importPollTimer: ReturnType<typeof setTimeout> | null = null
|
||||||
const importPolling = ref(false)
|
const importPolling = ref(false)
|
||||||
|
const windsurfImportMethod = ref<WindsurfImportMethod>('email_password')
|
||||||
|
const windsurfEmail = ref('')
|
||||||
|
const windsurfPassword = ref('')
|
||||||
|
const windsurfAccountName = ref('')
|
||||||
|
|
||||||
const isOpen = computed(() => props.open)
|
const isOpen = computed(() => props.open)
|
||||||
|
|
||||||
@@ -920,6 +988,11 @@ const canCompleteDeviceAuth = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const canImport = computed(() => {
|
const canImport = computed(() => {
|
||||||
|
if (isWindsurfEmailPasswordImport.value) {
|
||||||
|
return windsurfEmail.value.trim().length > 0
|
||||||
|
&& windsurfPassword.value.trim().length > 0
|
||||||
|
&& !importing.value
|
||||||
|
}
|
||||||
return importText.value.trim().length > 0 && !importing.value
|
return importText.value.trim().length > 0 && !importing.value
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -934,7 +1007,9 @@ const importDropHint = computed(() => (
|
|||||||
const importManualPlaceholder = computed(() => (
|
const importManualPlaceholder = computed(() => (
|
||||||
isGrokProvider.value
|
isGrokProvider.value
|
||||||
? '粘贴 Grok sso/session token,支持每行一个;或粘贴包含 token、sso_token、access_token、plan_type、pool_tier 的 JSON'
|
? '粘贴 Grok sso/session token,支持每行一个;或粘贴包含 token、sso_token、access_token、plan_type、pool_tier 的 JSON'
|
||||||
: '粘贴 Refresh Token / Access Token 或 JSON 内容'
|
: isWindsurfProvider.value
|
||||||
|
? '粘贴 show-auth-token Token、API key 或 JSON 内容'
|
||||||
|
: '粘贴 Refresh Token / Access Token 或 JSON 内容'
|
||||||
))
|
))
|
||||||
const importManualDescription = computed(() => (
|
const importManualDescription = computed(() => (
|
||||||
isGrokProvider.value
|
isGrokProvider.value
|
||||||
@@ -948,6 +1023,18 @@ const importFileToggleText = computed(() => (
|
|||||||
isGrokProvider.value ? '或选择 Grok Token 文件导入' : '或选择 JSON 文件导入'
|
isGrokProvider.value ? '或选择 Grok Token 文件导入' : '或选择 JSON 文件导入'
|
||||||
))
|
))
|
||||||
const providerCredentialActionLabel = computed(() => (isGrokProvider.value ? '导入' : '授权'))
|
const providerCredentialActionLabel = computed(() => (isGrokProvider.value ? '导入' : '授权'))
|
||||||
|
const isWindsurfEmailPasswordImport = computed(() =>
|
||||||
|
isWindsurfProvider.value && windsurfImportMethod.value === 'email_password'
|
||||||
|
)
|
||||||
|
|
||||||
|
const importButtonText = computed(() => {
|
||||||
|
if (importing.value) {
|
||||||
|
return importTask.value && !isWindsurfEmailPasswordImport.value
|
||||||
|
? `导入中 ${importTask.value.progress_percent}%`
|
||||||
|
: '导入中...'
|
||||||
|
}
|
||||||
|
return isWindsurfEmailPasswordImport.value ? '登录并导入' : importButtonLabel.value
|
||||||
|
})
|
||||||
|
|
||||||
function stopImportPolling() {
|
function stopImportPolling() {
|
||||||
if (importPollTimer) {
|
if (importPollTimer) {
|
||||||
@@ -1153,6 +1240,10 @@ function resetForm() {
|
|||||||
importing.value = false
|
importing.value = false
|
||||||
importTask.value = null
|
importTask.value = null
|
||||||
importInputResetKey.value += 1
|
importInputResetKey.value += 1
|
||||||
|
windsurfImportMethod.value = 'email_password'
|
||||||
|
windsurfEmail.value = ''
|
||||||
|
windsurfPassword.value = ''
|
||||||
|
windsurfAccountName.value = ''
|
||||||
proxyPopoverOpen.value = false
|
proxyPopoverOpen.value = false
|
||||||
selectedProxyNodeId.value = ''
|
selectedProxyNodeId.value = ''
|
||||||
mode.value = defaultMode.value
|
mode.value = defaultMode.value
|
||||||
@@ -1468,8 +1559,47 @@ function handleImportInputError(payload: { message: string; title?: string }) {
|
|||||||
showError(payload.message, payload.title)
|
showError(payload.message, payload.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setWindsurfImportMethod(method: WindsurfImportMethod) {
|
||||||
|
if (!isWindsurfProvider.value || importing.value) return
|
||||||
|
windsurfImportMethod.value = method
|
||||||
|
importTask.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleWindsurfEmailPasswordImport() {
|
||||||
|
if (!props.providerId) return
|
||||||
|
|
||||||
|
const email = windsurfEmail.value.trim()
|
||||||
|
const password = windsurfPassword.value.trim()
|
||||||
|
if (!email || !password) {
|
||||||
|
showError('请输入邮箱和密码', '格式错误')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
importing.value = true
|
||||||
|
try {
|
||||||
|
const result = await importProviderRefreshToken(props.providerId, {
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
name: windsurfAccountName.value.trim() || email,
|
||||||
|
proxy_node_id: selectedProxyNodeId.value || undefined,
|
||||||
|
})
|
||||||
|
success(getOAuthSuccessMessage('导入', result))
|
||||||
|
emit('saved')
|
||||||
|
handleClose()
|
||||||
|
} catch (err: unknown) {
|
||||||
|
const errorMessage = parseApiError(err, '导入失败')
|
||||||
|
showError(errorMessage, '错误')
|
||||||
|
} finally {
|
||||||
|
importing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleImport() {
|
async function handleImport() {
|
||||||
if (!canImport.value || !props.providerId) return
|
if (!canImport.value || !props.providerId) return
|
||||||
|
if (isWindsurfEmailPasswordImport.value) {
|
||||||
|
await handleWindsurfEmailPasswordImport()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const inputText = importText.value.trim()
|
const inputText = importText.value.trim()
|
||||||
if (!inputText) {
|
if (!inputText) {
|
||||||
|
|||||||
Reference in New Issue
Block a user