uniapp+springboot 实现前后端分离的个人备忘录系统【超详细】

目录

(1)项目可行性分析

(一)技术可行性:

(二)经济可行性:

(三)社会可行性:

(2)需求描述

功能模块图

用例图:

(3)界面原型

1.登录:

2.注册:

3.我的: 

4.分类: 

5.记录: 

6.主页: 

(4)数据库设计

(1)User表:

(2)Notes表:

(3)Type表:

E-r图如下:

数据库的sql文件:

(5)后端工程

***mon包: 

config包: 

CorsConfig.java

domain包:

Notes.java

Type.java

User.java

utils包:

PathUtils.java

controller包:

NotesController.java

TypeController.java

UploadController.java

 UserController.java

 service包:

NotesService

OssUploadService

TypeService

UserService

Impl包:

NotesServiceImpl.java

OssUploadServiceImpl.java

TypeServiceImpl.java

UserServiceImpl.java

(6)前端工程

index.js

pages.json

 catNote.vue

 Login.vue

 Main.vue

 mine.vue

Register.vue


接下来我们使用uniapp+springboot实现一个简单的前后端分离的小项目----个人备忘录系统,适合初学者学习,全文大约5w字,全部免费,以下是详细步骤:

(1)项目可行性分析

(一)技术可行性:

1.uniapp是一个基于Vue.js框架的跨平台开发工具,可以在多个平台上实现一次开发多端运行。它提供了丰富的组件和插件,使得开发变得更加高效。

2.uniapp支持多个主流的移动端平台,如iOS和Android,以及微信小程序、H5等。这意味着你可以通过uniapp开发一个备忘录系统,并在多个平台上发布和使用。

3.Vue.js作为uniapp的底层框架,拥有活跃的开发社区和丰富的生态系统,可以提供大量的资源和支持。

(二)经济可行性:

1.uniapp的开发成本相对较低,因为它使用了一套代码可以覆盖多个平台的开发方式,减少了重复的工作量和开发时间。

2.由于uniapp支持多个主流平台,你可以在不同的平台上发布你的备忘录系统,扩大用户群体,增加潜在的收入来源。

3.uniapp的跨平台特性可以降低维护成本,因为你只需要维护一套代码,而不是针对每个平台都进行独立的开发和维护。

(三)社会可行性:

1.备忘录系统是一个常见且实用的应用,它可以帮助个人记录重要事项、提醒任务等。这种类型的应用在社会中有广泛的需求。

2.通过使用uniapp开发备忘录系统,你可以满足不同用户使用不同平台的需求,提高用户体验和满意度。

3.在移动互联网时代,人们越来越依赖手机和移动应用程序进行工作和生活管理。开发备忘录系统可以满足人们随时随地记录和查看备忘录的需求,符合社会的发展趋势。

(2)需求描述

个人备忘录系统主要有登录、注册、查看所有备忘录、创建新的备忘录、删除备忘录、修改备忘录、根据分类查询已完成或未完成的备忘录。

功能模块图
用例图:

(3)界面原型

主要界面如下:

1.登录:
2.注册:
3.我的: 
4.分类: 
5.记录: 
6.主页: 

(4)数据库设计

数据库主要有三个表:

(1)User表:

表名

类型

长度

注释

id

int

255

id

username

varchar

255

用户名

password

varchar

255

密码

avatar

varchar

255

头像

(2)Notes表:

表名

类型

长度

注释

id

int

255

id

rid

int

255

用户id

detail

varchar

255

内容

time

datetime

255

截止时间

type

int

255

类型

finish

int

255

任务是否完成

(3)Type表:

表名

类型

长度

注释

typeid

int

255

主键

type

varchar

255

是什么类型

E-r图如下:
数据库的sql文件:
/*
 Navicat Premium Data Transfer

 Source Server         : mySQL
 Source Server Type    : MySQL
 Source Server Version : 80019
 Source Host           : localhost:3305
 Source Schema         : memo

 Target Server Type    : MySQL
 Target Server Version : 80019
 File Encoding         : 65001

 Date: 25/12/2023 11:06:46
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for notes
-- ----------------------------
DROP TABLE IF EXISTS `notes`;
CREATE TABLE `notes`  (
  `id` int(0) NOT NULL AUTO_INCREMENT ***MENT 'note表的id',
  `rid` int(0) NOT NULL ***MENT '这个笔记是哪个人的',
  `detail` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL ***MENT '笔记的内容',
  `photo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL ***MENT '用户上传的图片,null或者0表示无',
  `time` datetime(0) NOT NULL ***MENT '笔记的创建时间',
  `type` int(0) NOT NULL ***MENT '笔记的类型',
  `finish` int(0) NOT NULL ***MENT '任务是否完成',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 57 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of notes
-- ----------------------------
INSERT INTO `notes` VALUES (31, 13, '软件设计师考试', 'http://s6422okdy.hn-bkt.clouddn.***/2023/12/23/e7bbeaecf4984a72af03d6d623a4f96c.jpg', '2023-11-04 00:04:28', 2, 1);
INSERT INTO `notes` VALUES (32, 13, '国奖答辩', NULL, '2023-10-16 20:26:27', 2, 1);
INSERT INTO `notes` VALUES (33, 13, 'hehang-blog数据库项目', NULL, '2024-01-07 20:26:56', 2, 1);
INSERT INTO `notes` VALUES (34, 13, '数据库详细设计报告', NULL, '2023-11-08 20:27:24', 2, 1);
INSERT INTO `notes` VALUES (35, 13, '数据库课设验收', NULL, '2023-11-22 20:28:02', 2, 1);
INSERT INTO `notes` VALUES (36, 13, '计算机组成原理期中考试', NULL, '2023-11-24 20:28:44', 2, 1);
INSERT INTO `notes` VALUES (37, 13, '计算机能力挑战赛C语言', NULL, '2023-11-25 08:00:00', 2, 1);
INSERT INTO `notes` VALUES (38, 13, '学生代表大会', '0', '2023-12-07 13:15:00', 3, 1);
INSERT INTO `notes` VALUES (39, 13, '闪聚支付springclound项目', NULL, '2023-12-19 18:30:21', 2, 1);
INSERT INTO `notes` VALUES (40, 13, '英语四级考试', NULL, '2023-12-16 09:00:00', 2, 1);
INSERT INTO `notes` VALUES (41, 13, '数据库课设详细设计文档', '0', '2024-01-07 23:59:59', 2, 0);
INSERT INTO `notes` VALUES (43, 13, '完成代码细节的修改', NULL, '2023-12-18 12:00:00', 2, 1);
INSERT INTO `notes` VALUES (44, 14, '记得吃药', '0', '2023-12-24 14:57:57', 8, 0);
INSERT INTO `notes` VALUES (45, 14, '写完uniapp期末课设的报告', '', '2023-12-24 14:08:58', 2, 1);
INSERT INTO `notes` VALUES (47, 13, '计算机能力挑战赛决赛\n地点:武汉纺织大学阳光校区', 'http://s6422okdy.hn-bkt.clouddn.***/2023/12/23/daa53bce645146d08531649be4308db4.jpg', '2023-12-09 09:00:00', 2, 1);

-- ----------------------------
-- Table structure for type
-- ----------------------------
DROP TABLE IF EXISTS `type`;
CREATE TABLE `type`  (
  `typeid` int(0) NOT NULL ***MENT '主键',
  `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL ***MENT '是什么类型',
  PRIMARY KEY (`typeid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of type
-- ----------------------------
INSERT INTO `type` VALUES (1, '日常');
INSERT INTO `type` VALUES (2, '学习');
INSERT INTO `type` VALUES (3, '工作');
INSERT INTO `type` VALUES (4, '娱乐');
INSERT INTO `type` VALUES (5, '社交');
INSERT INTO `type` VALUES (6, '家庭');
INSERT INTO `type` VALUES (7, '个人');
INSERT INTO `type` VALUES (8, '健康');
INSERT INTO `type` VALUES (9, '财务');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(0) NOT NULL AUTO_INCREMENT ***MENT '用户的id',
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL ***MENT '用户名字',
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL ***MENT '用户密码',
  `avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL ***MENT '用户的头像',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (13, 'hehang', '123456', 'https://img2.baidu.***/it/u=3841326637,2519425910&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=501');
INSERT INTO `user` VALUES (14, 'abcd', '123456', 'https://pic2.zhimg.***/v2-fc348d5e926116782149d2151dc09834.jpg');
INSERT INTO `user` VALUES (15, 'mynote', '123456', 'https://img2.baidu.***/it/u=3841326637,2519425910&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=501');
INSERT INTO `user` VALUES (16, '1234', '123456', 'https://img2.baidu.***/it/u=3841326637,2519425910&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=501');

SET FOREIGN_KEY_CHECKS = 1;

(5)后端工程

首先打开IDEA,选择创建Spring Initializr,按照以下配置,jdk版本无法选择jdk1.8,先不管,进去以后可以改,具体操作可以看我的另一篇相关的博客

配置spring版本,先选择3.2.0,进入项目后再通过pom文件修改

进入项目后修改pom.xml文件为如下配置,我们在pom中手动修改了jdk版本为1.8,spring为2.7.8,这样兼容性比较好,修改后记得刷新maven

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.8</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>***.example</groupId>
    <artifactId>Memo-hehang</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hehang</name>
    <description>memo</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>***.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>***.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

        <!--七牛云OOS-->
        <dependency>
            <groupId>***.qiniu</groupId>
            <artifactId>qiniu-java-sdk</artifactId>
            <version>[7.13.0, 7.13.99]</version>
        </dependency>
        <dependency>
            <groupId>***.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.10.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-***piler-plugin</artifactId>
                <configuration>
                    <source>9</source>
                    <target>9</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>


然后我们修改resources文件夹下的application.yml,数据库连接修改成你自己的,七牛云的使用可以看我上一篇博客

server:
  port: 2023

spring:
  datasource:
    driver-class-name: ***.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3305/memo?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456

mybatis-plus:
  global-config:
    db-config:
      id-type: auto
#  configuration:
#    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 打开七牛云,找到密钥管理,把相关信息填写到下面3行
myoss:
  a***essKey: yourAK
  secretKey: yourSK
  bucket: yourbucket



接下来在项目下创建如下几个包:

***mon包: 
R.java

在***mon文件下创建 R.java 类,主要用于封装返回给前端的数据:

package ***.example.memohehang.***mon;

import lombok.Data;

import java.io.Serializable;

/**
 * 统一返回类型
 */
@Data
public class R implements Serializable
{

    private int code; // 200是正常,非200表示异常
    private String msg;
    private Object data;

    public static R su***ess(Object data)
    {
        return su***ess(200, "操作成功", data);
    }

    public static R su***ess(int code, String msg, Object data)
    {
        R r = new R();
        r.setCode(code);
        r.setMsg(msg);
        r.setData(data);
        return r;
    }

    public static R error(int i, String msg)
    {
        return error(400, msg, null);
    }

    public static R error(String msg, Object data)
    {
        return error(400, msg, data);
    }

    public static R error(int code, String msg, Object data)
    {
        R r = new R();
        r.setCode(code);
        r.setMsg(msg);
        r.setData(data);
        return r;
    }

}
config包: 
CorsConfig.java

在config包下创建 CorsConfig.java 类,用于解决前端跨域问题,在config.AllowedOrigin中填写你自己的前端端口,一般为8080,或者填 * ,允许所有

package ***.example.memohehang.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.cors.CorsConfiguration;

//解决前端跨域问题
@Configuration
public class CorsConfig {
    @Bean
    public CorsFilter corsFilter()
    {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedOrigin("http://localhost:8080");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        config.setAllowCredentials(true);
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}
domain包:

对应数据库的实体类

Notes.java

这里我们利用jsonformat注解进行时间格式化

package ***.example.memohehang.domain;

import ***.baomidou.mybatisplus.annotation.IdType;
import ***.baomidou.mybatisplus.annotation.TableId;
import ***.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Notes {
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    private Integer rid;
    // 内容
    private String detail;
    // 截止时间
    @JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private Date time;
    // 类型
    private Integer type;

    //图片
    private String photo;
    // 是否完成 0表示还没有 1表示完成了
    private Integer finish;


}
Type.java
package ***.example.memohehang.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Type
{
    private Integer typeid;
    private String type;
}
User.java
package ***.example.memohehang.domain;

import lombok.AllArgsConstructor;
import ***.baomidou.mybatisplus.annotation.IdType;
import ***.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User
{
    //主键自增
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    private String username;
    private String password;
    // 头像
    private String avatar;
}
utils包:
PathUtils.java

这是对上传的文件进行重命名,在后面的七牛云相关的service中会用到

package ***.example.memohehang.utils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;


//对原始文件名进行修改文件名,并修改存放目录
public class PathUtils
{

    public static String generateFilePath(String fileName)
    {
        //根据日期生成路径
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd/");
        String datePath = sdf.format(new Date());
        //uuid作为文件名
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        //后缀和文件后缀一致
        int index = fileName.lastIndexOf(".");
        // test.jpg -> .jpg
        String fileType = fileName.substring(index);
        return new StringBuilder().append(datePath).append(uuid).append(fileType).toString();
    }
}

 我一般写代码的顺序为:Controller----service----serviceImpl----mapper,在方法学一般先写调用体,根据调用写对应的实现,下面我们按照这个顺序来写:

controller包:
NotesController.java
package ***.example.memohehang.controller;

import ***.example.memohehang.***mon.R;
import ***.example.memohehang.domain.Notes;
import ***.example.memohehang.service.NotesService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;


@Controller
@RequestMapping("/note")
public class NotesController {
    @Autowired
    private NotesService notesService;


    // 添加
    @RequestMapping("/save")
    @ResponseBody
    public R save(@RequestBody Notes notes)
    {
        System.out.println("添加备忘录:" + notes);
        return notesService.addNote(notes);
    }


    // 根据noteid查询对应的note
    @RequestMapping ("/selectByNote/{noteid}")
    @ResponseBody
    public R selectByNote(@PathVariable("noteid") Integer noteid)
    {

        return notesService.selectByNote(noteid);
    }

    // 查询 只显示用户自己的
    @RequestMapping ("/selectAllByUserID/{userid}")
    @ResponseBody
    public R selectAllById(@PathVariable("userid") Integer userid,@RequestParam(defaultValue = "1") Integer currentPage)
    {
        return notesService.selectAllById(userid,currentPage);
    }

    // 查询最近即将截止的未完成的备忘录的时间差
    @RequestMapping ("/selectTime/{userid}")
    @ResponseBody
    public R selectcutDownTime(@PathVariable("userid") Integer userid)
    {
        return notesService.selectcutDownTime(userid);
    }



    // 分类查询
    @RequestMapping ("/selectByType/{userid}/{type}/{isFinish}")
    @ResponseBody
    public R selectByType(@PathVariable("userid") Integer userid, @PathVariable("type") Integer type,@PathVariable("isFinish") Integer isFinish,@RequestParam(defaultValue = "1") Integer currentPage)
    {

        return notesService.selectByType(userid,type,isFinish,currentPage);

    }


    // 修改
    @RequestMapping("/update")
    @ResponseBody
    public R update(@RequestBody Notes notes)
    {
        return notesService.update(notes);
    }

    // 删除
    @DeleteMapping("/delete/{id}")
    @ResponseBody
    public R delete(@PathVariable("id") Integer id)
    {
        return notesService.delete(id);
    }



}


TypeController.java
package ***.example.memohehang.controller;

import ***.example.memohehang.***mon.R;
import ***.example.memohehang.service.TypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;


@Controller
@RequestMapping("/type")
public class TypeController
{
    @Autowired
    private TypeService typeService;

    // 获取编号与类型的映射表
    @ResponseBody
    @GetMapping("/getMapping")
    public R typeMapping()
    {
        return typeService.typeMapping();
    }

}


UploadController.java
package ***.example.memohehang.controller;


import ***.example.memohehang.***mon.R;
import ***.example.memohehang.service.OssUploadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
public class UploadController
{
    @Autowired
    private OssUploadService ossUploadService;

    @PostMapping("/upload")
    public R uploadImg(MultipartFile img)
    {

        return ossUploadService.uploadImg(img);
    }
}
 UserController.java
package ***.example.memohehang.controller;

import ***.example.memohehang.***mon.R;
import ***.example.memohehang.domain.User;
import ***.example.memohehang.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;


@RestController
@RequestMapping("/user")
public class UserController {


    @Autowired
    private UserService userService;

    // 登录功能
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public R login(@RequestBody User user)
    {
        return userService.login(user);

    }

    // 注册功能
    @RequestMapping(value = "/register", method = RequestMethod.POST)
    public R register(@RequestBody User user)
    {
        return userService.register(user);
    }

    // 根据用户id查询
    @RequestMapping ("/selectUserById/{userid}")
    @ResponseBody
    public R selectUserById(@PathVariable("userid") Integer userid)
    {
        return userService.selectUserById(userid);
    }

}



我们用mybatisplus自带的查询函数即可完成所有的CRUD,因此我们不需要写SQL语句

 service包:
NotesService
package ***.example.memohehang.service;



import ***.baomidou.mybatisplus.extension.service.IService;
import ***.example.memohehang.***mon.R;
import ***.example.memohehang.domain.Notes;
import ***.example.memohehang.domain.User;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

public interface NotesService extends IService<Notes>
{
    /**
     * 添加备忘录
     * @param notes
     * @return
     */
    public R addNote(Notes notes);

    /**
     * 根据noteid查询note
     * @param noteid
     * @return
     */
    public R selectByNote(Integer noteid);

    /**
     * 查询 只显示用户自己的
     * @param userid
     * @param currentPage
     * @return
     */
    public R selectAllById(Integer userid,Integer currentPage);

    /**
     * 查询最近即将截止的未完成的备忘录的时间差
     * @param userid
     * @return
     */
    public R selectcutDownTime(Integer userid);

    /**
     * 分类查询
     * @param userid
     * @param type
     * @param isFinish
     * @param currentPage
     * @return
     */
    public R selectByType(Integer userid, Integer type,Integer isFinish,Integer currentPage);

    /**
     * 修改
     * @param notes
     * @return
     */
    public R update(Notes notes);

    /**
     * 删除
     * @param id
     * @return
     */
    public R delete(Integer id);
}


OssUploadService
package ***.example.memohehang.service;

import ***.example.memohehang.***mon.R;
import org.springframework.web.multipart.MultipartFile;

public interface OssUploadService {
    //图片上传到七牛云
    R uploadImg(MultipartFile img);
}
TypeService
package ***.example.memohehang.service;
import ***.baomidou.mybatisplus.extension.service.IService;
import ***.example.memohehang.***mon.R;
import ***.example.memohehang.domain.Type;


public interface TypeService extends IService<Type>
{
    /**
     * 获取所有类型
     * @return
     */
    public R typeMapping();
}


UserService
package ***.example.memohehang.service;
import ***.baomidou.mybatisplus.extension.service.IService;
import ***.example.memohehang.***mon.R;
import ***.example.memohehang.domain.User;



public interface UserService extends IService<User>
{
    /**
     * 登录
     * @param user
     * @return
     */
    public R login(User user);

    /**
     * 注册
     * @param user
     * @return
     */
    public R register(User user);

    /**
     * 根据id查询用户
     * @param userid
     * @return
     */
    public R selectUserById(Integer userid);
}


接下来在service包中创建Impl,注意第一个 “ I ” 是大写的 “ i ”,第二个是小写的 “ L ”

Impl包:
NotesServiceImpl.java
package ***.example.memohehang.service.Impl;



import ***.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import ***.baomidou.mybatisplus.core.metadata.IPage;
import ***.baomidou.mybatisplus.extension.plugins.pagination.Page;
import ***.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import ***.example.memohehang.***mon.R;
import ***.example.memohehang.domain.Notes;
import ***.example.memohehang.mapper.NotesMapper;
import ***.example.memohehang.service.NotesService;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class NotesServiceImpl extends ServiceImpl<NotesMapper, Notes> implements NotesService
{
    private NotesMapper notesMapper;
    /**
     * 添加备忘录
     * @param notes
     * @return
     */
    @Override
    public R addNote(Notes notes)
    {
        boolean b = save(notes);//调用mybatis-plus
        if (b)
        {
            return R.su***ess(200, "添加成功", null);
        }
        else
        {
            return R.error(405, "添加失败");
        }

    }

    /**
     * 根据noteid查询note
     * @param noteid
     * @return
     */
    @Override
    public R selectByNote(Integer noteid)
    {
        QueryWrapper<Notes> wrapper = new QueryWrapper<>();

        wrapper.eq("id", noteid);
        List<Notes> note =  list(wrapper);

        if (note == null)
        {
            return R.error(400,"查询失败");
        }
        else
        {
            return R.su***ess(note.get(0));
        }
    }

    /**
     * 查询 只显示用户自己的
     * @param userid
     * @param currentPage
     * @return
     */
    @Override
    public R selectAllById(Integer userid, Integer currentPage)
    {
        Page page = new Page(currentPage, 20);
        // 查询条件 分页的基础上 再按照创建时间排序
        IPage pageData = page(page, new QueryWrapper<Notes>().orderByAsc("finish","time").eq("rid", userid));

        return R.su***ess(pageData);
    }

    /**
     * 查询最近即将截止的未完成的备忘录的时间差
     * @param userid
     * @return
     */
    @Override
    public R selectcutDownTime(Integer userid)
    {
        QueryWrapper<Notes> wrapper = new QueryWrapper<>();
        wrapper.eq("rid", userid).orderByAsc("finish", "time");
        List<Notes> note = list(wrapper);

        // 处理查询结果
        if (note != null && !note.isEmpty())
        {
            Notes firstUnfinishedNote = null;
            for (Notes n : note)
            {
                if (n.getFinish() == 0)
                {
                    firstUnfinishedNote = n;
                    break;
                }
            }

            if (firstUnfinishedNote != null)
            {
                LocalDateTime currentTime = LocalDateTime.now();
                Date noteTime = firstUnfinishedNote.getTime();

                Instant instant = noteTime.toInstant();
                LocalDateTime noteLocalDateTime = instant.atZone(ZoneId.systemDefault()).toLocalDateTime();

                Duration duration = Duration.between(currentTime, noteLocalDateTime);
                long days = duration.toDays();
                long hours = duration.toHoursPart();
                long minutes = duration.toMinutesPart();
                long seconds = duration.toSecondsPart();

                // 构造返回结果
                Map<String, Object> resultMap = new HashMap<>();
                resultMap.put("days", days);
                resultMap.put("hours", hours);
                resultMap.put("minutes", minutes);
                resultMap.put("seconds", seconds);

                System.out.println(resultMap);
                // 返回结果
                return R.su***ess(200, "duration", resultMap);
            }
            else
            {
                return R.error(405, "没有找到未完成的备忘录");
            }
        }
        else
        {
            return R.error(405, "没有找到备忘录");
        }
    }

    /**
     * 分类查询
     * @param userid
     * @param type
     * @param isFinish
     * @param currentPage
     * @return
     */
    @Override
    public R selectByType(Integer userid, Integer type, Integer isFinish, Integer currentPage)
    {
        Page page = new Page(currentPage, 20);

        IPage pageData = page(page, new QueryWrapper<Notes>().orderByAsc("time").eq("rid", userid).eq("finish", isFinish).eq("type", type));

        return R.su***ess(pageData);
    }

    /**
     * 修改
     * @param notes
     * @return
     */
    @Override
    public R update(Notes notes)
    {
        System.out.println("开始更新");
        System.out.println(notes);
        boolean b = update(notes, new QueryWrapper<Notes>().eq("id", notes.getId()));
        if (b)
        {
            System.out.println("更新成功");
            return R.su***ess(200, "更新成功", null);
        }
        System.out.println("更新失败");
        return R.error(405, "更新失败");
    }

    /**
     * 删除
     * @param id
     * @return
     */
    @Override
    public R delete(Integer id)
    {
        System.out.println("user delete..." + id);
        boolean b = removeById(id);
        if (b)
        {
            return R.su***ess(200, "删除成功", null);
        }
        return R.error(405, "删除失败");
    }
}


OssUploadServiceImpl.java

注意这里需要修改外链回显链接

package ***.example.memohehang.service.Impl;

import ***.example.memohehang.***mon.R;
import ***.example.memohehang.service.OssUploadService;
import ***.example.memohehang.utils.PathUtils;
import ***.google.gson.Gson;
import ***.qiniu.***mon.QiniuException;
import ***.qiniu.http.Response;

import ***.qiniu.storage.Configuration;
import ***.qiniu.storage.Region;
import ***.qiniu.storage.UploadManager;
import ***.qiniu.storage.model.DefaultPutRet;
import ***.qiniu.util.Auth;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;


@Service
@Data//为成员变量生成get和set方法
@ConfigurationProperties(prefix = "myoss")
//把文件上传到七牛云
public class OssUploadServiceImpl implements OssUploadService {

    @Override
    //MultipartFile是spring提供的接口
    public R uploadImg(MultipartFile img) {

        //获取原始文件名
        String originalFilename = img.getOriginalFilename();

        // 获取文件大小
        long fileSize = img.getSize();

        //PathUtils.generateFilePath(originalFilename)表示把原始文件名转换成指定文件名

        String filePath = PathUtils.generateFilePath(originalFilename);

        //下面用于调用的uploadOss方法返回的必须是String类型
        String url = uploadOss(img,filePath);
        System.out.println("外链地址:"+url);
        //把得到的外链地址返回给前端
        return R.su***ess(200,"操作成功",url);
    }

    //----------------------------------上传文件到七牛云----------------------------------------

    //注意要从application.yml读取属性数据,下面的3个成员变量的名字必须对应application.yml的myoss属性的三个子属性名字
    private String a***essKey;
    private String secretKey;
    private String bucket;

    //上传文件的具体代码。MultipartFile是spring提供的接口,作用是实现文件上传
    private String uploadOss(MultipartFile imgFile, String filePath){
        //构造一个带指定 Region 对象的配置类。你的七牛云OSS创建的是哪个区域的,那么就调用Region的什么方法即可
        Configuration cfg = new Configuration(Region.huanan());
        cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;// 指定分片上传版本
        UploadManager uploadManager = new UploadManager(cfg);

        //打开七牛云,把鼠标悬浮在右上角的个人头像,然后就会看到'密钥管理',点击进入就有你的密钥,把其中的AK和SK复制到下面两行
        //String a***essKey = "_ibGP9wytjLCAZPqcFaWQNxbw7fMUvofSOvOFFR3";
        //String secretKey = "QSOAU-cv3sSDGNfVNPF6iXz-PsP5X9QTrjFI9zYw";
        //String bucket = "hehang-blog";
        //为避免上面3行暴露信息,我们会把信息写到application.yml里面,然后添加ConfigurationProperties注解、3个成员变量即可读取

        //文件名,如果写成null的话,就以文件内容的hash值作为文件名
        String key = filePath;

        try {

            //byte[] uploadBytes = "hello qiniu cloud".getBytes("utf-8");
            //ByteArrayInputStream byteInputStream=new ByteArrayInputStream(uploadBytes);

            //上面两行是官方写的(注释掉),下面那几行是我们写的
            //把前端传过来的文件转换成InputStream对象
            InputStream xxinputStream = imgFile.getInputStream();

            Auth auth = Auth.create(a***essKey, secretKey);
            String upToken = auth.uploadToken(bucket);

            try {
                //把前端传过来的xxinputStream图片上传到七牛云
                Response response = uploadManager.put(xxinputStream,key,upToken,null, null);
                //解析上传成功的结果
                DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
                System.out.println("上传成功! 生成的key是: "+putRet.key);
                System.out.println("上传成功! 生成的hash是: "+putRet.hash);
                return "http://s6422okdy.hn-bkt.clouddn.***/"+key;//注意这个地方替换成自己的域名,http://不能掉

            } catch (QiniuException ex) {
                Response r = ex.response;
                System.err.println(r.toString());
                try {
                    System.err.println(r.bodyString());
                } catch (QiniuException ex2) {
                    //ignore
                }
            }
        }catch (Exception e) {
            //ignore
        }
        return "上传失败";
    }
}


TypeServiceImpl.java
package ***.example.memohehang.service.Impl;



import ***.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import ***.example.memohehang.***mon.R;
import ***.example.memohehang.domain.Type;
import ***.example.memohehang.mapper.TypeMapper;
import ***.example.memohehang.service.TypeService;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;

@Service
public class TypeServiceImpl extends ServiceImpl<TypeMapper, Type> implements TypeService
{
    /**
     * 获取所有类型
     * @return
     */
    @Override
    public R typeMapping()
    {
        List<Type> types = list();
        HashMap<Integer, String> hashMap = new HashMap<>();
        for (Type type : types)
        {
            hashMap.put(type.getTypeid(), type.getType());
        }
        return R.su***ess(hashMap);
    }
}


UserServiceImpl.java
package ***.example.memohehang.service.Impl;



import ***.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import ***.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import ***.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import ***.example.memohehang.***mon.R;
import ***.example.memohehang.domain.User;
import ***.example.memohehang.mapper.UserMapper;
import ***.example.memohehang.service.UserService;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService
{

    /**
     * 登录
     * @param user
     * @return
     */
    @Override
    public R login(User user)
    {
        // 判断用户账号是否正确
        User one = getOne(new QueryWrapper<User>()
                .eq("username", user.getUsername())
                .eq("password", user.getPassword())
        );

        if (one != null)
        {
            return R.su***ess(200,"登录成功", one);
        }
        else
        {
            return R.error(405, "账号或密码错误");
        }
    }

    @Override
    public R register(User user)
    {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, user.getUsername());
        System.out.println(user);
        User tempUsername = getOne(queryWrapper);
        // 先判断一下用户是否存在 存在就返回false
        if (tempUsername != null)
        {
            return R.error(405,"账户已存在");
        }
        else
        {
            //网上随机找的一个图片
            user.setAvatar("https://img2.baidu.***/it/u=3841326637,2519425910&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=501");
            // 反之加入
            save(user);
            return R.su***ess(user);
        }
    }

    /**
     * 根据id查询用户
     * @param userid
     * @return
     */
    @Override
    public R selectUserById(Integer userid)
    {
        // 判断用户账号是否正确
        User one = getOne(new QueryWrapper<User>()
                .eq("id", userid)
        );

        if (one != null)
        {
            return R.su***ess(200,"查询成功", one);
        }
        else
        {
            return R.error(405, "查询失败");
        }
    }
}


至此后端工程全部创建完毕,按照下图检查一下是否有缺少,注意Mapper可写也可以不写,因为我们不需要写SQL语句,不需要调用Mapper,全部完毕后就可以运行项目,利用apipost进行测试,或者前端创建完后直接集成测试

(6)前端工程

我们采用uniapp来写前端代码,按照以下步骤建立项目

输入项目名称,选择vue2

我们需要用到以下uniapp的组件,可以从uniapp组件库下载,链接为:组件使用的入门教程 | uni-app官网

对应的矢量图标可以在阿里巴巴矢量图库下载,链接为iconfont-阿里巴巴矢量图标库

然后我们在根目录下建立store文件夹,在此文件夹下建立index.js,用于存放一些函数

index.js
export function getUserid() {
	let userid = uni.getStorageSync("userid");
	if (userid == null || userid === '')
		return '';
	else
		return userid;
}

export function getNoteid()
{
	let noteid = uni.getStorageSync("noteid");
	if (noteid == null || noteid === '')
		return '';
	else
		return noteid;
}

export function getUsername()
{
	let username = uni.getStorageSync("username");
	if (username == null || username === '')
		return '';
	else
		return username;
}


export function hasLogin() {
	let userid = uni.getStorageSync("userid");
	if (userid == null || userid === '') {
		return false;
	} else
		return true;
}

export function logout() {
	uni.removeStorageSync("userid");
	uni.removeStorageSync("noteid");
	uni.removeStorageSync("username");
	uni.redirectTo({
		url: "/pages/mine"
	});
}

随后修改pages.json中修改相关页面配置文件,可以先创建相关页面后再进行此步骤,以免编译时报错

pages.json
{
	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
		{
			"path": "pages/Main",
			"style": {
				"navigationBarTitleText": "个人备忘录"
			}
		}
	    ,{
            "path" : "pages/Login",
            "style" :                                                                                    
            {
                "navigationBarTitleText": "登录",
                "enablePullDownRefresh": false
            }
            
        }
        ,{
            "path" : "pages/Main",
            "style" :                                                                                    
            {
                "navigationBarTitleText": "所有备忘录",
                "enablePullDownRefresh": false
            }
            
        },
				{
				    "path" : "pages/mine",
				    "style" :                                                                                    
				    {
				        "navigationBarTitleText": "个人中心",
				        "enablePullDownRefresh": false
				    }
				    
				}
        ,{
            "path" : "pages/addNote",
            "style" :                                                                                    
            {
                "navigationBarTitleText": "添加/修改备忘录",
                "enablePullDownRefresh": false
            }
            
        }
        ,{
            "path" : "pages/catNote",
            "style" :                                                                                    
            {
                "navigationBarTitleText": "查询备忘录",
                "enablePullDownRefresh": false
            }
            
        }
        ,{
            "path" : "pages/Register",
            "style" :                                                                                    
            {
                "navigationBarTitleText": "注册",
                "enablePullDownRefresh": false
            }
            
        }
    ],
	"globalStyle": {
		"navigationBarTextStyle": "black",
		"navigationBarTitleText": "个人备忘录",
		"navigationBarBackgroundColor": "#F8F8F8",
		"backgroundColor": "#F8F8F8"
	},
	"tabBar": {
		"list": [
			{
				"text": "首页",
				"pagePath": "pages/Main",
				"iconPath": "static/images/首页.png",
				"selectedIconPath": "static/images/首页2.png"
			},
			{
				"text": "记录",
				"pagePath": "pages/addNote",
				"iconPath": "static/images/记录.png",
				"selectedIconPath": "static/images/记录1.png"
			},
			{
				"text": "分类",
				"pagePath": "pages/catNote",
				"iconPath": "static/images/分类.png",
				"selectedIconPath": "static/images/分类2.png"
			},
			{
				"text": "我的",
				"pagePath": "pages/mine",
				"iconPath": "static/images/我的.png",
				"selectedIconPath": "static/images/我的2.png"
			}
		]
	},
	"uniIdRouter": {}
}

 然后再pages中建立以下页面文件,我这里没有创建同名目录

addNote.vue
<template>
  <view>
			<uni-card title="添加/修改笔记" extra="必填">
		<uni-section>
					<view class="example">
						<uni-forms>
							<uni-forms-item label="截止时间">
									<uni-datetime-picker type="datetime" return-type="timestamp" v-model="time"/>
							</uni-forms-item>
							
							<uni-forms-item label="分类">
									<select v-model="selectedType" class="select">
									  <option v-for="(value, key) in types" :key="key" :value="key">{{ value }}</option>
									</select>
							</uni-forms-item>
							
							<uni-forms-item label="内容">
								<uni-section type="line" padding>
									<uni-easyinput type="textarea" autoHeight v-model="context" placeholder="请输入内容"></uni-easyinput>
								</uni-section>
							</uni-forms-item>
							
							<uni-forms-item label="完成?">
								<zero-switch  v-model="finish"></zero-switch>
							</uni-forms-item>
							
						</uni-forms>
						<button type="primary" @click="saveNote()">提交</button>
					</view>
		</uni-section>
		</uni-card>

			<uni-card title="上传文件" extra="可选">
				 <view>
				   <div class="button-container">
				     <button type="primary" @tap="chooseFile">选择文件</button>
				     <button type="primary" @click="cancelChooseFile">取消选择</button>
				   </div><br>
						<image class="myimg" v-if="this.filePath != null && this.filePath != '0'" :src="this.filePath"></image>
				    <button  type="primary" @tap="uploadFile">上传文件</button>
				  </view>
      </uni-card>
			

  </view>
</template>

<script>
	import {getUserid,getNoteid} from "@/store/index.js"
	export default {
		data(){
				return{
					noteId: '',
					userId: '',
					time: '',
					selectedType: '',
					types: {},
					context: '',
					finish: false,
					note:{},
					
					filePath: null,
					fileUrl: null,
					

    };
  },
  onLoad() {
    this.noteId = getNoteid()//获取缓存中的noteid
    this.userId = getUserid()
		
    uni.request({
      url:`http://localhost:2023/type/getMapping`,
      method:"GET",
      su***ess:(res)=>{
        // 将返回的备忘录类型数据赋值给types数组
        this.types = res.data.data
    }  
    });
		
		if(this.noteId != null && this.noteId != '')
		{//noteId不为空查询对应的note
    uni.request({
      url: `http://localhost:2023/note/selectByNote/${this.noteId}`,
			method:"POST",
      su***ess: (res) => {
				this.note = res.data.data
				//console.log(this.note);
        this.time = this.note.time
        this.selectedType = this.note.type
        this.context = this.note.detail
        this.finish = this.note.finish !== 0;
				//uni.removeStorageSync("noteid");
				this.filePath = this.note.photo;
				this.fileUrl = this.note.photo
				
      }
    })
		}
  },

  methods: {
		
		cancelChooseFile() {
				this.filePath = null;
				this.fileUrl = null;
		    console.log('用户取消选择文件');
		  },
		chooseFile() {
		      uni.chooseImage({
		        count: 1,
		        su***ess: (res) => {
		          // 选择文件成功
		          this.filePath = res.tempFilePaths[0];
		        },
		        fail: (error) => {
		          // 选择文件失败
		          console.error(error);
		        },
		      });
		    },
		uploadFile() {
		      uni.uploadFile({
		        url: 'http://localhost:2023/upload',
		        filePath: this.filePath,
		        name: 'img',
						method:"POST",
		       su***ess: function (res) {
		          // 上传成功
		        console.log(res);
						let responseData = JSON.parse(res.data); // 解析字符串为JSON对象
						console.log(responseData);
						 if(responseData.code === 200)
						 {
							 uni.showToast({
							 	title: "上传成功",
							 	icon: "su***ess",
							 	duration: 2000
							 });
							 	 this.fileUrl = responseData.data;
								 console.log( "上传后:"+ this.fileUrl);
								 uni.setStorageSync("fileurl", this.fileUrl);
						 }
						 else
						 {
							 uni.showToast({
							 	title: "上传失败",
							 	icon: "error",
							 	duration: 2000
							 });
						 }
		
		        }
		      })
					console.log(this.filePath);
		    },
		
		
		
    saveNote() {
			if(this.fileUrl === null || this.fileUrl === '0')
			{
			  this.fileUrl = uni.getStorageSync("fileurl");				
			}
			//this.noteId = uni.getStorageSync('noteid')
			if(this.noteId === null || this.noteId === '')
			{//添加note	
      // 构造备忘录的请求参数
			console.log("添加:"+this.fileUrl);
      const note = {
        //id: this.noteId,
        rid: this.userId,
				detail: this.context,
        time: this.time,
        type: this.selectedType,
        finish: this.finish ? 1 : 0,
				photo: this.fileUrl ? this.fileUrl : 0
      }
      uni.request({
        url: 'http://localhost:2023/note/save',
        method: 'POST',
        data: note,
        su***ess: (res) => {
          // 备忘录添加成功的处理逻辑
					if(res.data.code === 200)
					{
						uni.showToast({
							title: "添加成功",
							icon: "su***ess",
							duration: 2000
						});
					}
					else
					{
						uni.showToast({
							title: "添加失败",
							icon: "error",
							duration: 2000
						});
					}

        }
      })
    }
		else
		{//更新note,更新完后要在缓存中移除noteid
		console.log("更新:" +this.fileUrl);
			const notes = {
			  id: this.noteId,
			  rid: this.userId,
				detail: this.context,
			  time: this.time,
			  type: this.selectedType,
			  finish: this.finish ? 1 : 0,
				photo: this.fileUrl ? this.fileUrl : 0//因为mybatisplus不能更新为null
			}
			uni.request({
			  url: 'http://localhost:2023/note/update',
			  method: 'POST',
			  data: notes,
			  su***ess: (res) => {
			    // 备忘录更新成功的处理逻辑
					if(res.data.code === 200)
					{
						uni.showToast({
							title: "修改成功",
							icon: "su***ess",
							duration: 2000
						});
					}
					else
					{
						uni.showToast({
							title: "修改失败",
							icon: "error",
							duration: 2000
						});
					}

					uni.removeStorageSync("noteid");
					this.userId = null;
					
			  }
			})
			
		}
		uni.removeStorageSync("fileurl");
		uni.reLaunch({
			url:`/pages/Main`
		});
		},
		

		
  }
}
</script>


<style lang="scss">

	.example {
		padding: 15px;
		background-color: #fff;
	}

	.segmented-control {
		margin-bottom: 15px;
	}

	.button-group {
		margin-top: 15px;
		display: flex;
		justify-content: space-around;
	}

	.form-item {
		display: flex;
		align-items: center;
	}

	.button {
		display: flex;
		align-items: center;
		height: 35px;
		margin-left: 10px;
	}
	.select{
		//display: flex;
		//align-items: center;
		height: 65rpx;
		width: 250rpx;
	  background-color: #fff;
		 border-radius: 10rpx;
			
		
		//margin-left: 10px;
	}
	
	.button-container {
	  display: flex; /* 使用flex布局 */
	  justify-content: center; /* 水平居中对齐按钮 */
	}
	.myimg{
		width: 600rpx;
		height: 400rpx;
	}
</style>
 catNote.vue
<template>
	<view>
		<uni-card title="分类查询">
			<uni-forms-item label="分类">
					<select v-model="selectedType" class="select">
					  <option v-for="(value, key) in types" :key="key" :value="key" class="">{{ value }}</option>
					</select>
			</uni-forms-item>
			<uni-forms-item label="完成?">
				<zero-switch  v-model="finish"></zero-switch>
			</uni-forms-item>
			<button type="primary" @click="findNotes()">查询</button>
		</uni-card>
		
		<uni-swipe-action>
		<view v-for="note in notes" :key="note.id" >
		 <uni-swipe-action-item :right-options="options" :auto-close="false" @click="bindClick(note.id)">
			<uni-card title="" extra="">
				<view class="" @click="updateNote(note.id)">
				<p>截止日期:{{note.time}}</p><br>
				<p>内&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;容:{{note.detail}}</p><br>
				<p>任务类型:{{types[""+note.type]}}</p><br>
				<p><image class="myimg" v-if="note.photo != null && note.photo != '0'" :src='note.photo'></image></p><br>
				<p :class="{'finished': note.finish === 1, 'unfinished': note.finish !== 1}">
				是否完成:{{ note.finish === 1 ? "完成" : "未完成" }}
				</p>
				</view>
				</uni-card>
				</uni-swipe-action-item>
		</view>
		</uni-swipe-action>
		
	</view>
</template>

<script>
	import {getUserid} from "@/store/index.js"
	export default {
		data() {
			return {
				userId: '',
				selectedType: '',
				finish: false,
				
				types: {},
				notes: {},
				currentPage: 0,
				total: 0,
				pageSize: 0,
				options:[
				        {
				            text: '删除',
				            style: {
				                backgroundColor: '#dd524d'
				            }
				        }
				      ],
			};
		},
		onLoad() {
			this.userId = getUserid();
			uni.request({
			  url:`http://localhost:2023/type/getMapping`,
			  method:"GET",
			  su***ess:(res)=>{
			    // 将返回的备忘录类型数据赋值给types数组
			    this.types = res.data.data
			}  
			});
		},
		methods:{
			findNotes()
			{
				const isfinish = this.finish ? 1 : 0
				uni.request({					
					url:`http://localhost:2023/note/selectByType/${this.userId}/${this.selectedType}/${isfinish}?currentPage=${this.currentPage}`,
					method:"POST",
				
					su***ess:(res)=>{
					   this.notes = res.data.data.records 
					   this.currentPage = res.data.data.current
					   this.total = res.data.data.total
					   this.pageSize = res.data.data.size
						
					}
				});
			},
			onReachBottom(){
				this.pageSize += 1;
				this.findNotes();
			},
			bindClick(noteid) {
							uni.showModal({
								title: "是否确定删除",
								su***ess: res => {
									if (res.confirm) {
										//删除
										uni.request({
											url:`http://localhost:2023/note/delete/${noteid}`,
											method:"DELETE",
										
											su***ess:(res)=>{
											  //删除成功
												uni.showToast({
													title: "删除成功",
													icon: "su***ess",
													duration: 2000
												});
												uni.reLaunch({
													url:`/pages/catNote`
												});
											}
										});
									}
								}
							});
			},
			updateNote(id){
				uni.setStorageSync("noteid",id);
				uni.reLaunch({
					url:`/pages/addNote`
				});
			},
		}
	}
</script>

<style lang="scss">
	.select{
		//display: flex;
		//align-items: center;
		height: 65rpx;
		width: 250rpx;
	  background-color: #fff;
		border-radius: 10rpx;
			
		
		//margin-left: 10px;
	}
	.select2{
		//display: flex;
		//align-items: center;
		height: 55rpx;
		width: 50rpx;
	  background-color: #fff;
		border-radius: 10rpx;
			
		
		//margin-left: 10px;
	}
	.finished {
	  color: green; /* 已完成的备忘录显示为绿色 */
	}
	
	.unfinished {
	  color: red; /* 未完成的备忘录显示为红色 */
	}
	.myimg{
		width: 600rpx;
		height: 400rpx;
	}
</style>

 Login.vue
<template>
	<uni-card title="个人备忘录" extra="">
	<view class="loginform">
		<uni-forms ref="form" :model="user" >
			<uni-forms-item label="账号" name="username" required>
				<uni-easyinput type="text" v-model="user.username" placeholder="请输入账号..." />
			</uni-forms-item>
			<uni-forms-item label="密码" name="password" required>
				<uni-easyinput type="password" v-model="user.password" placeholder="请输入密码..." />
			</uni-forms-item>
		</uni-forms>
		<button @tap="submitForm" class="submit">登录</button>
		<view class="noa***ount">
			<view @tap="gotoRegister">没有账号,<text>注册</text>一个</view>
		</view>
	</view>
	</uni-card>
</template>

<script>
	import {getUserid} from "@/store/index.js"
	export default {
		data() {
			return {
				user: {
				  id:"",
				  username: "",
				  password: "",
				  avatar:""
				},
			};
		},
		methods:{
			submitForm() {
				this.$refs.form.validate().then((res) => {
					uni.request({
						url: `http://localhost:2023/user/login`,
						method: "POST",
						data: {
							username: this.user.username,
							password: this.user.password
						},
						su***ess: (result) => {
							if (result.data.code === 200) {
								uni.showToast({
									title: "登录成功",
									icon: "su***ess",
									duration: 2000
								});
								setTimeout(() => {
									this.user = result.data.data,
									uni.setStorageSync("userid", result.data.data.id);
									uni.setStorageSync("username", result.data.data.username);
									uni.reLaunch({
										url: "/pages/Main"
									})
								}, 2000);
			
							} else {
								uni.showToast({
									title: result.data.msg,
									icon: "error"
								});
							}
						}
					});
				});
			},
			gotoRegister()
			{
				uni.reLaunch({
					url: "/pages/Register"
				})
			}
		}
	}
</script>

<style lang="scss" scoped>
	.loginform {
		margin: 6rpx;

		.submit {
			background-color: $uni-color-primary;
			color: white;
		}

		.noa***ount {
			font-size: 30rpx;
			display: flex;
			justify-content: space-between;
			margin: 10rpx;

			text {

				color: red;
			}
		}
	}
</style>
 Main.vue
<template>
	<view>
					<uni-notice-bar show-icon scrollable
						:text="this.msg"/>

			<uni-card title="最近一条待办的截止时间" extra="">
				<uni-section type="line" padding>
						<uni-countdown :font-size="30" :day="this.days" :hour="this.hours" :minute="this.minutes" :second="this.seconds" color="#FFFFFF" background-color="#007AFF" />
				</uni-section>	
			</uni-card >

	   <uni-swipe-action>
	   	<!-- 基础用法 -->
			<view v-for="note in notes" :key="note.id" >	
	   	<uni-swipe-action-item :right-options="options" :auto-close="false" @click="bindClick(note.id)">
			<uni-card title="" extra="">
				<view class="" @click="updateNote(note.id)">
				<p>截止日期:{{note.time}}</p><br>
				<p>内&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;容:{{note.detail}}</p><br>
				<p>任务类型:{{typeMap[""+note.type]}}</p><br>
				<p><image class="myimg" v-if="note.photo != null && note.photo != '0'" :src='note.photo'></image></p><br>
				<p :class="{'finished': note.finish === 1, 'unfinished': note.finish !== 1}">
				是否完成:{{ note.finish === 1 ? "完成" : "未完成" }}
				</p>
				</view>
				</uni-card>
	   	</uni-swipe-action-item>
			</view>
	   </uni-swipe-action>
		
	</view>
</template>

<script>
	import {getUserid,getUsername} from "@/store/index.js"
	export default {
		data() {
			return {
				imgP:"https://img2.baidu.***/it/u=3841326637,2519425910&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=501",
				options:[
				        {
				            text: '删除',
				            style: {
				                backgroundColor: '#dd524d'
				            }
				        }
				      ],

				userid:0,
				user:{},
				notes: [],
				currentPage: 0,
				total: 0,
				pageSize: 0,
				typeMap:{},
				finish:0,
				
				msg:'',
				days:0,
				hours:0,
				minutes:0,
				seconds:0,
				
			};
		},
		methods:{
			
			showNotes(){
				uni.request({
					url:`http://localhost:2023/note/selectAllByUserID/${this.userid}?currentPage=${this.currentPage}`,
					method:"POST",

					su***ess:(res)=>{
					   this.notes = res.data.data.records 
					   this.currentPage = res.data.data.current
					   this.total = res.data.data.total
					   this.pageSize = res.data.data.size
						
					}
				});
				
				uni.request({
					url:`http://localhost:2023/type/getMapping`,
					method:"GET",
				
					su***ess:(res)=>{
					   this.typeMap = res.data.data;
						
					}
				});
				
			},
			updateNote(id){
				uni.setStorageSync("noteid",id);
				uni.reLaunch({
					url:`/pages/addNote`
				});
			},
			bindClick(noteid) {
							uni.showModal({
								title: "是否确定删除",
								su***ess: res => {
									if (res.confirm) {
										//删除
										uni.request({
											url:`http://localhost:2023/note/delete/${noteid}`,
											method:"DELETE",
										
											su***ess:(res)=>{
											  //删除成功
												if(res.data.code === 200)
												{
													uni.showToast({
														title: "删除成功",
														icon: "su***ess",
														duration: 2000
													});
													uni.reLaunch({
														url:`/pages/Main`
													});
												}
												else
												{
													uni.showToast({
														title: "删除失败",
														icon: "error",
														duration: 2000
													});
												}

											}
										});
									}
								}
							});
			},
			 SetCountdown() {
							uni.request({
								url:`http://localhost:2023/note/selectTime/${this.userid}`,
								method:"GET",
							
								su***ess:(res)=>{
								   const { days, hours, minutes, seconds } = res.data.data
								      this.days = parseInt(days)
								      this.hours = parseInt(hours)
								      this.minutes = parseInt(minutes)
								      this.seconds = parseInt(seconds)
									
								}
			     });
					},
			
		},
		onLoad(){
			this.userid = getUserid();
			this.showNotes();
			this.msg = '欢迎' + getUsername() + '回来,您有以下几条待办事件';
			this.SetCountdown();
		},
		onReachBottom(){
			this.pageSize += 1;
			this.showNotes();
		},

	}
</script>


<style lang="scss">
	
		.a0{
			//align-items: center;
			padding-left: 40rpx;
			
		}
		.a1{
		   border: 1px solid #***c;
			 border-radius: 5rpx;
			 width: 650rpx;
			//text-align: center;
			  //display: flex;
			 // justify-content: center;
			  // align-items: center;
				padding-left: 10rpx;
				padding-top: 20rpx;
				

		}
		.content-box {
				flex: 1;
				/* #ifdef APP-NVUE */
				justify-content: center;
				/* #endif */
				height: 44px;
				line-height: 44px;
				padding: 0 15px;
				position: relative;
				background-color: #fff;
				border-bottom-color: #f5f5f5;
				border-bottom-width: 1px;
				border-bottom-style: solid;
			}
		
			.content-text {
				font-size: 15px;
			}
		
			.example-body {
				/* #ifndef APP-NVUE */
				display: flex;
				/* #endif */
				flex-direction: row;
				justify-content: center;
				padding: 10px 0;
				background-color: #fff;
			}
		
			.button {
				border-color: #e5e5e5;
				border-style: solid;
				border-width: 1px;
				padding: 4px 8px;
				border-radius: 4px;
			}
		
			.button-text {
				font-size: 15px;
			}
		
			.slot-button {
				/* #ifndef APP-NVUE */
				display: flex;
				height: 100%;
				/* #endif */
				flex: 1;
				flex-direction: row;
				justify-content: center;
				align-items: center;
				padding: 0 20px;
				background-color: #ff5a5f;
			}
		
			.slot-button-text {
				color: #ffffff;
				font-size: 14px;
			}
			.finished {
			  color: green; /* 已完成的备忘录显示为绿色 */
			}
			
			.unfinished {
			  color: red; /* 未完成的备忘录显示为红色 */
			}
				.container {
					/* #ifndef APP-NVUE */
					position: absolute;
					left: 0;
					right: 0;
					top: 0;
					bottom: 0;
					/* #endif */
				}
				.myimg{
					width: 600rpx;
					height: 400rpx;
				}
</style>



 mine.vue
<template>
	<view class="mine">
		<!--顶部信息:头像,昵称,发文收藏和点赞数据-->
		<view class="top">
			<view class="userinfo">
				<view class="group">
					<view class="pic">
						<image v-if="user.avatar != null" :src="user.avatar"></image>
						<image v-else src="/static/tabs/member.png" mode="aspectFit"></image>
					</view>
					<view>
						<text v-if="user.username != null" v-text="user.username"></text>
						<text v-else class="login" @tap="gotoLogin" v-text="'点击登录'"></text>
					</view>

				</view>
				<view class="more">
					<text class="iconfont icon-a-10-you"></text>
				</view>
			</view>
		</view>
		<!-- 登录后的补充组件-->
		<view v-if="user.username !=null">

			<!--退出登录-->
			<view class="itemgroup">
				<view class="item">
					<view class="left" @tap="gotoLogout">
						<text class="iconfont icon-a-73-tuichu"></text>
						<text>退出登录</text>
					</view>
					<view>
						<text class="iconfont icon-a-10-you"></text>
					</view>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	import {
		hasLogin,
		logout,
		getUserid
	} from '@/store/index.js'

	export default {
		data() {
			return {
				user: {
					username: null, //用户账号
					avatar: null, //个人照片
					
				}
			};
		},

		onLoad() {
			if (hasLogin()) {
				this.getUserInfo();
			}
		},

		methods: {
			gotoLogin() {
				//跳转到的登录页面
				uni.redirectTo({
					url:"/pages/Login"
				});
			},

			getUserInfo() {
				this.userid = getUserid();
				if (this.userid != null) {
					uni.request({

						url: `http://localhost:2023/user/selectUserById/${this.userid}`,
						method: "GET",
						su***ess: (res) => {
							this.user = res.data.data;
						}
					});
				}
			},


			gotoLogout() {
				uni.showModal({
					title: "是否确定退出",
					su***ess: res => {
						if (res.confirm) {
							logout();
							this.user = {
								username: null,
								avatar: null,
							}
						}
					}
				});
			}

		}

	}
</script>

<style lang="scss">
	.mine {
		margin: 30rpx 30rpx;

		.top {
			display: flex;
			flex-direction: column;
			justify-content: space-between;

			.userinfo {
				display: flex;
				flex: row;
				justify-content: space-between;
				align-items: center;

				.group {
					display: flex;
					align-items: center;

					.pic {
						width: 120rpx;
						height: 120rpx;
						border-radius: 50%;
						overflow: hidden;
						border: 2rpx solid red;
						margin-right: 10rpx;

						image {
							width: 100%;
							height: 100%;
						}
					}

					.login {
						margin-left: 16rpx;
						font-size: 40rpx;
					}
				}

				.more {
					.iconfont {
						font-size: 40rpx;
					}
				}
			}

			.bloginfo {
				display: flex;
				flex: row;
				margin-top: 20rpx;

				view {
					margin-right: 30rpx;
					font-size: 26rpx;
				}
			}
		}

		.itemgroup {
			border-top: 4rpx solid #f4f4f4;
			margin-top: 30rpx;

			.item {
				padding: 24rpx 0;
				display: flex;
				justify-content: space-between;
				align-items: center;

				.left {
					text {
						margin-right: 16rpx;
						font-size: 36rpx;
					}
				}
			}
		}
	}
</style>
Register.vue

<template>
	<uni-card>
	<view class="registerform">
		<uni-forms ref="form" :model="user" :rules="rules" :labelWidth="85">
			<uni-forms-item label="账号" name="username" required>
				<uni-easyinput type="text" v-model="user.username" placeholder="请输入账号" />
			</uni-forms-item>
			<uni-forms-item label="密码" name="password" required>
				<uni-easyinput type="password" v-model="user.password" placeholder="请输入密码" />
			</uni-forms-item>
			<uni-forms-item label="再次输入" name="passwordconfirm" required>
				<uni-easyinput type="password" v-model="user.passwordconfirm" placeholder="再次输入密码" />
			</uni-forms-item>
		</uni-forms>
		<button @tap="submitForm" class="submit">注册</button>
	</view>
	</uni-card>
</template>

<script>
	
	export default {
		data() {
			return {
				user: {},
				rules: {
					username: {
						rules: [{
								required: true,
								errorMessage: '请输入账号',
							},
							{
								minLength: 2,
								maxLength: 20,
								errorMessage: '账号长度在 {minLength}到 {maxLength}个字符 ',
							}
						]
					},
					password: {
						rules: [{
								required: true,
								errorMessage: '请输入密码',
							},
							{
								minLength: 2,
								maxLength: 20,
								errorMessage: '密码长度在 {minLength}到 {maxLength}个字符 ',
							}

						]
					},
					passwordconfirm: {
						rules: [{
								required: true,
								errorMessage: '请输入密码',
							},
							{
								minLength: 2,
								maxLength: 20,
								errorMessage: '密码长度在 {minLength}到 {maxLength}个字符 ',
							},
							{
								validateFunction: function(rule, value, data, callback) {
									if (value !== data.password) {
										callback("两次输入的密码不一致");
										}
										return true;
									}
								}
							]
						},

					}

				};
			},
			methods: {
				submitForm() {
					this.$refs.form.validate().then((res) => {

						Reflect.deleteProperty(this.user, "passwordconfirm");
						// console.log('user 模型',this.user);

						uni.request({
							url: "http://localhost:2023/user/register",
							method: "POST",
							data: {
								username: this.user.username,
								password: this.user.password,
							},
							su***ess: (result) => {
								if (result.data.msg) {
									uni.showToast({
										title: "用户注册成功",
										duration: 2000
									});

									//注册成功则转到登录页面
									setTimeout(() => {
										uni.redirectTo({
											url: "/pages/Login"
										});
										this.user = {};
									}, 2000);
								} else {
									uni.showToast({
										title: result.data.describe,
										duration: 2000,
										icon: "error"
									})
								}

							} //end of su***ess

						});
					}).catch(err => {
						// console.log(err);
					})
				},
			}

		}
</script>

<style lang="scss" scoped>
	.registerform {
		margin: 6rpx;

		.submit {
			background-color: $uni-color-primary;
			color: white;
		}
	}
</style>

到这里前后端代码已经全部完成,下面是运行的截图

到此全部结束啦,如果此文章对你有帮助,希望给一个关注+点赞+收藏

若需要源码可以私信我,免费分享

转载请说明出处内容投诉
CSS教程_站长资源网 » uniapp+springboot 实现前后端分离的个人备忘录系统【超详细】

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买