SpringBoo+Vue简单开发“任务管理系统”学生版(GTD+OKR简易测试版)

目录

项目介绍

项目环境

基本环境

编辑器

前端

后端

项目界面预览

在 Web 界面

在 Adobe XD

数据库设计

共 4 张数据表

任务状态表

总任务表

任务回收表

用户表​编辑

智能数据生成

成功的界面

失败的界面

失败的原因

问题分析

解决方法

前端设计

目录结构

vue 3

app.vue

Home.vue

Vue-Router

Axios

细节节点

Elemten-Plus

后端设计

Springboot 2

实体类

配置类

控制层

服务层

Dao层(Mapper)

工具

时间类

启动类

MyBatis-plus

JWT

邮件服务

等技术......

项目部署

本地部署


项目介绍

这个项目是我在目前大一暑假,利用 Springboot+vue 简单编写的,目的就是个人对 GTD+OKR 的理解后,更好的去管理自己的任务计划,合理的安排时间,系统化的学习更多东西,同时可以做到定期复盘,邮件任务提醒,于是就产生了这个念头,利用几天去开发了出来(测试版).

这里有项目介绍看

大一暑假几天用SpringBoo+Vue简单开发任务管理系统(GTD+OKR简易测试版)_哔哩哔哩_bilibili

项目环境

基本环境

  • NodoJs 16以上
  • Maven
  • JDK 8/11/17/21/22

编辑器

  • VS Code
  • IntelliJ IDEA
  • 浏览器 开发者工具(F12)

前端

  •  Vue 3
  • Axios
  • Element - Plus
  • 等等

后端

  • SpringBoot 2
  • MyBatis-Plus
  • JWT
  • Java-Mail 2.7版
  • 等等

项目界面预览

在 Web 界面

在 Adobe XD

数据库设计

(我是随个人需求去设计的 并未遵循ER关系图,三范式等)

共 4 张数据表

任务状态表

总任务表

任务回收表

用户表

智能数据生成

由 Tasks 表为例

成功的界面

失败的界面
失败的原因
问题分析

      因为数据已经存在

解决方法

        需要全部删掉后在进行生成)

前端设计

目录结构

vue 3

app.vue
<script>
</script>

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

<style scoped>
</style>

Home.vue
<template>
    <!-- 个人信息 -->
    <div v-if="userContent_display" class="userRegisteroks ">
        <h2 class="f-s-30 f-s-b s-c-blue-80 p-20  t-a-c">个人信息</h2>
        <h2 class="f-s-20 f-s-b s-c-blue-80 p-10-0-10-60">编号:{{ this.userContent_Gerenxixi.userPid }}</h2>
        <h2 class="f-s-20 f-s-b s-c-blue-80 p-10-0-10-60">身份:{{ this.userContent_Gerenxixi.userRole }}</h2>
        <h2 class="f-s-20 f-s-b s-c-blue-80 p-10-0-10-60">姓名:{{ this.userContent_Gerenxixi.userName }}</h2>
        <h2 class="f-s-20 f-s-b s-c-blue-80 p-10-0-10-60">邮箱:{{ this.userContent_Gerenxixi.userMail }}</h2>
        <h2 class="f-s-20 f-s-b s-c-blue-80 p-10-0-10-60">注册时间:{{ this.userContent_Gerenxixi.userRegisterTime }}</h2>
        <h2 class="f-s-20 f-s-b s-c-blue-80 p-10-0-10-60">最近登录:{{ this.userContent_Gerenxixi.userNextTime }}</h2>
        <h2 class="f-s-20 f-s-b s-c-blue-80 p-10-0-10-60"></h2>
        <div class="Task_Remove_Box">
            <button type="button" @click="User_NO()" class="bg-c-blue-80 mar-0-auto">确定</button>
        </div>
    </div>

    <!-- 说明bug -->
    <div v-if="homeContent_display" class="userRegisterok ">
        <p class="f-s-1 f-s-b s-c-blue-20 p-20">Version 1.0.1 Bate</p>
        <h2 class="f-s-30 f-s-b s-c-blue-80 ">欢迎来到任务系统管理</h2>
        <p class="f-s-18 f-s-b s-c-blue-80 p-30">由UP对 GTD+ORK 的理解后融合而成</p>
        <div class="Task_Remove_Box">
            <button type="button" @click="Go()" class="bg-c-blue-80">确定</button>
            <button type="button" @click="NOs()" class="bg-c-pink-20">关闭窗口</button>
        </div>
    </div>

    <!-- 自我刷新 -->
    <div v-if="userLogin_b"
        style="width: 300px; position: absolute; top:15%; left:50%; transform: translate(-50%,0 ); z-index: 9999;"
        class="am-up-float">
        <el-alert title="欢迎回来" type="success" description="专心做好做好每一件事" show-icon />
    </div>
    <!-- 待会追加一个 退出登录(删除token) -->
    <div class="common-layout">
        <el-container>
            <el-header class="box-s am-home_down">
                <el-menu class="el-menu-demo home_box " mode="horizontal" :ellipsis="false">
                    <h2 class="f-s-30 f-s-b s-c-blue-80 p-0-0-0-30">{{ systenContent.title }}</h2>
                    <div class="flex-grow" />
                    <el-menu-item index="1">
                        <router-link to="/alltasklist" class="f-s-20 f-s-b t-d-n home_box"><span
                                class="s-c-blue-60">总任务清单</span></router-link>
                    </el-menu-item>
                    <el-menu-item index="2">
                        <router-link to="/nexttask" class="f-s-20 f-s-b t-d-n home_box"><span
                                class="s-c-blue-60">下一步清单</span></router-link>
                    </el-menu-item>
                    <el-menu-item index="3">
                        <router-link to="/waittask" class="f-s-20 f-s-b t-d-n home_box"><span
                                class="s-c-blue-60">等待清单</span></router-link>
                    </el-menu-item>
                    <el-menu-item index="4">
                        <router-link to="/wastebox" class="f-s-20 f-s-b t-d-n home_box"><span
                                class="s-c-blue-60">回收箱</span></router-link>
                    </el-menu-item>
                    <el-menu-item index="5">
                        <router-link to="/daybook" class="f-s-20 f-s-b t-d-n home_box"><span
                                class="s-c-blue-60">日记</span></router-link>
                    </el-menu-item>
                    <el-sub-menu index="6">
                        <template #title><span class="f-s-20 f-s-b t-d-n home_box"><span
                                    class="s-c-blue-60">设置</span></span></template>
                        <el-menu-item index="6-1">颜色设置</el-menu-item>
                        <el-menu-item index="6-2">界面排版</el-menu-item>
                        <el-menu-item index="6-3">切换模式</el-menu-item>
                    </el-sub-menu>
                    <el-sub-menu index="7">
                        <template #title>
                            <span v-if="userContent.username != null" class="f-s-20 f-s-b s-c-blue-80 h-g bg-dark-50 p-0-10-0-0" >{{ userContent.username }}</span>
                            <span v-else class="f-s-20 f-s-b s-c-blue-80 h-g bg-dark-50 p-0-10-0-0" >确定后显示用户名</span>
                            <img style="width: 50px;" class="w-50x50 b-r-50_"
                                src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
                                alt="Element logo" />
                        </template>
                        <el-menu-item index="7-1" @click="User_YES()">个人信息</el-menu-item>
                        <el-menu-item index="7-2">导出清单</el-menu-item>
                        <el-menu-item index="7-3">
                            <router-link to="/login" class="t-d-n f-s-b all_task"
                                @click="userexit()">安全退出</router-link>
                        </el-menu-item>
                    </el-sub-menu>
                </el-menu>

            </el-header>
            <el-main>
                <router-view></router-view>
            </el-main>
        </el-container>
    </div>
</template>

<script>
import axios from 'axios';
export default {
    beforeCreate() {
        console.log("home页面加载");
    },

    data() {
        return {
            userContent_display: false,
            homeContent_display: true,
            userLogin_b: false,
            systenContent: {
                title: '任务管理系统',
            },
            userContent: {
                username: '名称加载中',
            },
            userContent_Gerenxixi: {},

        }
    },
    created() {
        // 从localStorage中获取名为'userToken'的项
        const token = localStorage.getItem('userToken');
        axios.get('http://127.0.0.1/loginjwt', {
            params: {
                token: token
            }
        })
            .then(res => {
                console.log(res.data);
                if (res.data > 0) {
                    // 存储用户id
                    localStorage.setItem('userId', res.data);
                } else {
                    // 如果token不存在,可能需要用户重新登录  
                    console.log('No token found.');
                    this.$router.push({ name: 'Login' });
                }
            })
            .catch(error => {
                console.error('Error:', error);
            });




        //请求基本信息
        const userId = localStorage.getItem('userId');
        console.log("home中用户的id" + userId);
        axios.get('http://127.0.0.1/user', {
            params: {
                userId: userId
            }
        })
            .then(res => {
                console.log(res.data);
                this.userContent.username = res.data.userName;
                this.userContent_Gerenxixi = res.data;
                //提示框
                this.userLogin_b = true;
                setTimeout(() => {
                    this.userLogin_b = false;
                }, 2000);
            })
            .catch(error => {
                console.error('Error:', error);
            });

    },
    methods: {
        userexit() {
            console.log("安全退出");
            localStorage.removeItem('userToken');
            localStorage.removeItem('userId');
        },
        Go() {
            this.homeContent_display = false;
            location.href = 'http://localhost:5173'
        },
        NOs() {
            this.homeContent_display = false;
        },
        User_YES() {
            this.userContent_display = true;
        },
        User_NO() {
            this.userContent_display = false;
        },

    }
}
</script>

<style scoped>
.userRegisterok {
    box-shadow: 0 5px 5px rgba(204, 204, 204, 0.299);
    padding: 40px 0;
    border-radius: 20px;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 420px;
    background: rgb(255, 255, 255);
    z-index: 9999;
}

.userRegisteroks {
    box-shadow: 0 5px 5px rgba(204, 204, 204, 0.299);
    padding: 40px 0;
    border-radius: 20px;
    display: flex;
    justify-content: center;
    flex-direction: column;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 420px;
    background: rgb(255, 255, 255);
    z-index: 9999;
}

/*
.userRegisterok p {
    padding: 5px 0 30px 0;
    font-weight: bold;
    font-size: 20px;
    color: rgb(50, 157, 251);

}
*/

.Task_Remove_Box {
    width: 90%;
    display: flex;
    justify-content: center;
}

.Task_Remove_Box button {
    margin: 0 5px;
    padding: 10px 0;
    border-radius: 30px;
    width: 60%;
    border: 0;
    color: #fff;
    font-weight: bold;
    font-size: 18px;

}




.box-s {
    border-radius: 0 0 20px 20px;
    box-shadow: 0 5px 10px rgba(204, 204, 204, 0.474);
}

.home_box {
    align-items: center;
}

.flex-grow {
    flex-grow: 1;
}</style>

(需要更多的话 去vx公众号 : wmcode 去获取 (我不打广告只是公众号是作为我的云端链接))

Vue-Router

import { createRouter, createMemoryHistory } from 'vue-router';

import Home from './src/views/Home.vue'
import Login from './src/views/Login.vue'
import Register from './src/views/Register.vue'

import AllTaskList from './src/views/AllTaskList.vue'
import DayBook from './src/views/DayBook.vue'
import NextTask from './src/views/NextTask.vue'
import WaitTask from './src/views/WaitTask.vue'
import WasteBox from './src/views/WasteBox.vue'

const router = createRouter({
    history: createMemoryHistory(),
    routes: [
        { 
            path: '/', 
            redirect: '/home' 
        },
        { 
            path: '/home', 
            name: 'Home', 
            component: Home, 
            children: [
                {  
                    // 空字符串 '' 表示它是默认的子路由  
                    path: '',  
                    name: 'AllTaskList',  
                    component: AllTaskList 
                }, 
                { 
                    path: '/alltasklist', 
                    name: 'AllTaskListDirect',
                    component: AllTaskList 
                },
                { 
                    path: '/daybook', 
                    name: 'DayBook',
                    component: DayBook 
                },
                { 
                    path: '/nexttask', 
                    name: 'NextTask',
                    component: NextTask 
                },
                { 
                    path: '/waittask', 
                    name: 'WaitTask',
                    component: WaitTask 
                },
                { 
                    path: '/wastebox', 
                    name: 'WasteBox',
                    component: WasteBox 
                }
            ]
        },
        { 
            path: '/register', 
            name: 'Register',// 名字要大写
            component: Register 
        },
        { 
            path: '/login', 
            name: 'Login',
            component: Login 
        },
        
    ]
});


export default router;

Axios

细节节点
  • 用Get/Post当异步发送到后端
  • Get的格式
  • Post的格式跟后端实体类映射的 接口,参数名 一定要一致!

Elemten-Plus

看着文档结合需求去修改就好了

等更多前端技术......

后端设计

(懒得搞了, 去vx公众号 : wmcode 去获取 (我不打广告只是公众号是作为我的云端链接))

Springboot 2

  • 实体类
  • 配置类
  • 控制层
  • 服务层
  • Dao层(Mapper)
  • 工具
  • 自定义响应类
  • package com.takem.util;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;
    
    
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @ToString
    public class JsonResult {
        // 状态码
        private  Integer stateCode;
    
        // 响应信息
        private String message;
    
        // 纯ok
        public static JsonResult ok() {
            JsonResult jsonResult = new JsonResult();
            jsonResult.setStateCode(ServiceCode.OK.getValue());
            return jsonResult;
        }
    
    
    }
    
    package com.takem.util;
    
    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTVerifier;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.exceptions.JWTVerificationException;
    import com.auth0.jwt.exceptions.SignatureVerificationException;
    import com.auth0.jwt.exceptions.TokenExpiredException;
    import com.auth0.jwt.interfaces.DecodedJWT;
    import com.takem.entity.Users;
    import org.springframework.stereotype.Component;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServletRequest;
    import java.util.Calendar;
    
    
    @Component
    public class JwtTokenUtil {
        private static final String TOKENKEY = "chenchen"; // 随机盐
        private static final long EXPIRATION = 86400000; // token有效时间(毫秒)
    
        public String generateToken(Integer userId , String userName) {
    
            //生成Token
            Calendar instance = Calendar.getInstance();
            instance.add(Calendar.SECOND, (int)EXPIRATION);
    
            String token = JWT.create()//生成令牌
                    .withClaim("userId", userId)//payload
                    .withClaim("username", userName)//设置自定义用户名
                    .withExpiresAt(instance.getTime())//设置令牌的有效时间
                    .sign(Algorithm.HMAC256(TOKENKEY))//设置签名 保密 复杂
                    ;
            System.out.println(token);//输出令牌
            return token;
        }
    
    
        // 验证token
        public Integer validateToken(String token) {
            if (token == null || token.isEmpty()) {
                System.out.println("Token is null or empty");
                return 0;
            }
            try {
                 // 检查token是否包含两个点号,这是JWT标准格式的一部分
                if (token.split("\\.").length != 3) {
                    System.out.println("Invalid token format");
                    return 0;
                }
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(TOKENKEY)).build();
                try {
                    DecodedJWT decodedJWT = jwtVerifier.verify(token);
                    System.out.println("用户Id:" + decodedJWT.getClaim("userId").asInt());
                    System.out.println("用户名:" + decodedJWT.getClaim("username").asString());
                    System.out.println("过期时间:" + decodedJWT.getExpiresAt());
                    return decodedJWT.getClaim("userId").asInt();
                }catch (Exception e){
                    if (e instanceof TokenExpiredException) {
                        System.out.println("Token已过期");
                    } else if (e instanceof SignatureVerificationException) {
                        System.out.println("签名验证失败");
                    } else if (e instanceof JWTVerificationException) {
                        System.out.println("JWT验证失败: " + e.getMessage());
                    } else {
                        System.out.print("未知错误: ");
                        e.printStackTrace();
                    }
                }
                return 0;
            } catch (Exception e) {
                return 0;
            }
        }
    
    }
    
    package com.takem.util;
    
    // 自定义状态码
    public enum ServiceCode {
        OK(200),
        ERR_NOT_FOUND(404),
        ERR_CONFLICT(409),
        ERR_CUSTOM(1000);
    
        private Integer value;
    
        ServiceCode(Integer value) {
            this.value = value;
        }
    
        public Integer getValue() {
            return value;
        }
    }
    
  • 时间类
  • package com.takem.util;
    
    import java.time.LocalDateTime;
    import java.time.ZoneId;
    import java.time.ZonedDateTime;
    import java.time.format.DateTimeFormatter;
    
    public class TimeConverter {
    
        public String TimeZ8(String utcTimeStr){
    
            // 解析UTC时间字符串为ZonedDateTime
            DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT.withZone(ZoneId.of("UTC"));
            ZonedDateTime utcTime = ZonedDateTime.parse(utcTimeStr, formatter);
    
            // 转换为东八区时间(中国标准时间)
            ZonedDateTime cstTime = utcTime.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
    
            // 打印结果
            System.out.println("CST Time (ZonedDateTime): " + cstTime);
            return cstTime+"";
        }
    }

  • 启动类
  • package com.takem;
    
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    @MapperScan("com.takem.mapper")
    public class TakeMApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(TakeMApplication.class, args);
        }
    
    }
    

MyBatis-plus

  • 增添数据
  • 删除数据
    • 软删除
    • 硬删除
  • 改动数据
  • 查询数据

JWT

有效的token处理很重要,我方法是存在了Cookie跟用户登录时存储在本地内

这个的话我其实也做过相关的文章了,可以直接去跳转观看

Springboot | 零基础快速搭建JWT简单登录案例(一)

邮件服务

这个的话我其实也做过相关的文章了,可以直接去跳转观看

基于SpringBoot构造超简易QQ邮件服务发送(分离-图解-新手)

等技术......

项目部署

本地部署

这个的话我其实也做过相关的文章了,可以直接去跳转观看

新手快速部署Springboot 的Jar包 (图解-BuiId,Maven)

(到底啦~)

(需要更多的话 去vx公众号 : wmcode 去获取 (我不打广告只是公众号是作为我的云端链接))

相关推荐

  1. 联系人管理系统简易

    2024-07-17 14:10:04       24 阅读
  2. 【物流管理系统-Python简易

    2024-07-17 14:10:04       53 阅读
  3. Django开发一个简易学生管理系统

    2024-07-17 14:10:04       30 阅读
  4. 学生管理系统(残缺

    2024-07-17 14:10:04       20 阅读
  5. 前端架构: 简易脚手架开发

    2024-07-17 14:10:04       49 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-17 14:10:04       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-17 14:10:04       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-17 14:10:04       45 阅读
  4. Python语言-面向对象

    2024-07-17 14:10:04       55 阅读

热门阅读

  1. 使用云服务器的Docker安装MySQL 5.7

    2024-07-17 14:10:04       17 阅读
  2. svn ldap认证临时切换到本地认证

    2024-07-17 14:10:04       17 阅读
  3. 定期整理pycharm相关缓存

    2024-07-17 14:10:04       16 阅读
  4. Linux C++ 055-设计模式之状态模式

    2024-07-17 14:10:04       17 阅读
  5. MySQL left join、right join以及inner join的区别 ?

    2024-07-17 14:10:04       17 阅读
  6. 网络安全-网络安全及其防护措施5

    2024-07-17 14:10:04       18 阅读
  7. 数据结构课程设计:客房信息管理系统 基于c

    2024-07-17 14:10:04       18 阅读
  8. 微信小程序:声明式导航、刷新节流

    2024-07-17 14:10:04       20 阅读
  9. 苹果HEIC 数据转 PNG

    2024-07-17 14:10:04       18 阅读
  10. uniapp使用 web-view 与网页互相通信

    2024-07-17 14:10:04       18 阅读