first commit
This commit is contained in:
11
src/App.vue
Normal file
11
src/App.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<Login />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Login from './views/Login.vue'
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 页面样式由 Login 组件内维护 */
|
||||
</style>
|
||||
9
src/api/auth.js
Normal file
9
src/api/auth.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import http from '../plugins/http'
|
||||
|
||||
// 登录 API 封装
|
||||
export function login(payload) {
|
||||
// 约定 payload: { username: string, password: string }
|
||||
// 根据实际接口调整路径与字段
|
||||
return http.post('/auth/login', payload)
|
||||
}
|
||||
|
||||
10
src/main.js
Normal file
10
src/main.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import { createApp } from 'vue'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
|
||||
import App from './App.vue'
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(ElementPlus)
|
||||
app.mount('#app')
|
||||
|
||||
29
src/plugins/http.js
Normal file
29
src/plugins/http.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import axios from 'axios'
|
||||
|
||||
const http = axios.create({
|
||||
baseURL: '/',
|
||||
timeout: 15000,
|
||||
})
|
||||
|
||||
// 请求拦截器
|
||||
http.interceptors.request.use(
|
||||
(config) => {
|
||||
// 例如:在此添加认证 token
|
||||
// const token = localStorage.getItem('token')
|
||||
// if (token) config.headers.Authorization = `Bearer ${token}`
|
||||
return config
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
)
|
||||
|
||||
// 响应拦截器
|
||||
http.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
// 统一错误处理(可以按需要自定义)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default http
|
||||
|
||||
141
src/views/Login.vue
Normal file
141
src/views/Login.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<div class="login-page">
|
||||
<el-card class="login-card" shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<img class="logo" alt="logo" src="https://vuejs.org/images/logo.png" />
|
||||
<div class="title">平台登录</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px" @keyup.enter.native="onSubmit">
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-input v-model.trim="form.username" placeholder="请输入用户名" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="密码" prop="password">
|
||||
<el-input v-model.trim="form.password" type="password" show-password placeholder="请输入密码" />
|
||||
</el-form-item>
|
||||
|
||||
<div class="actions">
|
||||
<el-checkbox v-model="remember">记住我</el-checkbox>
|
||||
<el-link type="primary" :underline="false" @click="onForget">忘记密码?</el-link>
|
||||
</div>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="loading" class="submit" @click="onSubmit">登 录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-alert
|
||||
v-if="notice"
|
||||
:title="notice"
|
||||
type="info"
|
||||
show-icon
|
||||
class="notice"
|
||||
/>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { login } from '../api/auth'
|
||||
|
||||
const formRef = ref()
|
||||
const loading = ref(false)
|
||||
const remember = ref(false)
|
||||
const notice = ref('')
|
||||
|
||||
const form = ref({
|
||||
username: '',
|
||||
password: '',
|
||||
})
|
||||
|
||||
const rules = {
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 6, message: '密码至少 6 位', trigger: 'blur' },
|
||||
],
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const saved = localStorage.getItem('login-remember')
|
||||
const savedUser = localStorage.getItem('login-username')
|
||||
remember.value = saved === '1'
|
||||
if (remember.value && savedUser) {
|
||||
form.value.username = savedUser
|
||||
}
|
||||
notice.value = import.meta?.env?.VITE_API_BASE
|
||||
? `当前 API: ${import.meta.env.VITE_API_BASE}`
|
||||
: '未配置 VITE_API_BASE,默认使用 /'
|
||||
})
|
||||
|
||||
function onForget() {
|
||||
ElMessage.info('请联系管理员重置密码')
|
||||
}
|
||||
|
||||
function persistRemember() {
|
||||
localStorage.setItem('login-remember', remember.value ? '1' : '0')
|
||||
if (remember.value) {
|
||||
localStorage.setItem('login-username', form.value.username || '')
|
||||
} else {
|
||||
localStorage.removeItem('login-username')
|
||||
}
|
||||
}
|
||||
|
||||
async function onSubmit() {
|
||||
if (!formRef.value) return
|
||||
await formRef.value.validate(async (valid) => {
|
||||
if (!valid) return
|
||||
loading.value = true
|
||||
try {
|
||||
const payload = { username: form.value.username, password: form.value.password }
|
||||
const res = await login(payload)
|
||||
// 依据后端返回结构处理,这里仅做示例提示
|
||||
ElMessage.success('登录成功')
|
||||
persistRemember()
|
||||
console.debug('login response:', res.data)
|
||||
// TODO: 登录成功后的跳转(如接入 Router)
|
||||
} catch (e) {
|
||||
const msg = e?.response?.data?.message || e.message || '登录失败'
|
||||
ElMessage.error(msg)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login-page {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #f2f6fc 0%, #ffffff 100%);
|
||||
}
|
||||
.login-card {
|
||||
width: 420px;
|
||||
}
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.logo { width: 32px; height: 32px; }
|
||||
.title { font-size: 18px; font-weight: 600; }
|
||||
.actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: -4px 0 8px;
|
||||
}
|
||||
.submit { width: 100%; }
|
||||
.notice { margin-top: 8px; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user