88 lines
3.6 KiB
Vue
88 lines
3.6 KiB
Vue
<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>
|