1. 在src/views目录下新建Login目录
2. 在Login目录新建index.vue
// @/views/Login/index.vue
<template>
<h1>login</h1>
</template>
<script lang="ts">
import {defineComponent} from 'vue'
export default defineComponent({
name: 'login',
})
</script>
<style lang="less" scoped>
</style>
3. 修改路由
// @/router/index.ts
import { createRouter,createWebHashHistory} from "vue-router";
const routes = [
{
path: "/",
name: "home",
component: () => import('../views/Home/index.vue'),
meta:{
title:'首页',
}
},
{
path: "/login",
name: "Login",
component: () => import('../views/Login/index.vue'),
meta:{
title:'登录',
}
},
]
const router = createRouter({
history: createWebHashHistory(),
routes: routes
})
export default router;
4. 修改App.vue
//@/App.vue
<template>
<router-view />
</template>
<script lang="ts">
import {defineComponent} from 'vue'
export default defineComponent({
name: 'App',
components: {},
})
</script>
<style>
</style>
5. 修改home页
// @/Home/index.vue
<template>
<h1>home</h1>
</template>
<script lang="ts">
import {defineComponent} from 'vue'
export default defineComponent({
name: 'home',
})
</script>
<style lang="less" scoped>
</style>
6. 切换浏览器地址
- http://localhost:3000/#/
- http://localhost:3000/#/login
- 可以切换两个页面表示正常
- 可以继续下一步
7. 页面布局
// @/views/Login/index.vue
<template>
<a-row class="layout" type="flex" justify="center" align="middle">
<a-card class="login_card" bodyStyle="height:100%;padding:unset;" hoverable>
<div class="card_body">
<div class="login_img">
<img src="https://via.placeholder.com/320x180/fff.png?text=Tian-Admin">
<p>Tian-Admin</p>
</div>
<div class="login_form">
<a-form class="form" layout="vertical">
<a-form-item label="账号:">
<a-input size="large"/>
</a-form-item>
<a-form-item label="密码:">
<a-input size="large"/>
</a-form-item>
<a-form-item>
<a-button type="primary" block>提交</a-button>
</a-form-item>
</a-form>
</div>
</div>
</a-card>
</a-row>
</template>
<script lang="ts">
import {
defineComponent
} from 'vue'
export default defineComponent({
name: 'login',
})
</script>
<style lang="less" scoped>
.layout {
width: 100%;
height: 100%;
background: #f5f5fa;
.login_card {
width: 100%;
height: 100%;
max-width: 1000px;
max-height: 550px;
border-radius: 10px;
background: #fff;
}
.card_body{
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
}
.login_img {
background: #1890ff;
width: 40%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
img {
padding: 10px;
background: #fff;
max-width: 160px;
}
p {
margin: 20px 0;
font-size: 22px;
color: #fff;
}
}
.login_form {
width: 60%;
display: flex;
justify-content: center;
align-items: center;
.form {
width: 100%;
max-width: 380px;
}
}
}
</style>
8. 加入表单验证
// @/views/Login/index.vue
<template>
<a-row class="layout" type="flex" justify="center" align="middle">
<a-card class="login_card" bodyStyle="height:100%;padding:unset;" hoverable>
<div class="card_body">
<div class="login_img">
<img src="https://via.placeholder.com/320x180/fff.png?text=Tian-Admin">
<p>Tian-Admin</p>
</div>
<div class="login_form">
<a-form class="form" ref="loginFormRef" layout="vertical" :rules="loginRules" :model="loginForm">
<a-form-item label="账号:" name="username">
<a-input v-model:value="loginForm.username" size="large"/>
</a-form-item>
<a-form-item label="密码:" name="password">
<a-input v-model:value="loginForm.password" size="large"/>
</a-form-item>
<a-form-item>
<a-button type="primary" size="large" block @click="onSubmit">提交</a-button>
</a-form-item>
</a-form>
</div>
</div>
</a-card>
</a-row>
</template>
<script lang="ts">
import {defineComponent,reactive,ref,toRaw} from 'vue'
//表单的类型
interface formState {
username:string,
password:string,
}
export default defineComponent({
name: 'login',
setup(){
//表单的ref属性
const loginFormRef = ref();
//登录表单
const loginForm: formState = reactive({
username: '',
password: '',
});
//登录验证规则
const loginRules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
],
password: [
{ required: true, message: '请输入密码', trigger: 'blur' },
],
};
//提交表单
const onSubmit = ()=>{
loginFormRef.value.validate().then(()=>{
console.log(toRaw(loginForm));
})
};
return{
loginFormRef,
loginForm,
loginRules,
onSubmit
}
}
})
</script>
<style lang="less" scoped>
.layout {
width: 100%;
height: 100%;
background: #f5f5fa;
.login_card {
width: 100%;
height: 100%;
max-width: 1000px;
max-height: 550px;
border-radius: 10px;
background: #fff;
}
.card_body{
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
}
.login_img {
background: #1890ff;
width: 40%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
img {
padding: 10px;
background: #fff;
max-width: 160px;
}
p {
margin: 20px 0;
font-size: 22px;
color: #fff;
}
}
.login_form {
width: 60%;
display: flex;
justify-content: center;
align-items: center;
.form {
width: 100%;
max-width: 380px;
}
}
}
</style>
9. 项目中引入mock用于接口模拟
10. mock安装执行命令
npm install mockjs -d
npm install vite-plugin-mock -d
npm install cross-env -d
11. 修改package.json
// @/package.json
{
"scripts": {
// 修改dev构建脚本的命令
"dev": "cross-env NODE_ENV=development vite",
"build": "vue-tsc --noEmit && vite build",
"serve": "vite preview"
},
}
12. 修改vite.config.ts,同时设置一下src目录别名,方便文件的引入。
// @/vite.config.ts
import vue from "@vitejs/plugin-vue";
import path from "path";
import {defineConfig} from "vite";
import {viteMockServe} from "vite-plugin-mock";
export default defineConfig({
plugins: [
vue(),
viteMockServe({
supportTs: true
})
],
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
},
},
});
13. 在项目根目录新建mock目录(与src同级)
14. 在mock下新建login.ts文件
// /mock/login.ts
import Mock from 'mockjs'
export default [
{
//http://mockjs.com/examples.html
url: "/mock/api/login",
method: "post",
// timeout: 500,
// statusCode: 500,
response: ({}) => {
return {
code: 200,
message: "ok",
data: {
'token': Mock.Random.string('lower',200),
},
}
},
},
];
15. 请求登录接口,执行成功后跳转
// @/views/Login/index.vue
import {defineComponent,ref,reactive,toRaw} from 'vue'
import {useRouter} from 'vue-router'
import axiso from "axios"
......
const router = useRouter();
const loginFormRef = ref();
......
const onSubmit = () => {
loginFormRef.value.validate().then(() => {
axios({
url: '/mock/api/login',
method: 'POST',
data: toRaw(loginForm),
}).then(res => {
console.log(res.data);
router.push({
path:'/'
})
})
})
};
......
16.完整代码
<template>
<a-row class="layout" type="flex" justify="center" align="middle">
<a-card class="login_card" bodyStyle="height:100%;padding:unset;" hoverable>
<div class="card_body">
<div class="login_img">
<img src="https://via.placeholder.com/320x180/fff.png?text=Tian-Admin" />
<p>Tian-Admin</p>
</div>
<div class="login_form">
<a-form class="form" ref="loginFormRef" layout="vertical" :rules="loginRules" :model="loginForm">
<a-form-item label="账号:" name="username">
<a-input v-model:value="loginForm.username" size="large" />
</a-form-item>
<a-form-item label="密码:" name="password">
<a-input-password v-model:value="loginForm.password" size="large" />
</a-form-item>
<a-form-item>
<a-button type="primary" size="large" block @click="onSubmit">提交</a-button>
</a-form-item>
</a-form>
</div>
</div>
</a-card>
</a-row>
</template>
<script lang="ts">
import { defineComponent, reactive, ref, toRaw } from 'vue';
import { useRouter } from 'vue-router';
import axios from 'axios';
interface formState {
username: string;
password: string;
}
export default defineComponent({
name: 'login',
setup() {
const router = useRouter();
const loginFormRef = ref();
const loginForm: formState = reactive({
username: '',
password: '',
});
const loginRules = {
username: [
{
required: true,
message: '请输入用户名',
trigger: 'blur',
},
],
password: [
{
required: true,
message: '请输入密码',
trigger: 'blur',
},
],
};
const onSubmit = () => {
loginFormRef.value.validate().then(() => {
axios({
url: '/mock/api/login',
method: 'POST',
data: toRaw(loginForm),
}).then((res) => {
console.log(res.data);
router.push({ path: '/' });
});
});
};
return {
loginFormRef,
loginForm,
loginRules,
onSubmit,
};
},
});
</script>
<style lang="less" scoped>
.layout {
width: 100%;
height: 100%;
background: #f5f5fa;
.login_card {
width: 100%;
height: 100%;
max-width: 1000px;
max-height: 550px;
border-radius: 10px;
background: #fff;
}
.card_body {
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
}
.login_img {
background: #1890ff;
width: 40%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
img {
padding: 10px;
background: #fff;
max-width: 160px;
}
p {
margin: 20px 0;
font-size: 22px;
color: #fff;
}
}
.login_form {
width: 60%;
display: flex;
justify-content: center;
align-items: center;
.form {
width: 100%;
max-width: 380px;
}
}
}
</style>
17.视频演示及源码
本文演示视频:点击浏览
更多前端内容欢迎关注公众号:天小天个人网