创建Vue的实例流程

一、环境准备

1.1 创建vue工程

1
npm init vue@latest

1.2 安装插件

1.安装element-plus

1
2
3
4
5
1.1 执行命令: npm install element-plus --save
1.2 在main.js中做如下配置
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
app.use(ElementPlus)

2.安装axios

1
npm install axios

1.3 目录调整

  1. 删除components目录下的内容

  2. 删除App.vue中的内容,只保留script和template标签

2. 页面搭建

2.1 文件介绍

index.html是最初始的页面,可以在这里定义页面的title这类的页面基础标签,其中映入了一个叫App.vue的文件,所有的vue项目都是建立在这里html渲染的,如果在创建的vue项目的所有页面都有一个白框,只需要将index.html的margin设置为0px,因为自带的element属性中会有一个默认的margin=8px

1731487360021.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="">

<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vite App</title>
</head>

<body style="margin: 0px;">
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>

</html>

main.js文件和上述的html一样,是对所有vue文件的一个全局属性,当我们需要对所有的vue项目导入同一个属性的时候,就可以到这里进行统一处理,例如将Element的js和css导入几句可以在这里进行注入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import router from '@/router'
import App from './App.vue'
import {createPinia} from 'pinia'
import { createPersistedState } from 'pinia-persistedstate-plugin'
import locale from 'element-plus/dist/locale/zh-cn.js'

const app = createApp(App);
const pinia = createPinia();
const persist = createPersistedState();
pinia.use(persist)
app.use(pinia)
app.use(router)
app.use(ElementPlus,{locale});
app.mount('#app')

App.vue是所有vue项目展示的位置,为了将页面渲染到当前页面,我们就需要在这里设置将我们需要的页面渲染在这里:

1
2
3
4
5
6
7
8
9
10
11
12
<script setup>

</script>

<template>
<router-view></router-view>
</template>

<style scoped>

</style>

2.2 路由

在App.vue中,不能同时展示Login.vue和Layout.vue,实际的需求是用户第一次访问程序,先展示登录页面,当用户登录成功后,再展示主页面,如果要达成这个需求,需要用到vue提供的路由相关的知识

路由,从起点到终点时,决定从起点到终点的路径的进程,在前端工程中,路由指的是根据不同的访问路径,展示不同组件的内容。Vue Router是Vue.js的官方路由,它与Vue.js深度集成,让Vue.js构建单页面应用变得更加轻而易举

2.2.1 安装路由

1
npm install vue-router@4j

2.2.2 创建路由

在src/router目录下,定义一个js文件,起名为index.js。这样名字的js文件在导入时,可以不写文件名,只要定位到文件所在的文件夹即可,使用起来很方便

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 导入 vue-router 库中的函数
import { createRouter, createWebHistory } from 'vue-router';
// 导入需要的组件
import LoginVue from '@/views/Login.vue'; // 登录页面组件
import LayoutVue from '@/views/Layout.vue'; // 布局页面组件
import BookCategory from '@/views/book/BookCategory.vue'; // 书籍分类页面组件
import BookManage from '@/views/book/BookManger.vue'; // 书籍管理页面组件
import UserManage from '@/views/user/UserManger.vue'; // 用户管理页面组件
import UserAdd from '@/views/user/UserAdd.vue'; // 添加用户页面组件
import UserEdit from '@/views/user/UserEdit.vue'; // 编辑用户页面组件

// 定义路由关系
const routes = [
{
path: '/login', // 登录页面的路径
component: LoginVue // 登录页面对应的组件
},
{
path: '/', // 根路径
component: LayoutVue, // 布局组件
redirect: '/book/category/', // 默认重定向到书籍分类页面
children: [ // 定义子路由
{
path: 'book/category', // 书籍分类路径
component: BookCategory // 书籍分类组件
},
{
path: 'book/manage', // 书籍管理路径
component: BookManage // 书籍管理组件
},
{
path: 'user/manage', // 用户管理路径
component: UserManage // 用户管理组件
},
{
path: 'user/add', // 添加用户路径
component: UserAdd // 添加用户组件
},
{
path: 'user/edit/:id', // 编辑用户路径,带参数 id
component: UserEdit, // 编辑用户组件
props: true, // 将路由参数作为 props 传递给组件
name: 'UserEdit' // 给该路由命名,方便在其他地方引用
},
]
}
];

// 创建路由器实例
const router = createRouter({
history: createWebHistory(), // 使用 HTML5 History 模式
routes: routes // 注册定义的路由
});

// 导出路由器实例
export default router;

2.2.3 在vue应用实例中使用router

在2.1中介绍的main.js中加入这些:

1
2
3
import router from '@/router'

app.use(router)

2.2.4 使用路由

在上面定义好路由后,继续在我们想要使用页面转跳的时候,使用如下,在中引入路由:

1
2
import { useRouter } from 'vue-router'
const router = useRouter();

在需要转跳的地方如下编写,相当与会转跳到‘‘/‘’这个路径之下:

1
router.push('/')

2.2.5 子路由

在咱们的主页面中,当用户点击左侧的菜单时,右侧主区域的内容需要发生变化,将来每切换一个菜单,右侧需要加载对应组件的内容进行展示,像这样的场景咱们也需要使用路由来完成

由于这些组件都需要在Layout.vue中展示, 而Layout.vue本身已经参与了路由,因此我们需要在Layout.vue中通过子路由的方式来完成组件的切换

1
children: [ // 定义子路由]

2.3 注册页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
<template>
<div class="container">
<div class="wrapper">
<form @submit.prevent="handleLogin">
<h1>登录</h1>
<div class="input-box">
<input v-model="LoginData.adminName" type="text" required />
<label>用户名</label>
</div>
<div class="input-box">
<input v-model="LoginData.adminPass" type="password" required />
<label>密码</label>
</div>

<div class="row">
<a @click="forgotPassword">忘记密码?</a>
</div>

<button type="submit" class="btn">登录</button>
<div class="signup-link">
<p>还没有账号?<a href="#" @click="showSignUpAlert">创建一个。</a></p>
</div>
</form>
</div>
</div>
</template>

<script>
import { ref } from 'vue'; // 引入 Vue 的 ref 函数用于响应式数据
import { mangerLoginService } from '@/api/manger'; // 导入登录服务 API
import { ElMessage } from 'element-plus'; // 导入 Element Plus 消息组件
import { useRouter } from 'vue-router'; // 引入 Vue Router 用于页面导航
import { useTokenStore } from '@/stores/token'; // 导入 token 存储管理

export default {
setup() {
const tokenStore = useTokenStore(); // 获取 token 存储
const router = useRouter(); // 获取路由对象
const LoginData = ref({
adminName: '', // 用户名
adminPass: '', // 密码
});

// 处理登录的函数
const handleLogin = async () => {
let result = await mangerLoginService(LoginData.value); // 调用登录 API
if (result.code == 200) { // 判断返回的状态码
console.log('登入成功,用户名是:', result.data.adminName);
// localStorage.setItem('userInfo', JSON.stringify(result.data.adminName)); // 可选:存储用户信息
ElMessage.success('登录成功!'); // 显示成功消息
console.log('登入的token值是:', tokenStore);
tokenStore.setToken(result.data); // 存储 token
router.push('/'); // 登录成功后重定向到主页
} else {
ElMessage.error('账号或密码错误!'); // 显示错误消息
}
};

// 处理忘记密码的函数
const forgotPassword = () => {
ElMessage.info('请联系管理员重置密码。'); // 提示信息
};

// 显示注册提示的函数
const showSignUpAlert = () => {
// 这里可以添加注册逻辑或提示
};

return {
LoginData, // 返回响应式数据
handleLogin, // 返回登录处理函数
forgotPassword, // 返回忘记密码处理函数
showSignUpAlert, // 返回注册提示函数
};
},
};
</script>

<style scoped>
* {
font-family: "Poppins", sans-serif; /* 设置全局字体 */
margin: 0; /* 去除默认外边距 */
padding: 0; /* 去除默认内边距 */
box-sizing: border-box; /* 包含边框和内边距在内的宽高计算 */
}

html,
body {
height: 100%; /* 使 html 和 body 填满整个视口 */
margin: 0; /* 去除默认外边距 */
}

.container {
height: 100vh; /* 使 container 填满整个视口高度 */
display: flex; /* 使用 flex 布局 */
align-items: center; /* 垂直居中 */
justify-content: center; /* 水平居中 */
background: #282a37; /* 设置背景颜色 */
}

.wrapper {
width: 400px; /* 设置 wrapper 宽度 */
height: 450px; /* 设置 wrapper 高度 */
background: #3e404d; /* 设置背景颜色 */
border-radius: 20px; /* 设置圆角 */
display: flex; /* 使用 flex 布局 */
flex-direction: column; /* 垂直排列子元素 */
align-items: center; /* 水平居中子元素 */
justify-content: center; /* 垂直居中子元素 */
backdrop-filter: blur(15px); /* 背景模糊效果 */
}

.wrapper:hover {
box-shadow: 0 0 40px rgba(255, 255, 255, 0.5); /* 鼠标悬停时的阴影效果 */
background: #46474e; /* 鼠标悬停时的背景颜色 */
}

.wrapper h1 {
font-size: 2em; /* 设置标题字体大小 */
color: #fff; /* 设置标题颜色 */
text-align: center; /* 设置标题居中 */
}

.wrapper .input-box {
position: relative; /* 相对定位用于 label 的绝对定位 */
width: 310px; /* 设置输入框宽度 */
margin: 30px 0; /* 设置上下外边距 */
border-bottom: 2px solid #fff; /* 底部边框 */
}

.wrapper .input-box input {
width: 100%; /* 输入框宽度填满父元素 */
height: 50px; /* 设置输入框高度 */
background: transparent; /* 输入框背景透明 */
outline: none; /* 去除输入框焦点样式 */
border: none; /* 去除输入框边框 */
font-size: 1em; /* 设置字体大小 */
color: #fff; /* 设置字体颜色 */
padding: 0 40px 0 5px; /* 设置内边距 */
}

.wrapper .input-box label {
position: absolute; /* 绝对定位 */
top: 50%; /* 垂直居中 */
left: 5px; /* 左边距 */
transform: translateY(-50%); /* 垂直居中调整 */
font-size: 1em; /* 设置字体大小 */
color: #fff; /* 设置字体颜色 */
pointer-events: none; /* 禁用标签的鼠标事件 */
transition: 0.5s; /* 标签移动动画 */
}

.wrapper .input-box input:focus~label,
.wrapper .input-box input:valid~label {
top: -5px; /* 聚焦或有效输入时标签上移 */
}

.wrapper .input-box .icon {
position: absolute; /* 绝对定位 */
right: 8px; /* 右边距 */
color: #fff; /* 设置颜色 */
font-size: 1.2em; /* 设置字体大小 */
line-height: 57px; /* 使图标垂直居中 */
}

.wrapper .row {
margin: -15px 0 15px; /* 设置上下外边距 */
font-size: 0.9em; /* 设置字体大小 */
color: #fff; /* 设置字体颜色 */
display: flex; /* 使用 flex 布局 */
justify-content: space-between; /* 子元素水平分散对齐 */
}

.wrapper .row a {
color: #fff; /* 设置链接颜色 */
text-decoration: none; /* 去除链接下划线 */
}

.wrapper .btn {
width: 100%; /* 按钮宽度填满父元素 */
height: 40px; /* 设置按钮高度 */
background: #fff; /* 设置按钮背景颜色 */
border: none; /* 去除按钮边框 */
border-radius: 40px; /* 设置圆角 */
cursor: pointer; /* 设置鼠标悬停时光标为手型 */
font-size: 1em; /* 设置字体大小 */
color: #000; /* 设置字体颜色 */
margin-top: 10px

3. 跨域

由于发起ajax请求的域为http://localhost:5173, 而后台服务器的域为 http://localhost:8080, 所以浏览器会限制该请求的发送, 这种问题称为跨域问题, 跨域问题可以在服务器端解决,也可以在浏览器端解决, 咱们这一块通过配置代理的方式解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//定制请求的实例

//导入axios npm install axios
import axios from 'axios';

import { ElMessage } from 'element-plus'
//定义一个变量,记录公共的前缀 , baseURL
//const baseURL = 'http://localhost:8080';
const baseURL = 'http://localhost:8080';
const instance = axios.create({ baseURL })

import {useTokenStore} from '@/stores/token.js'
//添加请求拦截器
instance.interceptors.request.use(
(config)=>{
//请求前的回调
//添加token
const tokenStore = useTokenStore();
//判断有没有token
if(tokenStore.token){
config.headers.Authorization = tokenStore.token
}
return config;
},
(err)=>{
//请求错误的回调
Promise.reject(err)
}
)

/* import {useRouter} from 'vue-router'
const router = useRouter(); */

import router from '@/router'
//添加响应拦截器
instance.interceptors.response.use(
result => {
//判断业务状态码
if(result.data.code===200){
return result.data;
}

//操作失败
//alert(result.data.msg?result.data.msg:'服务异常')
ElMessage.error(result.data.msg?result.data.msg:'服务异常')
//异步操作的状态转换为失败
return Promise.reject(result.data)

},
err => {
//判断响应状态码,如果为401,则证明未登录,提示请登录,并跳转到登录页面
if(err.response.status===401){
ElMessage.error('请先登录')
router.push('/login')
}else{
ElMessage.error('服务异常')
}

return Promise.reject(err);//异步的状态转化成失败的状态
}
)

export default instance;

4. 接口

接口是和后端进行交互的地址, 在src/api/统一定义接口路径

例如:登入接口:

1
2
3
4
5
6
7
8
9
10
11
import request from '@/utils/request';

//管理员登入
export const mangerLoginService = (loginData) => {
const params = {};
for(let key in loginData){
params[key] = loginData[key];
}
console.log('登入api参数:', params);
return request.post('/manger/login',params)
}

那么我们可以在需要转跳的vue项目中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//引入接口
import { mangerLoginService } from '@/api/manger';
//调用接口并接收返回值
const handleLogin = async () => {
let result = await mangerLoginService(LoginData.value)
if (result.code == 200) {
console.log('登入成功,用户名是:', result.data.adminName);
// localStorage.setItem('userInfo', JSON.stringify(result.data.adminName)); // 存储用户信息
ElMessage.success('登录成功!')
console.log('登入的token值是:', tokenStore);
tokenStore.setToken(result.data);
router.push('/')
} else {
ElMessage.error('账号或密码错误!')
}
}

等待学习:

上传图片

见最新

图片回显

见最新

文件上传