系列文章目录
本系列记录一下通过Abp搭建后端,Vue+Element UI Plus搭建前端,实现一个小型项目的过程。
- Day 1 Vue 页面框架
- Day 2 Abp框架下,MySQL数据迁移时,添加表和字段注释
- Day 3 登录页以及路由 (一)
- Day 4 登录页及路由 (二)Vue状态管理
- Day 5 登录页及路由 (三) 基于axios的API调用
文章目录
目录
系列文章目录
文章目录
前言
一、整体布局
二、登录窗体
三、API 调用修改
总结
前言
还是走到了这一步,要涉足我的盲区,样式和布局了。先看看最终的效果:
一、页面需求
这是一个非常非常简单的登录页,基本功能如下:
1. 提供账号密码输入框
2. 输入框左侧显示图标
3. 账号可以一键清空
4. 密码可以查看明文
5. 账号不能为空;密码不能为空,密码长度6-12位
6. 重置按钮清空输入的账号密码
7. 登录按钮调用api,成功后转到/home/index,失败则弹出失败消息。
一、整体布局
登录视图中,背景为统一颜色 #2b4b6b,非常好记的一个颜色值。然后整体居中。这个居中是通过视频中的方法,先绝对定位,然后左上点到正中央,再平移回去,这个思路对于居中只知道 text-align的我而言就友好多了。
具体布局如下:
src/views/login/LoginView.vue
<template>
<div class="login_container">
<div class="login_box">
<div class="logo_box">
<img src="@/assets/logo.svg" alt="logo" />
</div>
</div>
</div>
</template>
<style lang="less" scoped>
.login_container {
background-color: #2b4b6b;
height: 100%;
}
.login_box {
width: 430px;
height: 300px;
background-color: white;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 3px;
.logo_box {
height: 100px;
width: 100px;
position: absolute;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
border: 1px solid #ddd;
padding: 10px;
box-shadow: 0 0 10px;
background-color: #eee;
img {
height: 100%;
width: 100%;
border-radius: inherit;
background-color: #eee;
}
}
}
</style>
这里基本上都是跟着视频走,用的less,还没有看到和普通css有啥区别,另外,好像element ui plus用的是sass。后续有时间了再看看。
上述代码渲染出来的效果是这样的:
就是一个图标,盖在一个白色矩形上。这个白色矩形,就是后续的输入窗体了。
二、登录窗体
输入窗体直接用el-form,这里提一下和视频不一样的地方,就是图标的自动导入。在前面已经配置好了自动导入,所以不用像视频那样手动引入,直接在代码里写就可以。
在components.d.ts中,会自动生成如下代码:
其它的基本上就是根据ElementUI Plus的官方文档来处理了。包括输入框、校验、事件处理,官方文档上都有说明,基本就是CV大法就可以了。
src/views/login/components/LoginForm.vue
<template>
<el-form ref='loginFormRef' :model='loginForm' :rules='rules' class='login_form' label-width="0">
<el-form-item prop="username">
<el-input v-model="loginForm.username" class="w-50 m-2" placeholder="账号" clearable>
<template #prefix>
<ElIcon>
<ElIconUser></ElIconUser>
</ElIcon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginForm.password" class="w-50 m-2" type="password" placeholder="密码" show-password>
<template #prefix>
<ElIcon>
<ElIconLock></ElIconLock>
</ElIcon>
</template>
</el-input>
</el-form-item>
<el-form-item>
<div class="login_buttons">
<el-button type="primary" class="justify-end" @click="submitForm(loginFormRef)">登录</el-button>
<el-button class="justify-end" @click="resetForm(loginFormRef)">重置</el-button>
</div>
</el-form-item>
</el-form>
</template>
三、API 调用修改
需要特别说明的就是登录API调用了。昨天已经做了一个简单处理,但是实际上还是需要再修改一下。
修改主要提现在两点,错误处理和响应数据统一处理。
错误处理又分为两个地方,axios的响应拦截器和login方法里面。上代码:
src\api\index.ts
http.interceptors.response.use(
(response: AxiosResponse) => {
const { data } = response
return data
},
async (error: AxiosError) => {
if (error.code == AxiosError.ERR_BAD_RESPONSE) {
error.message = '服务器错误!请您稍后重试'
}
if (error.code == AxiosError.ECONNABORTED) {
error.message = '请求超时!请您稍后重试'
}
if (error.code == AxiosError.ERR_NETWORK) {
error.message = '网络错误!请您稍后重试'
}
const result = {
code: error.code,
message: error.message,
success: false
}
return result
}
)
在响应拦截器中,做了两个处理,第一个就是正常返回时,把data直接返回;第二个就是错误时,针对特定code,添加了中文消息,然后将错误封装成统一响应数据。
然后在调用的地方,也做了相应的修改
src\api\login.ts
主要是针对异常做了处理,具体errorToResult方法如下:
export const errorToResult = (error: unknown) => {
const axError = error as AxiosError
return {
code: axError ? (axError.code ? axError.code : '-1') : '-1',
success: false,
message: axError ? (axError.message ? axError.message : '') : error + '',
data: null
}
}
那为什么要这么做?
首先在调用axios时,会有各种各样的可能出错,那么login方法就需要去try-catch,所以这里的异常处理是必不可少的。
另外,后端返回的结果,会有定义code,根据code不同,会有不同的处理方式,而这个又是和业务紧密相关的,并不是一个统一弹窗就能完全覆盖的。当然,一些基础的错误用统一弹窗也就够了。
总体思路就是在login这里,把所有的异常处理掉,统一成一致的响应数据,然后在实际使用的地方,根据响应数据再进行后续处理。
最后,在LoginForm中,实际调用的代码是这个样子的:
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate(async (valid, fields) => {
if (valid) {
await doLogin()
}
})
}
const currentUser = useCurrentUserStore()
const router = useRouter()
const doLogin = async () => {
const result = await login(loginForm)
handleResultDTO(result,
(r: ResultDTO<Login.LoginResponse>) => {
const { data } = r
currentUser.setToken(data!.token)
currentUser.setUserInfo({ name: data!.nickname })
router.push('/home/index')
}
)
// if (result.success && result.data) {
// const loginResponse = result.data
// currentUser.setToken(loginResponse.token)
// currentUser.setUserInfo({ name: loginResponse.nickname })
// router.push('/home/index')
// } else {
// ElMessage.error(result.message)
// }
}
在 doLogin中,注释掉的内容后面被封装到 handleResultDTO方法里:
export const handleResultBase = (result: ResultBase, action: (r: ResultBase) => any): any => {
if (result && result.success) {
return action(result)
} else {
ElMessage.error(result.message)
}
}
export const handleResultDTO = <T>(result: ResultDTO<T>, action: (r: ResultDTO<T>) => any): any => {
if (result && result.success && result.data) {
return action(result)
} else {
ElMessage.error(result.message)
}
}
这个只是两种不同的写法,本质上都是依据响应数据是否返回了正确了结果来处理,提取成方法只是一个习惯。
总结
以上就是今天的内容,本文记录了一下vue登录页面的具体搭建过程,另外对API调用进行了重构,主要是涉及到异常处理部分。