index.vue 4.53 KB
<script setup lang="ts">
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { AuthApi } from '@/api/login'
import { useUserStore } from '@/store'
import { APP_TITLE, HOME_PATH } from '@/config'
import type { LoginReqVO } from '@/types'

/**
 * 登录页
 *
 * 验证码按后端能力动态显示:GET /system/auth/captcha-info 返回 captchaEnable。
 * P0 后端返回 false(内存占位实现),P1 接入真实验证码后前端**无需改动**。
 */
const router = useRouter()
const route = useRoute()
const userStore = useUserStore()

const formRef = ref<FormInstance>()
const loading = ref(false)
const captchaEnable = ref(false)
const captchaImage = ref('')
const captchaUuid = ref('')

const form = reactive<LoginReqVO>({
  username: '',
  password: '',
  captchaCode: '',
  captchaUuid: ''
})

const rules: FormRules = {
  username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
  password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
}

async function loadCaptcha() {
  try {
    const data = await AuthApi.getCaptchaInfo()
    captchaEnable.value = !!data?.captchaEnable
    captchaImage.value = data?.captchaImage || ''
    captchaUuid.value = data?.captchaUuid || ''
    form.captchaUuid = captchaUuid.value
  } catch (e) {
    // 验证码接口不可用时降级为不显示验证码,不阻塞登录
    captchaEnable.value = false
  }
}

async function handleLogin() {
  const valid = await formRef.value?.validate().catch(() => false)
  if (!valid) return
  loading.value = true
  try {
    await userStore.login({ ...form })
    ElMessage.success('登录成功')
    const redirect = (route.query.redirect as string) || HOME_PATH
    await router.replace(redirect)
  } catch (e: any) {
    console.error('[login] 登录失败', e)
    // 登录失败刷新验证码(若启用)
    if (captchaEnable.value) {
      form.captchaCode = ''
      await loadCaptcha()
    }
  } finally {
    loading.value = false
  }
}

onMounted(loadCaptcha)
</script>

<template>
  <div class="login-page">
    <div class="login-box">
      <div class="login-header">
        <div class="login-title">{{ APP_TITLE }}</div>
        <div class="login-subtitle">停车场 · 充电桩 · 支付结算 一体化运营平台</div>
      </div>

      <el-form ref="formRef" :model="form" :rules="rules" size="large" @keyup.enter="handleLogin">
        <el-form-item prop="username">
          <el-input v-model="form.username" placeholder="请输入用户名" clearable>
            <template #prefix><Icon icon="ep:user" :size="16" /></template>
          </el-input>
        </el-form-item>

        <el-form-item prop="password">
          <el-input v-model="form.password" type="password" placeholder="请输入密码" show-password>
            <template #prefix><Icon icon="ep:lock" :size="16" /></template>
          </el-input>
        </el-form-item>

        <el-form-item v-if="captchaEnable" prop="captchaCode">
          <div class="captcha-row">
            <el-input v-model="form.captchaCode" placeholder="请输入验证码" clearable />
            <img v-if="captchaImage" :src="captchaImage" class="captcha-img" title="点击刷新" @click="loadCaptcha" />
          </div>
        </el-form-item>

        <el-button type="primary" class="login-btn" :loading="loading" @click="handleLogin">
          {{ loading ? '登录中…' : '登 录' }}
        </el-button>
      </el-form>

      <div class="login-footer">
        <span>© {{ new Date().getFullYear() }} 中兴智能交通股份有限公司</span>
      </div>
    </div>
  </div>
</template>

<style scoped lang="scss">
.login-page {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, #1f2d3d 0%, #2b5876 55%, #4e4376 100%);
}

.login-box {
  width: 400px;
  padding: 32px 34px 22px;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.22);
}

.login-header {
  text-align: center;
  margin-bottom: 24px;
}

.login-title {
  font-size: 20px;
  font-weight: 600;
  color: $text-color-primary;
}

.login-subtitle {
  margin-top: 8px;
  font-size: 12px;
  color: $text-color-secondary;
}

.captcha-row {
  display: flex;
  gap: 8px;
  width: 100%;
}

.captcha-img {
  height: 40px;
  width: 110px;
  cursor: pointer;
  border-radius: 4px;
  border: 1px solid $border-color-light;
  flex-shrink: 0;
}

.login-btn {
  width: 100%;
  margin-top: 4px;
}

.login-footer {
  margin-top: 18px;
  text-align: center;
  font-size: 12px;
  color: $text-color-secondary;
}
</style>