目录
接下来我们使用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 COMMENT 'note表的id',
`rid` int(0) NOT NULL COMMENT '这个笔记是哪个人的',
`detail` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '笔记的内容',
`photo` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户上传的图片,null或者0表示无',
`time` datetime(0) NOT NULL COMMENT '笔记的创建时间',
`type` int(0) NOT NULL COMMENT '笔记的类型',
`finish` int(0) NOT NULL COMMENT '任务是否完成',
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.com/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.com/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 COMMENT '主键',
`type` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '是什么类型',
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 COMMENT '用户的id',
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '用户名字',
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '用户密码',
`avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '用户的头像',
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.com/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.com/v2-fc348d5e926116782149d2151dc09834.jpg');
INSERT INTO `user` VALUES (15, 'mynote', '123456', 'https://img2.baidu.com/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.com/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>com.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>com.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>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<!--七牛云OOS-->
<dependency>
<groupId>com.qiniu</groupId>
<artifactId>qiniu-java-sdk</artifactId>
<version>[7.13.0, 7.13.99]</version>
</dependency>
<dependency>
<groupId>com.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-compiler-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: com.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:
accessKey: yourAK
secretKey: yourSK
bucket: yourbucket
接下来在项目下创建如下几个包:
common包:
R.java
在common文件下创建 R.java 类,主要用于封装返回给前端的数据:
package com.example.memohehang.common;
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 success(Object data)
{
return success(200, "操作成功", data);
}
public static R success(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 com.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 com.example.memohehang.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.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 com.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 com.example.memohehang.domain;
import lombok.AllArgsConstructor;
import com.baomidou.mybatisplus.annotation.IdType;
import com.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 com.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 com.example.memohehang.controller;
import com.example.memohehang.common.R;
import com.example.memohehang.domain.Notes;
import com.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 com.example.memohehang.controller;
import com.example.memohehang.common.R;
import com.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 com.example.memohehang.controller;
import com.example.memohehang.common.R;
import com.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 com.example.memohehang.controller;
import com.example.memohehang.common.R;
import com.example.memohehang.domain.User;
import com.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 com.example.memohehang.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.memohehang.common.R;
import com.example.memohehang.domain.Notes;
import com.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 com.example.memohehang.service;
import com.example.memohehang.common.R;
import org.springframework.web.multipart.MultipartFile;
public interface OssUploadService {
//图片上传到七牛云
R uploadImg(MultipartFile img);
}
TypeService
package com.example.memohehang.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.memohehang.common.R;
import com.example.memohehang.domain.Type;
public interface TypeService extends IService<Type>
{
/**
* 获取所有类型
* @return
*/
public R typeMapping();
}
UserService
package com.example.memohehang.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.example.memohehang.common.R;
import com.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 com.example.memohehang.service.Impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.memohehang.common.R;
import com.example.memohehang.domain.Notes;
import com.example.memohehang.mapper.NotesMapper;
import com.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.success(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.success(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.success(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.success(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.success(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.success(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.success(200, "删除成功", null);
}
return R.error(405, "删除失败");
}
}
OssUploadServiceImpl.java
注意这里需要修改外链回显链接
package com.example.memohehang.service.Impl;
import com.example.memohehang.common.R;
import com.example.memohehang.service.OssUploadService;
import com.example.memohehang.utils.PathUtils;
import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.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.success(200,"操作成功",url);
}
//----------------------------------上传文件到七牛云----------------------------------------
//注意要从application.yml读取属性数据,下面的3个成员变量的名字必须对应application.yml的myoss属性的三个子属性名字
private String accessKey;
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 accessKey = "_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(accessKey, 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.com/"+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 com.example.memohehang.service.Impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.memohehang.common.R;
import com.example.memohehang.domain.Type;
import com.example.memohehang.mapper.TypeMapper;
import com.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.success(hashMap);
}
}
UserServiceImpl.java
package com.example.memohehang.service.Impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.memohehang.common.R;
import com.example.memohehang.domain.User;
import com.example.memohehang.mapper.UserMapper;
import com.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.success(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.com/it/u=3841326637,2519425910&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=501");
// 反之加入
save(user);
return R.success(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.success(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",
success:(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",
success: (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,
success: (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",
success: function (res) {
// 上传成功
console.log(res);
let responseData = JSON.parse(res.data); // 解析字符串为JSON对象
console.log(responseData);
if(responseData.code === 200)
{
uni.showToast({
title: "上传成功",
icon: "success",
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,
success: (res) => {
// 备忘录添加成功的处理逻辑
if(res.data.code === 200)
{
uni.showToast({
title: "添加成功",
icon: "success",
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,
success: (res) => {
// 备忘录更新成功的处理逻辑
if(res.data.code === 200)
{
uni.showToast({
title: "修改成功",
icon: "success",
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>内 容:{{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",
success:(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",
success:(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: "是否确定删除",
success: res => {
if (res.confirm) {
//删除
uni.request({
url:`http://localhost:2023/note/delete/${noteid}`,
method:"DELETE",
success:(res)=>{
//删除成功
uni.showToast({
title: "删除成功",
icon: "success",
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="noaccount">
<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
},
success: (result) => {
if (result.data.code === 200) {
uni.showToast({
title: "登录成功",
icon: "success",
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;
}
.noaccount {
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>内 容:{{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.com/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",
success:(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",
success:(res)=>{
this.typeMap = res.data.data;
}
});
},
updateNote(id){
uni.setStorageSync("noteid",id);
uni.reLaunch({
url:`/pages/addNote`
});
},
bindClick(noteid) {
uni.showModal({
title: "是否确定删除",
success: res => {
if (res.confirm) {
//删除
uni.request({
url:`http://localhost:2023/note/delete/${noteid}`,
method:"DELETE",
success:(res)=>{
//删除成功
if(res.data.code === 200)
{
uni.showToast({
title: "删除成功",
icon: "success",
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",
success:(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 #ccc;
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",
success: (res) => {
this.user = res.data.data;
}
});
}
},
gotoLogout() {
uni.showModal({
title: "是否确定退出",
success: 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,
},
success: (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 success
});
}).catch(err => {
// console.log(err);
})
},
}
}
</script>
<style lang="scss" scoped>
.registerform {
margin: 6rpx;
.submit {
background-color: $uni-color-primary;
color: white;
}
}
</style>
到这里前后端代码已经全部完成,下面是运行的截图
到此全部结束啦,如果此文章对你有帮助,希望给一个关注+点赞+收藏
若需要源码可以私信我,免费分享