feat(desktop): remember login credentials
Desktop Client Build / Resolve Build Metadata (push) Successful in 49s
Frontend CI / Frontend (push) Successful in 5m47s
Desktop Client Build / Build Desktop Client (push) Successful in 42m28s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 2m38s

This commit is contained in:
2026-05-14 21:49:27 +08:00
parent 639516f374
commit 5bbbbc5cf1
5 changed files with 181 additions and 16 deletions
+10
View File
@@ -17,6 +17,11 @@ declare global {
keepRunningInBackground: boolean
}
interface DesktopLoginCredentials {
identifier: string
password: string
}
interface Window {
desktopBridge: {
app: {
@@ -54,6 +59,11 @@ declare global {
openExternalUrl(url: string): Promise<null>
openSettingsWindow(): Promise<null>
getAppSettings(): Promise<DesktopAppSettings>
getLoginCredentials(): Promise<DesktopLoginCredentials | null>
saveLoginCredentials(
credentials: DesktopLoginCredentials,
): Promise<DesktopLoginCredentials>
clearLoginCredentials(): Promise<null>
setAppSetting(key: keyof DesktopAppSettings, value: boolean): Promise<DesktopAppSettings>
syncRuntimeSession(session: DesktopRuntimeSessionSyncRequest | null): Promise<null>
rotateRuntimeClientToken(): Promise<DesktopClientRotateResponse>
@@ -7,15 +7,15 @@ const { apiBaseURL, error, pending, setApiBaseURL, login } = useDesktopSession()
const menuOpen = ref(false)
const showServerDialog = ref(false)
const showPassword = ref(false)
const rememberAccount = ref(!!localStorage.getItem('geo_rankly_saved_identifier'))
const savedIdentifier =
const legacySavedIdentifier =
localStorage.getItem('geo_rankly_saved_identifier') ??
localStorage.getItem('geo_rankly_saved_email') ??
''
const rememberCredentials = ref(!!legacySavedIdentifier)
const form = reactive({
apiBaseURL: apiBaseURL.value,
identifier: savedIdentifier,
identifier: legacySavedIdentifier,
password: '',
})
@@ -25,8 +25,11 @@ function toggleMenu() {
menuOpen.value = !menuOpen.value
}
function onRememberChange(event: Event) {
rememberAccount.value = (event.target as HTMLInputElement).checked
async function onRememberChange(event: Event) {
rememberCredentials.value = (event.target as HTMLInputElement).checked
if (!rememberCredentials.value) {
await clearSavedCredentials()
}
}
function handleOutsideClick(event: MouseEvent) {
@@ -53,22 +56,58 @@ function saveServerSettings() {
}
async function submitLogin() {
localStorage.removeItem('geo_rankly_saved_password')
if (rememberAccount.value) {
localStorage.setItem('geo_rankly_saved_identifier', form.identifier)
localStorage.removeItem('geo_rankly_saved_email')
} else {
localStorage.removeItem('geo_rankly_saved_identifier')
localStorage.removeItem('geo_rankly_saved_email')
}
await login({
identifier: form.identifier,
password: form.password,
})
await persistCredentialsPreference()
await window.desktopBridge?.app?.setWindowMode?.('main', { source: 'login' })
}
async function hydrateSavedCredentials() {
try {
const saved = await window.desktopBridge?.app?.getLoginCredentials?.()
if (!saved) return
form.identifier = saved.identifier
form.password = saved.password
rememberCredentials.value = true
} catch (err) {
console.warn('[login-view] load saved credentials failed', err)
}
}
async function clearSavedCredentials() {
localStorage.removeItem('geo_rankly_saved_identifier')
localStorage.removeItem('geo_rankly_saved_email')
localStorage.removeItem('geo_rankly_saved_password')
try {
await window.desktopBridge?.app?.clearLoginCredentials?.()
} catch (err) {
console.warn('[login-view] clear saved credentials failed', err)
}
}
async function persistCredentialsPreference() {
if (!rememberCredentials.value) {
await clearSavedCredentials()
return
}
const identifier = form.identifier.trim()
localStorage.setItem('geo_rankly_saved_identifier', identifier)
localStorage.removeItem('geo_rankly_saved_email')
localStorage.removeItem('geo_rankly_saved_password')
try {
await window.desktopBridge?.app?.saveLoginCredentials?.({
identifier,
password: form.password,
})
} catch (err) {
console.warn('[login-view] save credentials failed', err)
}
}
const lockCountdown = ref(0)
let countdownTimer: ReturnType<typeof setInterval> | null = null
@@ -103,6 +142,7 @@ const displayError = computed(() => {
onMounted(() => {
document.addEventListener('mousedown', handleOutsideClick)
void hydrateSavedCredentials()
})
onBeforeUnmount(() => {
@@ -178,8 +218,8 @@ onBeforeUnmount(() => {
<div class="options-row">
<label class="check-label">
<input type="checkbox" :checked="rememberAccount" @change="onRememberChange" />
<span>记住账号</span>
<input type="checkbox" :checked="rememberCredentials" @change="onRememberChange" />
<span>记住账号和密码</span>
</label>
</div>