一.技术架构
- vscode+vite+vue3+js
- pinia做状态管理
- route4做路由管理
- axios网络封装
二.业务描述
本套页面是一个简单的课程管理页面,包括注册登录,课程的查询、修改、删除、增加功能
1.用户界面
注册
登录
3.课程管理
查询
增加
删除
修改
三.项目介绍
1.新建项目
npm create vite
设置项目名称为MySchdeule
2.配置依赖
1.npm install vue-router --save 下载路由
2.npm install pinia --save 使用pina
3.npm install axios --save 使用网络框架
3.路由工具封装
//配置一个myrouter.js文件,用来管理路由,路由的跳转等功能。
import { createRouter, createWebHashHistory } from 'vue-router'
// import Login from '../pages/Login.vue'
import Register from '../pages/Register.vue'
import ScheduleList from '../pages/ScheduleList.vue'
//路由配置 需要补充具体的路由
const routes = [
{
path: '/',
redirect: '/login'
},
{
path: '/register',
name: 'Register',
component: Register
},
{
path: '/login',
name: 'Login',
// component: Login 这样写会报错 因为Login是一个异步组件,所以需要使用() => import('../pages/Login.vue')的方式导入组件
component: () => import('../pages/Login.vue')
},
{
path: '/scheduleList',
name: 'ScheduleList',
component: ScheduleList
}
]
const myRouter = createRouter({
history: createWebHashHistory(),
routes
})
//添加路由守卫
myRouter.beforeEach((to, from, next) => {
console.log("from",from.path)
console.log("to",to.path)
next()
})
export default myRouter
4.网络工具封装
// 网络请求工具,并且配置请求和响应拦截器,请求头,超时时间等。
import axios from 'axios'
const myRequest = axios.create({
baseURL: 'http://localhost:8080/',
timeout: 10000,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
// 请求拦截器
myRequest.interceptors.request.use(config => {
// 在发送请求之前做些什么
return config
}, error => {
// 对请求错误做些什么
console.log("请求错误", error)
return Promise.reject(error)
})
// 响应拦截器
myRequest.interceptors.response.use(response => {
// 对响应数据做点什么
console.log("响应数据", response)
return response
}, error => {
console.log("响应错误", error)
// 对响应错误做点什么
return Promise.reject(error)
})
export default myRequest
// 这样就可以使用axios来进行网络请求了
3.pinia封装
//配置一个pinia实例
import { createPinia } from 'pinia'
const MyPinia = createPinia()
export default MyPinia
//user的状态管理
import { defineStore } from 'pinia'
import myRouter from './myrouter.js'
export const userStore = defineStore('user', {
state: () => ({
name: '',
uid: -1,
}),
getters: {
getName() {
if (this.name === '') {
return sessionStorage.getItem('name') //存储到session中关闭浏览器后或新开后无效
}
return this.name
},
getUid() {
if (this.uid === -1) {
return sessionStorage.getItem('uid')
}
return this.uid
},
},
actions: {
setName(name) {
this.name = name
},
setUid(uid) {
this.uid = uid
},
loginSuccess(name, uid) {
console.log("login", `$name: ${name}, $uid: ${uid}`)
this.name = name
this.uid = uid
sessionStorage.setItem('name', name)
sessionStorage.setItem('uid', uid)
// myRouter.push('/scheduleList')
},
logout() {
this.$reset() //清空pinia
sessionStorage.removeItem('name')
sessionStorage.removeItem('uid')
}
}
})
5.用户界面编写
5.1 公共头部组件
5.1.1 未登录
5.1.2 已登录
5.1.3 业务逻辑代码
<template>
<div class="header">
<h1 style="color: #42b983;">欢迎使用学习计划管理系统</h1>
<div class="header-detail">
<div v-if="isNotLogin && isLoginOrRegisterShow">
<!-- <button>登录</button> -->
<!-- <button>注册</button> -->
<router-link class="router-link-style" to="/login">登录</router-link>
<router-link class="router-link-style" to="/register">注册</router-link>
</div>
<div v-else>
<span>欢迎您,{{ user.getName }}</span>
<router-link class="router-link-style" to="/login" @click="logout">退出</router-link>
</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue';
import { userStore } from '../utils/userstore';
import myRouter from '../utils/myrouter';
let user = userStore();
//判断pinia状态或内存状态来显示登录或注册按钮
const isNotLogin = computed(() => {
const name = user.getName;
console.log("computed name:", name);
if (name == '' || name == null) {
return true
}
return false;
})
//根据当前页面路径 判断是否要显示登录或注册按钮
const isLoginOrRegisterShow = computed(() => {
console.log("computed path--->>>>:", myRouter.currentRoute.value.path);
return myRouter.currentRoute.value.path == '/login' || myRouter.currentRoute.value.path == '/register'
});
const logout = () => {
user.logout();
}
</script>
<style scoped>
/* 设置上下排列 */
.header {
display: flex;
flex-direction: column;
/* 垂直轴居中 */
align-items: center;
height: 130px;
width: 100%;
background-color: #fff;
padding: 0 20px;
}
.header-detail {
width: 100%;
display: flex;
margin-right: 100px;
/* 主轴靠右 */
justify-content: end;
/* 左右排列 */
flex-direction: row;
}
.header-detail button {
margin-right: 5px;
padding: 10px 10px;
/* 帮我设置下边框 选中时边框颜色 */
border: 1px solid #42b983;
border-radius: 4px;
background-color: #42b983;
color: #fff;
cursor: pointer;
}
.router-link-style {
margin-left: 20px;
margin-right: 20px;
padding: 10px 20px;
/* 帮我设置下边框 选中时边框颜色 */
border: 1px solid #42b983;
border-radius: 4px;
background-color: #42b983;
color: #fff;
cursor: pointer;
}
/* .header-detail button:hover {
background-color: #87de94;
color: #42b983;
border: 1px solid #42b983;
} */
</style>
5.2登录界面
5.2.1 页面展示
5.2.2 业务逻辑代码
<template>
<div class="login">
<h1>用户登录</h1>
<input class="input-style" type="text" placeholder="用户名" v-model="username">
<input class="input-style" type="password" placeholder="密码" v-model="password">
<button class="btn-style" @click="login">登录</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { userStore } from '../utils/userstore'
import myRouter from '../utils/myrouter'
import myRequest from '../utils/myrequest'
const user = userStore()
const username = ref('')
const password = ref('')
const login = () => {
// try {
// console.log("login", username.value)
// myRouter.push('/scheduleList')
// } catch (err) {
// console.log(err)
// }
//下面检查下用户名和密码是否正确
//如果正确,则登录成功,跳转到列表页面
//如果错误,则提示错误信息
//这里只是简单模拟一下,实际项目中,应该调用后端接口进行验证
if (username.value === '' && password.value === '') {
alert('用户名或密码不能为空')
return
}
myRequest.post('user/login', {
username: username.value,
password: password.value
}).then(res => {
if (res.status === 200) {
console.log("code", res.data.code)
if (res.data.code === 200) {
// alert('登录成功')
console.log("user", user.name)
const {username,uid} =res.data.data
user.loginSuccess(username,uid)
myRouter.push('/scheduleList')
} else {
alert(res.data.message)
}
} else {
alert('用户名或密码错误')
}
}).catch(err => {
console.log(err)
})
}
</script>
<style scoped>
h1 {
color: #42b983;
}
.login {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.input-style {
border: 1px solid #42b983;
margin: 5px;
border-radius: 5px;
width: 200px;
height: 40px;
}
.btn-style {
background-color: #42b983;
color: white;
border: none;
border-radius: 5px;
width: 200px;
height: 40px;
margin-top: 10px;
}
</style>
5.3 注册界面
5.3.1 页面展示
5.3.2 业务逻辑代码
<template>
<div class="register">
<h1>用户注册</h1>
<div>
<span class="span-centered">用户名</span>
<input class="input-style" type="text" placeholder="用户名" v-model="username">
</div>
<div>
<span class="span-centered">密码</span>
<input class="input-style" type="password" placeholder="密码" v-model="password">
</div>
<div>
<span class="span-centered">确认密码</span>
<input class="input-style" type="password" placeholder="确认密码" v-model="confirmPassword">
</div>
<div class="btn-container-style">
<button class="btn-style" @click="register">注册</button>
<button class="btn-style" @click="login">去登录</button>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import myRequest from '../utils/myrequest'
import myRouter from '../utils/myrouter'
const username = ref('')
const password = ref('')
const confirmPassword = ref('')
const register = () => {
if (username.value === '') {
alert('用户名不能为空')
return
}
if (password.value === '') {
alert('密码不能为空')
return
}
if (password.value !== confirmPassword.value) {
alert('两次输入的密码不一致')
return
}
// 注册逻辑
myRequest.post('user/register', {
username: username.value,
password: password.value
}).then(res => {
console.log(res)
if (res.data.code === 200) {
alert('注册成功')
} else {
alert(`注册失败 ${res.data.message}`)
}
}).catch(err => {
console.log(err)
alert(`注册失败${err.message}`)
})
}
const login = () => {
// 跳转登录页面
myRouter.push('/login')
}
</script>
<style scoped>
h1 {
color: #42b983;
}
.span-centered {
font-size: 16px;
display: inline-block;
/* 或者使用 display: block; 这样就可以给span设置宽度了 */
width: 80px;
/* 设置您想要的宽度 */
height: 40px;
/* 设置您想要的高度 */
text-align: right;
/* 水平居中文本 */
line-height: 40px;
/* 垂直居中文本,值应与高度相同 */
}
.register {
width: 100%;
height: 100%;
display: flex;
/*垂直 */
flex-direction: column;
justify-content: center;
align-items: center;
}
.input-style {
border: 1px solid #42b983;
margin: 5px;
border-radius: 5px;
width: 200px;
height: 40px;
}
.btn-container-style {
display: flex;
justify-content: right;
flex-direction: row;
align-items: right;
width: 260px;
}
.btn-style {
background-color: #42b983;
color: white;
border: none;
margin-right: 10px;
width: 80px;
border-radius: 5px;
height: 40px;
margin-top: 10px;
}
</style>
6.课程管理界面编写
6.1 页面展示
6.2 业务逻辑代码
<template>
<div>
<table class="tableStyle">
<thead>
<tr>
<th>内容</th>
<th>进度</th>
<th>操作</th>
</tr>
</thead>
<!-- 设置行数据 -->
<tbody>
<tr v-for="(item, index) in scheduleList" :key="index">
<td>
{{ index + 1 }}.
<input type="text" v-model="item.content">
</td>
<td>
<input type="radio" value="1" v-model="item.state"> 已完成
<input type="radio" value="0" v-model="item.state"> 未完成
</td>
<td>
<button @click="handleEdit(item)">修改</button>
<button @click="handleDelete(item)">删除</button>
</td>
</tr>
<tr >
<td colspan="4">
<button class="btn1" @click="addItem()">新增日程</button>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import myRequest from '../utils/myrequest';
import { userStore } from '../utils/userstore';
let user = userStore();
let scheduleList = ref([]);
// 编辑任务
async function handleEdit(item) {
console.log('编辑任务:', item);
let result = await myRequest.post('/schedule/updateSchedule', {
uid: user.getUid,
sid: item.sid,
content: item.content,
state: item.state
}).then(res => {
if (res.data.code == 200) {
console.log('编辑任务成功:', res);
alert('编辑任务成功');
getScheduleList();
} else {
alert(`编辑任务失败${res.data.message}`);
console.log('编辑任务失败:', res);
}
return "success"
}).catch(err => {
console.log('编辑任务失败:', err);
});
console.log('handleEdit:', result);
}
// 删除任务
async function handleDelete(item) {
console.log('删除任务:', item);
await myRequest.request({
method: 'post',
url: '/schedule/deleteSchedule',
params: {
sid: item.sid
}
}).then(res => {
if (res.data.code == 200) {
console.log('删除任务成功:', res);
alert('删除任务成功');
getScheduleList();
} else {
alert(`删除任务失败${res.data.message}`);
console.log('删除任务失败:', res);
}
return "success"
}).catch(err => {
console.log('删除任务失败:', err);
});
}
const getScheduleList = async() => {
await myRequest.request({
url: 'schedule/querySchedule',
method: 'post',
params: {
uid: user.getUid
}
}).then(res => {
console.log('请求数据成功--->>:', res.data.data.scheduleList);
scheduleList.value = res.data.data.scheduleList;
console.log('scheduleList', scheduleList);
}).catch(err => {
console.log('请求数据失败:', err);
});
}
// 新增任务
async function addItem() {
console.log('新增任务');
let newItem = {
content: '',
state: 0,
sid: -1
};
await myRequest.request({
method: 'post',
url: '/schedule/addSchedule',
data: {
uid: user.getUid,
content: newItem.content,
state: newItem.state
}
}).then(res => {
if (res.data.code == 200) {
console.log('新增任务成功:', res);
alert('新增任务成功');
getScheduleList();
} else {
alert(`新增任务失败${res.data.message}`);
console.log('新增任务失败:', res);
}
return "success"
}).catch(err => {
console.log('新增任务失败:', err);
});
}
// 页面加载完成后,请求数据
onMounted(() => {
console.log('页面加载完成');
// 页面加载完成后,请求数据
getScheduleList();
});
</script>
<style scoped>
.tableStyle {
margin: 30px auto;
border-collapse: collapse;
width: 60%;
text-align: center;
}
/* 设置表格单元格 */
.tableStyle td {
text-align: center;
border: 1px solid #ddd;
padding: 8px;
width: 33%;
}
.tableStyle input {
/* width: 80%; */
padding: 5px;
border: 1px solid #ddd;
border-radius: 5px;
margin-bottom: 10px;
}
/* 设置表头单元格 */
.tableStyle th {
border: 1px solid #ddd;
background-color: #f2f2f2;
color: #333;
}
/* 设置按钮样式 */
.tableStyle button {
margin-left: 10px;
padding: 5px 10px;
border: none;
border-radius: 5px;
background-color: #4CAF50;
color: white;
cursor: pointer;
}
</style>
四. 项目总结
课程管理系统前端页面是一个简单的入门项目,主要是为了熟悉vue3的路由、网络、状态管理以及选项式编程习惯,同时使用了html/css简单的网页布局。