Files
admin-web/src/views/LoginView.vue
T

88 lines
3.6 KiB
Vue
Raw Normal View History

2026-07-30 17:59:43 +08:00
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { ArrowRight, LockKeyhole, ShieldCheck, UserRound } from '@lucide/vue'
import { getApiErrorDetails, type ApiErrorDetails } from '@/api/http'
import ApiErrorMeta from '@/components/ApiErrorMeta.vue'
import { sanitizeInternalPath } from '@/router/accessDecision'
import { useAccessPreviewStore } from '@/stores/accessPreview'
import { useAuthStore } from '@/stores/auth'
const route = useRoute()
const router = useRouter()
const auth = useAuthStore()
const access = useAccessPreviewStore()
const loading = ref(false)
const loginError = ref<ApiErrorDetails | null>(null)
const isDevelopment = import.meta.env.DEV
const developmentHint = isDevelopment ? '当前为前端开发环境,可使用预填账号进入。' : ''
const form = reactive({
account: isDevelopment ? 'platform_admin' : '',
password: isDevelopment ? 'ChangeMe123!' : '',
})
async function submit() {
loginError.value = null
if (!form.account || !form.password) {
loginError.value = { message: '请输入账号和密码' }
ElMessage.warning(loginError.value.message)
return
}
loading.value = true
try {
await auth.login(form.account, form.password)
access.initializeAfterLogin()
ElMessage.success('登录成功')
const requestedPath = sanitizeInternalPath(route.query.redirect)
const targetPath = requestedPath === '/'
? access.firstAccessiblePath ?? '/403'
: requestedPath
await router.replace(targetPath)
} catch (error) {
loginError.value = getApiErrorDetails(error, '登录失败,请检查账号和密码后重试')
ElMessage.error(loginError.value.message)
} finally {
loading.value = false
}
}
</script>
<template>
<div class="login-page">
<section class="login-intro">
<div class="login-brand"><span>CC</span> 虫虫出海</div>
<div>
<p class="eyebrow">CHONGCHONG GLOBAL</p>
<h1>让跨境物流管理<br />清晰、可靠、可追踪</h1>
<p class="intro-copy">统一管理企业入驻、内部权限和头程订单,让每一次操作都有边界、有记录。</p>
</div>
<div class="security-note">
<ShieldCheck :size="22" />
<span>平台内部系统,请使用授权账号登录</span>
</div>
</section>
<section class="login-panel">
<div class="login-card">
<div class="login-card-icon"><LockKeyhole :size="24" /></div>
<h2>登录管理后台</h2>
<p>使用平台内部员工账号登录</p>
<el-form label-position="top" size="large" @submit.prevent="submit">
<el-form-item label="账号">
<el-input v-model.trim="form.account" placeholder="请输入账号" :prefix-icon="UserRound" autocomplete="username" maxlength="100" />
</el-form-item>
<el-form-item label="密码">
<el-input v-model="form.password" type="password" show-password placeholder="请输入密码" :prefix-icon="LockKeyhole" autocomplete="current-password" maxlength="128" @keyup.enter="submit" />
</el-form-item>
<el-button class="login-submit" type="primary" :loading="loading" @click="submit">
登录 <ArrowRight :size="17" />
</el-button>
</el-form>
<div v-if="loginError" class="login-error" role="alert"><p>{{ loginError.message }}</p><ApiErrorMeta :details="loginError" /></div>
<div v-if="developmentHint" class="demo-tip" role="note">{{ developmentHint }}</div>
</div>
</section>
</div>
</template>